~ubuntu-branches/ubuntu/vivid/apgdiff/vivid

« back to all changes in this revision

Viewing changes to src/main/java/cz/startnet/utils/pgdiff/loader/PgDumpLoader.java

  • Committer: Bazaar Package Importer
  • Author(s): Christoph Berg
  • Date: 2010-10-02 19:35:17 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20101002193517-9wesve8sksxcktkc
Tags: 2.2-1
* New upstream version.
* Update homepage location.
* Finally enable test suite, yay!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * Copyright 2006 StartNet s.r.o.
 
3
 *
 
4
 * Distributed under MIT license
 
5
 */
1
6
package cz.startnet.utils.pgdiff.loader;
2
7
 
3
8
import cz.startnet.utils.pgdiff.parsers.AlterTableParser;
 
9
import cz.startnet.utils.pgdiff.parsers.AlterViewParser;
4
10
import cz.startnet.utils.pgdiff.parsers.CreateFunctionParser;
5
11
import cz.startnet.utils.pgdiff.parsers.CreateIndexParser;
6
12
import cz.startnet.utils.pgdiff.parsers.CreateSchemaParser;
29
35
public class PgDumpLoader { //NOPMD
30
36
 
31
37
    /**
32
 
     * Pattern for testing whether command is CREATE SCHEMA command.
 
38
     * Pattern for testing whether it is CREATE SCHEMA statement.
33
39
     */
34
40
    private static final Pattern PATTERN_CREATE_SCHEMA = Pattern.compile(
35
41
            "^CREATE[\\s]+SCHEMA[\\s]+.*$", Pattern.CASE_INSENSITIVE);
41
47
            "^SET[\\s]+search_path[\\s]*=[\\s]*([^,\\s]+)(?:,[\\s]+.*)?;$",
42
48
            Pattern.CASE_INSENSITIVE);
43
49
    /**
44
 
     * Pattern for testing whether command is CREATE TABLE command.
 
50
     * Pattern for testing whether it is CREATE TABLE statement.
45
51
     */
46
52
    private static final Pattern PATTERN_CREATE_TABLE = Pattern.compile(
47
53
            "^CREATE[\\s]+TABLE[\\s]+.*$", Pattern.CASE_INSENSITIVE);
48
54
    /**
49
 
     * Pattern for testing whether command is CREATE VIEW command.
 
55
     * Pattern for testing whether it is CREATE VIEW statement.
50
56
     */
51
57
    private static final Pattern PATTERN_CREATE_VIEW = Pattern.compile(
52
58
            "^CREATE[\\s]+(?:OR[\\s]+REPLACE[\\s]+)?VIEW[\\s]+.*$",
53
59
            Pattern.CASE_INSENSITIVE);
54
60
    /**
55
 
     * Pattern for testing whether command is ALTER TABLE command.
 
61
     * Pattern for testing whether it is ALTER TABLE statement.
56
62
     */
57
63
    private static final Pattern PATTERN_ALTER_TABLE =
58
 
            Pattern.compile("^ALTER[\\s]+TABLE[\\s]+.*$", Pattern.CASE_INSENSITIVE);
 
64
            Pattern.compile("^ALTER[\\s]+TABLE[\\s]+.*$",
 
65
            Pattern.CASE_INSENSITIVE);
59
66
    /**
60
 
     * Pattern for testing whether command is CREATE SEQUENCE command.
 
67
     * Pattern for testing whether it is CREATE SEQUENCE statement.
61
68
     */
62
69
    private static final Pattern PATTERN_CREATE_SEQUENCE = Pattern.compile(
63
70
            "^CREATE[\\s]+SEQUENCE[\\s]+.*$", Pattern.CASE_INSENSITIVE);
64
71
    /**
65
 
     * Pattern for testing whether command is CREATE INDEX command.
 
72
     * Pattern for testing whether it is CREATE INDEX statement.
66
73
     */
67
74
    private static final Pattern PATTERN_CREATE_INDEX = Pattern.compile(
68
75
            "^CREATE[\\s]+(?:UNIQUE[\\s]+)?INDEX[\\s]+.*$",
69
76
            Pattern.CASE_INSENSITIVE);
70
77
    /**
71
 
     * Pattern for testing whether command is SET command.
72
 
     */
73
 
    private static final Pattern PATTERN_SET =
74
 
            Pattern.compile("^SET[\\s]+.*$", Pattern.CASE_INSENSITIVE);
75
 
    /**
76
 
     * Pattern for testing whether command is COMMENT command.
77
 
     */
78
 
    private static final Pattern PATTERN_COMMENT =
79
 
            Pattern.compile("^COMMENT[\\s]+.*$", Pattern.CASE_INSENSITIVE);
80
 
    /**
81
 
     * Pattern for testing whether command is SELECT command.
 
78
     * Pattern for testing whether it is SELECT statement.
82
79
     */
83
80
    private static final Pattern PATTERN_SELECT =
84
81
            Pattern.compile("^SELECT[\\s]+.*$", Pattern.CASE_INSENSITIVE);
85
82
    /**
86
 
     * Pattern for testing whether command is INSERT INTO command.
 
83
     * Pattern for testing whether it is INSERT INTO statement.
87
84
     */
88
85
    private static final Pattern PATTERN_INSERT_INTO =
89
 
            Pattern.compile("^INSERT[\\s]+INTO[\\s]+.*$", Pattern.CASE_INSENSITIVE);
90
 
    /**
91
 
     * Pattern for testing whether command is REVOKE command.
92
 
     */
93
 
    private static final Pattern PATTERN_REVOKE =
94
 
            Pattern.compile("^REVOKE[\\s]+.*$", Pattern.CASE_INSENSITIVE);
95
 
    /**
96
 
     * Pattern for testing whether command is GRANT command.
97
 
     */
98
 
    private static final Pattern PATTERN_GRANT =
99
 
            Pattern.compile("^GRANT[\\s]+.*$", Pattern.CASE_INSENSITIVE);
100
 
    /**
101
 
     * Pattern for testing whether command is CREATE TRIGGER command.
 
86
            Pattern.compile("^INSERT[\\s]+INTO[\\s]+.*$",
 
87
            Pattern.CASE_INSENSITIVE);
 
88
    /**
 
89
     * Pattern for testing whether it is UPDATE statement.
 
90
     */
 
91
    private static final Pattern PATTERN_UPDATE =
 
92
            Pattern.compile("^UPDATE[\\s].*$", Pattern.CASE_INSENSITIVE);
 
93
    /**
 
94
     * Pattern for testing whether it is DELETE FROM statement.
 
95
     */
 
96
    private static final Pattern PATTERN_DELETE_FROM =
 
97
            Pattern.compile("^DELETE[\\s]+FROM[\\s]+.*$",
 
98
            Pattern.CASE_INSENSITIVE);
 
99
    /**
 
100
     * Pattern for testing whether it is CREATE TRIGGER statement.
102
101
     */
103
102
    private static final Pattern PATTERN_CREATE_TRIGGER = Pattern.compile(
104
103
            "^CREATE[\\s]+TRIGGER[\\s]+.*$", Pattern.CASE_INSENSITIVE);
105
104
    /**
106
 
     * Pattern for testing whether command is CREATE FUNCTION or CREATE
107
 
     * OR REPLACE FUNCTION command.
 
105
     * Pattern for testing whether it is CREATE FUNCTION or CREATE OR REPLACE
 
106
     * FUNCTION statement.
108
107
     */
109
108
    private static final Pattern PATTERN_CREATE_FUNCTION = Pattern.compile(
110
109
            "^CREATE[\\s]+(?:OR[\\s]+REPLACE[\\s]+)?FUNCTION[\\s]+.*$",
111
110
            Pattern.CASE_INSENSITIVE);
112
111
    /**
 
112
     * Pattern for testing whether it is ALTER VIEW statement.
 
113
     */
 
114
    private static final Pattern PATTERN_ALTER_VIEW = Pattern.compile(
 
115
            "^ALTER[\\s]+VIEW[\\s]+.*$", Pattern.CASE_INSENSITIVE);
 
116
    /**
113
117
     * Pattern for getting the string that is used to end the function
114
118
     * or the function definition itself.
115
119
     */
116
120
    private static final Pattern PATTERN_END_OF_FUNCTION = Pattern.compile(
117
 
            "^(?:.*[\\s]+)?AS[\\s]+(['$][^\\s]*).*$", Pattern.CASE_INSENSITIVE);
 
121
            "^(?:.*[\\s]+)?AS[\\s]+([\\S]+).*$", Pattern.CASE_INSENSITIVE);
118
122
 
119
123
    /**
120
124
     * Creates a new instance of PgDumpLoader.
121
125
     */
122
126
    private PgDumpLoader() {
123
 
        super();
124
127
    }
125
128
 
126
129
    /**
128
131
     *
129
132
     * @param inputStream input stream that should be read
130
133
     * @param charsetName charset that should be used to read the file
131
 
     *
132
 
     * @return database schema from dump fle
133
 
     *
134
 
     * @throws UnsupportedOperationException Thrown if unsupported encoding has
135
 
     *         been encountered.
136
 
     * @throws FileException Thrown if problem occured while reading input
137
 
     *         stream.
 
134
     * @param outputIgnoredStatements whether ignored statements should be
 
135
     * included in the output
 
136
     *
 
137
     * @return database schema from dump file
138
138
     */
139
139
    public static PgDatabase loadDatabaseSchema(final InputStream inputStream,
140
 
            final String charsetName) { //NOPMD
 
140
            final String charsetName, final boolean outputIgnoredStatements) {
141
141
 
142
142
        final PgDatabase database = new PgDatabase();
143
143
        BufferedReader reader = null;
163
163
                    continue;
164
164
                } else if (PATTERN_CREATE_SCHEMA.matcher(line).matches()) {
165
165
                    CreateSchemaParser.parse(
166
 
                            database, getWholeCommand(reader, line));
 
166
                            database, getWholeStatement(reader, line));
167
167
                } else if (PATTERN_DEFAULT_SCHEMA.matcher(line).matches()) {
168
168
                    final Matcher matcher =
169
169
                            PATTERN_DEFAULT_SCHEMA.matcher(line);
171
171
                    database.setDefaultSchema(matcher.group(1));
172
172
                } else if (PATTERN_CREATE_TABLE.matcher(line).matches()) {
173
173
                    CreateTableParser.parse(
174
 
                            database, getWholeCommand(reader, line));
 
174
                            database, getWholeStatement(reader, line));
175
175
                } else if (PATTERN_ALTER_TABLE.matcher(line).matches()) {
176
176
                    AlterTableParser.parse(
177
 
                            database, getWholeCommand(reader, line));
 
177
                            database, getWholeStatement(reader, line));
178
178
                } else if (PATTERN_CREATE_SEQUENCE.matcher(line).matches()) {
179
179
                    CreateSequenceParser.parse(
180
 
                            database, getWholeCommand(reader, line));
 
180
                            database, getWholeStatement(reader, line));
181
181
                } else if (PATTERN_CREATE_INDEX.matcher(line).matches()) {
182
182
                    CreateIndexParser.parse(
183
 
                            database, getWholeCommand(reader, line));
 
183
                            database, getWholeStatement(reader, line));
184
184
                } else if (PATTERN_CREATE_VIEW.matcher(line).matches()) {
185
185
                    CreateViewParser.parse(
186
 
                            database, getWholeCommand(reader, line));
 
186
                            database, getWholeStatement(reader, line));
 
187
                } else if (PATTERN_ALTER_VIEW.matcher(line).matches()) {
 
188
                    AlterViewParser.parse(
 
189
                            database, getWholeStatement(reader, line));
187
190
                } else if (PATTERN_CREATE_TRIGGER.matcher(line).matches()) {
188
191
                    CreateTriggerParser.parse(
189
 
                            database, getWholeCommand(reader, line));
 
192
                            database, getWholeStatement(reader, line));
190
193
                } else if (PATTERN_CREATE_FUNCTION.matcher(line).matches()) {
191
194
                    CreateFunctionParser.parse(
192
195
                            database, getWholeFunction(reader, line));
193
 
                } else if (PATTERN_SET.matcher(line).matches()
194
 
                        || PATTERN_COMMENT.matcher(line).matches()
195
 
                        || PATTERN_SELECT.matcher(line).matches()
 
196
                } else if (PATTERN_SELECT.matcher(line).matches()
196
197
                        || PATTERN_INSERT_INTO.matcher(line).matches()
197
 
                        || PATTERN_REVOKE.matcher(line).matches()
198
 
                        || PATTERN_GRANT.matcher(line).matches()) {
199
 
                    getWholeCommand(reader, line);
 
198
                        || PATTERN_UPDATE.matcher(line).matches()
 
199
                        || PATTERN_DELETE_FROM.matcher(line).matches()) {
 
200
                    getWholeStatement(reader, line);
 
201
                } else if (outputIgnoredStatements) {
 
202
                    database.addIgnoredStatement(getWholeStatement(reader, line));
 
203
                } else {
 
204
                    getWholeStatement(reader, line);
200
205
                }
201
206
 
202
207
                line = reader.readLine();
213
218
     *
214
219
     * @param file name of file containing the dump
215
220
     * @param charsetName charset that should be used to read the file
 
221
     * @param outputIgnoredStatements whether ignored statements should be
 
222
     * included in the output
216
223
     *
217
224
     * @return database schema from dump file
218
 
     *
219
 
     * @throws FileException Thrown if file not found.
220
225
     */
221
226
    public static PgDatabase loadDatabaseSchema(final String file,
222
 
            final String charsetName) {
 
227
            final String charsetName, final boolean outputIgnoredStatements) {
223
228
        try {
224
 
            return loadDatabaseSchema(new FileInputStream(file), charsetName);
 
229
            return loadDatabaseSchema(new FileInputStream(file), charsetName,
 
230
                    outputIgnoredStatements);
225
231
        } catch (final FileNotFoundException ex) {
226
232
            throw new FileException("File '" + file + "' not found", ex);
227
233
        }
228
234
    }
229
235
 
230
236
    /**
231
 
     * Reads whole command from the reader into single-line string.
 
237
     * Reads whole statement from the reader into single-line string.
232
238
     *
233
239
     * @param reader reader to be read
234
240
     * @param line first line read
235
241
     *
236
 
     * @return whole command from the reader into single-line string
237
 
     *
238
 
     * @throws FileException Thrown if problem occured while reading string
239
 
     *         from <code>reader</code>.
 
242
     * @return whole statement from the reader into single-line string
240
243
     */
241
 
    private static String getWholeCommand(final BufferedReader reader,
 
244
    private static String getWholeStatement(final BufferedReader reader,
242
245
            final String line) {
243
246
        String newLine = line.trim();
244
 
        final StringBuilder sbCommand = new StringBuilder(newLine);
 
247
        final StringBuilder sbStatement = new StringBuilder(newLine);
245
248
 
246
249
        while (!newLine.trim().endsWith(";")) {
247
250
            try {
251
254
            }
252
255
 
253
256
            if (newLine.length() > 0) {
254
 
                sbCommand.append(' ');
255
 
                sbCommand.append(newLine);
 
257
                sbStatement.append(' ');
 
258
                sbStatement.append(newLine);
256
259
            }
257
260
        }
258
261
 
259
 
        return sbCommand.toString();
 
262
        return sbStatement.toString();
260
263
    }
261
264
 
262
265
    /**
267
270
     * @param line first line read
268
271
     *
269
272
     * @return whole CREATE FUNCTION DDL from the reader into multi-line string
270
 
     *
271
 
     * @throws FileException Thrown if problem occured while reading string
272
 
     *         from<code>reader</code>.
273
 
     * @throws RuntimeException Thrown if cannot find end of function.
274
273
     */
275
274
    private static String getWholeFunction(final BufferedReader reader,
276
275
            final String line) {
277
276
        final String firstLine = line;
278
 
        final StringBuilder sbCommand = new StringBuilder();
 
277
        final StringBuilder sbStatement = new StringBuilder(1000);
279
278
        String newLine = line;
280
 
        Pattern endOfFunctionPattern = null;
 
279
        String endOfFunction = null;
 
280
        boolean ignoreFirstOccurence = true;
281
281
        boolean searchForSemicolon = false;
282
282
 
283
283
        while (newLine != null) {
284
 
            if (!searchForSemicolon && (endOfFunctionPattern == null)) {
 
284
            if (endOfFunction == null) {
285
285
                final Matcher matcher =
286
286
                        PATTERN_END_OF_FUNCTION.matcher(newLine);
287
287
 
288
288
                if (matcher.matches()) {
289
 
                    String endOfFunction = matcher.group(1);
 
289
                    final String string = matcher.group(1);
290
290
 
291
 
                    if (endOfFunction.charAt(0) == '\'') {
 
291
                    if (string.charAt(0) == '\'') {
292
292
                        endOfFunction = "'";
293
293
                    } else {
294
 
                        endOfFunction = endOfFunction.substring(
295
 
                                0, endOfFunction.indexOf('$', 1) + 1);
296
 
                    }
297
 
 
298
 
                    if ("'".equals(endOfFunction)) {
299
 
                        endOfFunctionPattern = Pattern.compile(
300
 
                                "(?:.*[^\\\\]'.*|^.*[\\s]*'[\\s]*.*$)");
301
 
                    } else {
302
 
                        endOfFunctionPattern = Pattern.compile(
303
 
                                ".*\\Q" + endOfFunction + "\\E.*$",
304
 
                                Pattern.CASE_INSENSITIVE);
305
 
                    }
306
 
 
307
 
                    final String stripped = newLine.replaceAll(
308
 
                            "[\\s]+AS[\\s]+\\Q" + endOfFunction + "\\E", " ");
309
 
                    searchForSemicolon =
310
 
                            endOfFunctionPattern.matcher(stripped).matches();
 
294
                        endOfFunction =
 
295
                                string.substring(0, string.indexOf('$', 1) + 1);
 
296
                    }
311
297
                }
312
298
            }
313
299
 
314
 
            sbCommand.append(newLine);
315
 
            sbCommand.append('\n');
316
 
 
317
 
            if (searchForSemicolon && newLine.trim().endsWith(";")) {
318
 
                break;
 
300
            sbStatement.append(newLine);
 
301
            sbStatement.append('\n');
 
302
 
 
303
            if (endOfFunction != null) {
 
304
                // count occurences
 
305
                if (!searchForSemicolon) {
 
306
                    int count = ignoreFirstOccurence ? -1 : 0;
 
307
                    int pos = newLine.indexOf(endOfFunction);
 
308
                    ignoreFirstOccurence = false;
 
309
 
 
310
                    while (pos != -1) {
 
311
                        count++;
 
312
                        pos = newLine.indexOf(
 
313
                                endOfFunction, pos + endOfFunction.length());
 
314
                    }
 
315
 
 
316
                    if (count % 2 == 1) {
 
317
                        searchForSemicolon = true;
 
318
                    }
 
319
                }
 
320
 
 
321
                if (searchForSemicolon && newLine.trim().endsWith(";")) {
 
322
                    break;
 
323
                }
319
324
            }
320
325
 
321
326
            try {
328
333
                throw new RuntimeException(
329
334
                        "Cannot find end of function: " + firstLine);
330
335
            }
331
 
 
332
 
            if (!searchForSemicolon && (endOfFunctionPattern != null)
333
 
                    && endOfFunctionPattern.matcher(newLine).matches()) {
334
 
                searchForSemicolon = true;
335
 
            }
336
336
        }
337
337
 
338
 
        return sbCommand.toString();
 
338
        return sbStatement.toString();
339
339
    }
340
340
 
341
341
    /**
342
 
     * Strips comment from command line.
343
 
     *
344
 
     * @param command command
345
 
     *
346
 
     * @return if comment was found then command without the comment, otherwise
347
 
     *         the original command
 
342
     * Strips comment from statement line.
 
343
     *
 
344
     * @param statement statement
 
345
     *
 
346
     * @return if comment was found then statement without the comment,
 
347
     * otherwise the original statement
348
348
     */
349
 
    private static String stripComment(final String command) {
350
 
        String result = command;
 
349
    private static String stripComment(final String statement) {
 
350
        String result = statement;
351
351
        int pos = result.indexOf("--");
352
352
 
353
353
        while (pos >= 0) {