~ubuntu-branches/ubuntu/raring/apgdiff/raring

« 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-11 09:08:18 UTC
  • mfrom: (2.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20101011090818-sdw8yfemrnxo328k
Tags: 2.2.2-1
* New upstream version.
* Using changelog included in zipfile, thanks Miroslav for providing this.
* Update manpage.

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
 
 
8
import cz.startnet.utils.pgdiff.Resources;
3
9
import cz.startnet.utils.pgdiff.parsers.AlterTableParser;
 
10
import cz.startnet.utils.pgdiff.parsers.AlterViewParser;
4
11
import cz.startnet.utils.pgdiff.parsers.CreateFunctionParser;
5
12
import cz.startnet.utils.pgdiff.parsers.CreateIndexParser;
6
13
import cz.startnet.utils.pgdiff.parsers.CreateSchemaParser;
17
24
import java.io.InputStream;
18
25
import java.io.InputStreamReader;
19
26
import java.io.UnsupportedEncodingException;
 
27
import java.text.MessageFormat;
20
28
 
21
29
import java.util.regex.Matcher;
22
30
import java.util.regex.Pattern;
29
37
public class PgDumpLoader { //NOPMD
30
38
 
31
39
    /**
32
 
     * Pattern for testing whether command is CREATE SCHEMA command.
 
40
     * Pattern for testing whether it is CREATE SCHEMA statement.
33
41
     */
34
42
    private static final Pattern PATTERN_CREATE_SCHEMA = Pattern.compile(
35
43
            "^CREATE[\\s]+SCHEMA[\\s]+.*$", Pattern.CASE_INSENSITIVE);
41
49
            "^SET[\\s]+search_path[\\s]*=[\\s]*([^,\\s]+)(?:,[\\s]+.*)?;$",
42
50
            Pattern.CASE_INSENSITIVE);
43
51
    /**
44
 
     * Pattern for testing whether command is CREATE TABLE command.
 
52
     * Pattern for testing whether it is CREATE TABLE statement.
45
53
     */
46
54
    private static final Pattern PATTERN_CREATE_TABLE = Pattern.compile(
47
55
            "^CREATE[\\s]+TABLE[\\s]+.*$", Pattern.CASE_INSENSITIVE);
48
56
    /**
49
 
     * Pattern for testing whether command is CREATE VIEW command.
 
57
     * Pattern for testing whether it is CREATE VIEW statement.
50
58
     */
51
59
    private static final Pattern PATTERN_CREATE_VIEW = Pattern.compile(
52
60
            "^CREATE[\\s]+(?:OR[\\s]+REPLACE[\\s]+)?VIEW[\\s]+.*$",
53
61
            Pattern.CASE_INSENSITIVE);
54
62
    /**
55
 
     * Pattern for testing whether command is ALTER TABLE command.
 
63
     * Pattern for testing whether it is ALTER TABLE statement.
56
64
     */
57
65
    private static final Pattern PATTERN_ALTER_TABLE =
58
 
            Pattern.compile("^ALTER[\\s]+TABLE[\\s]+.*$", Pattern.CASE_INSENSITIVE);
 
66
            Pattern.compile("^ALTER[\\s]+TABLE[\\s]+.*$",
 
67
            Pattern.CASE_INSENSITIVE);
59
68
    /**
60
 
     * Pattern for testing whether command is CREATE SEQUENCE command.
 
69
     * Pattern for testing whether it is CREATE SEQUENCE statement.
61
70
     */
62
71
    private static final Pattern PATTERN_CREATE_SEQUENCE = Pattern.compile(
63
72
            "^CREATE[\\s]+SEQUENCE[\\s]+.*$", Pattern.CASE_INSENSITIVE);
64
73
    /**
65
 
     * Pattern for testing whether command is CREATE INDEX command.
 
74
     * Pattern for testing whether it is CREATE INDEX statement.
66
75
     */
67
76
    private static final Pattern PATTERN_CREATE_INDEX = Pattern.compile(
68
77
            "^CREATE[\\s]+(?:UNIQUE[\\s]+)?INDEX[\\s]+.*$",
69
78
            Pattern.CASE_INSENSITIVE);
70
79
    /**
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.
 
80
     * Pattern for testing whether it is SELECT statement.
82
81
     */
83
82
    private static final Pattern PATTERN_SELECT =
84
83
            Pattern.compile("^SELECT[\\s]+.*$", Pattern.CASE_INSENSITIVE);
85
84
    /**
86
 
     * Pattern for testing whether command is INSERT INTO command.
 
85
     * Pattern for testing whether it is INSERT INTO statement.
87
86
     */
88
87
    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.
 
88
            Pattern.compile("^INSERT[\\s]+INTO[\\s]+.*$",
 
89
            Pattern.CASE_INSENSITIVE);
 
90
    /**
 
91
     * Pattern for testing whether it is UPDATE statement.
 
92
     */
 
93
    private static final Pattern PATTERN_UPDATE =
 
94
            Pattern.compile("^UPDATE[\\s].*$", Pattern.CASE_INSENSITIVE);
 
95
    /**
 
96
     * Pattern for testing whether it is DELETE FROM statement.
 
97
     */
 
98
    private static final Pattern PATTERN_DELETE_FROM =
 
99
            Pattern.compile("^DELETE[\\s]+FROM[\\s]+.*$",
 
100
            Pattern.CASE_INSENSITIVE);
 
101
    /**
 
102
     * Pattern for testing whether it is CREATE TRIGGER statement.
102
103
     */
103
104
    private static final Pattern PATTERN_CREATE_TRIGGER = Pattern.compile(
104
105
            "^CREATE[\\s]+TRIGGER[\\s]+.*$", Pattern.CASE_INSENSITIVE);
105
106
    /**
106
 
     * Pattern for testing whether command is CREATE FUNCTION or CREATE
107
 
     * OR REPLACE FUNCTION command.
 
107
     * Pattern for testing whether it is CREATE FUNCTION or CREATE OR REPLACE
 
108
     * FUNCTION statement.
108
109
     */
109
110
    private static final Pattern PATTERN_CREATE_FUNCTION = Pattern.compile(
110
111
            "^CREATE[\\s]+(?:OR[\\s]+REPLACE[\\s]+)?FUNCTION[\\s]+.*$",
111
112
            Pattern.CASE_INSENSITIVE);
112
113
    /**
113
 
     * Pattern for getting the string that is used to end the function
114
 
     * or the function definition itself.
 
114
     * Pattern for testing whether it is ALTER VIEW statement.
115
115
     */
116
 
    private static final Pattern PATTERN_END_OF_FUNCTION = Pattern.compile(
117
 
            "^(?:.*[\\s]+)?AS[\\s]+(['$][^\\s]*).*$", Pattern.CASE_INSENSITIVE);
 
116
    private static final Pattern PATTERN_ALTER_VIEW = Pattern.compile(
 
117
            "^ALTER[\\s]+VIEW[\\s]+.*$", Pattern.CASE_INSENSITIVE);
118
118
 
119
119
    /**
120
120
     * Creates a new instance of PgDumpLoader.
121
121
     */
122
122
    private PgDumpLoader() {
123
 
        super();
124
123
    }
125
124
 
126
125
    /**
128
127
     *
129
128
     * @param inputStream input stream that should be read
130
129
     * @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.
 
130
     * @param outputIgnoredStatements whether ignored statements should be
 
131
     * included in the output
 
132
     *
 
133
     * @return database schema from dump file
138
134
     */
139
135
    public static PgDatabase loadDatabaseSchema(final InputStream inputStream,
140
 
            final String charsetName) { //NOPMD
 
136
            final String charsetName, final boolean outputIgnoredStatements) {
141
137
 
142
138
        final PgDatabase database = new PgDatabase();
143
139
        BufferedReader reader = null;
147
143
                    new InputStreamReader(inputStream, charsetName));
148
144
        } catch (final UnsupportedEncodingException ex) {
149
145
            throw new UnsupportedOperationException(
150
 
                    "Unsupported encoding: " + charsetName, ex);
 
146
                    Resources.getString("UnsupportedEncoding") + ": "
 
147
                    + charsetName, ex);
151
148
        }
152
149
 
153
150
        try {
163
160
                    continue;
164
161
                } else if (PATTERN_CREATE_SCHEMA.matcher(line).matches()) {
165
162
                    CreateSchemaParser.parse(
166
 
                            database, getWholeCommand(reader, line));
 
163
                            database, getWholeStatement(reader, line));
167
164
                } else if (PATTERN_DEFAULT_SCHEMA.matcher(line).matches()) {
168
165
                    final Matcher matcher =
169
166
                            PATTERN_DEFAULT_SCHEMA.matcher(line);
171
168
                    database.setDefaultSchema(matcher.group(1));
172
169
                } else if (PATTERN_CREATE_TABLE.matcher(line).matches()) {
173
170
                    CreateTableParser.parse(
174
 
                            database, getWholeCommand(reader, line));
 
171
                            database, getWholeStatement(reader, line));
175
172
                } else if (PATTERN_ALTER_TABLE.matcher(line).matches()) {
176
 
                    AlterTableParser.parse(
177
 
                            database, getWholeCommand(reader, line));
 
173
                    AlterTableParser.parse(database,
 
174
                            getWholeStatement(reader, line),
 
175
                            outputIgnoredStatements);
178
176
                } else if (PATTERN_CREATE_SEQUENCE.matcher(line).matches()) {
179
177
                    CreateSequenceParser.parse(
180
 
                            database, getWholeCommand(reader, line));
 
178
                            database, getWholeStatement(reader, line));
181
179
                } else if (PATTERN_CREATE_INDEX.matcher(line).matches()) {
182
180
                    CreateIndexParser.parse(
183
 
                            database, getWholeCommand(reader, line));
 
181
                            database, getWholeStatement(reader, line));
184
182
                } else if (PATTERN_CREATE_VIEW.matcher(line).matches()) {
185
183
                    CreateViewParser.parse(
186
 
                            database, getWholeCommand(reader, line));
 
184
                            database, getWholeStatement(reader, line));
 
185
                } else if (PATTERN_ALTER_VIEW.matcher(line).matches()) {
 
186
                    AlterViewParser.parse(database,
 
187
                            getWholeStatement(reader, line),
 
188
                            outputIgnoredStatements);
187
189
                } else if (PATTERN_CREATE_TRIGGER.matcher(line).matches()) {
188
190
                    CreateTriggerParser.parse(
189
 
                            database, getWholeCommand(reader, line));
 
191
                            database, getWholeStatement(reader, line));
190
192
                } else if (PATTERN_CREATE_FUNCTION.matcher(line).matches()) {
191
193
                    CreateFunctionParser.parse(
192
194
                            database, getWholeFunction(reader, line));
193
 
                } else if (PATTERN_SET.matcher(line).matches()
194
 
                        || PATTERN_COMMENT.matcher(line).matches()
195
 
                        || PATTERN_SELECT.matcher(line).matches()
 
195
                } else if (PATTERN_SELECT.matcher(line).matches()
196
196
                        || PATTERN_INSERT_INTO.matcher(line).matches()
197
 
                        || PATTERN_REVOKE.matcher(line).matches()
198
 
                        || PATTERN_GRANT.matcher(line).matches()) {
199
 
                    getWholeCommand(reader, line);
 
197
                        || PATTERN_UPDATE.matcher(line).matches()
 
198
                        || PATTERN_DELETE_FROM.matcher(line).matches()) {
 
199
                    getWholeStatement(reader, line);
 
200
                } else if (outputIgnoredStatements) {
 
201
                    database.addIgnoredStatement(
 
202
                            getWholeStatement(reader, line));
 
203
                } else {
 
204
                    getWholeStatement(reader, line);
200
205
                }
201
206
 
202
207
                line = reader.readLine();
203
208
            }
204
209
        } catch (final IOException ex) {
205
 
            throw new FileException(FileException.CANNOT_READ_FILE, ex);
 
210
            throw new FileException(Resources.getString("CannotReadFile"), ex);
206
211
        }
207
212
 
208
213
        return database;
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
 
            throw new FileException("File '" + file + "' not found", ex);
 
232
            throw new FileException(MessageFormat.format(
 
233
                    Resources.getString("FileNotFound"), file), ex);
227
234
        }
228
235
    }
229
236
 
230
237
    /**
231
 
     * Reads whole command from the reader into single-line string.
 
238
     * Reads whole statement from the reader into single-line string.
232
239
     *
233
240
     * @param reader reader to be read
234
241
     * @param line first line read
235
242
     *
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>.
 
243
     * @return whole statement from the reader into single-line string
240
244
     */
241
 
    private static String getWholeCommand(final BufferedReader reader,
 
245
    private static String getWholeStatement(final BufferedReader reader,
242
246
            final String line) {
243
247
        String newLine = line.trim();
244
 
        final StringBuilder sbCommand = new StringBuilder(newLine);
 
248
        final StringBuilder sbStatement = new StringBuilder(newLine);
245
249
 
246
250
        while (!newLine.trim().endsWith(";")) {
247
251
            try {
248
252
                newLine = stripComment(reader.readLine()).trim();
249
253
            } catch (IOException ex) {
250
 
                throw new FileException(FileException.CANNOT_READ_FILE, ex);
 
254
                throw new FileException(
 
255
                        Resources.getString("CannotReadFile"), ex);
251
256
            }
252
257
 
253
258
            if (newLine.length() > 0) {
254
 
                sbCommand.append(' ');
255
 
                sbCommand.append(newLine);
 
259
                sbStatement.append(' ');
 
260
                sbStatement.append(newLine);
256
261
            }
257
262
        }
258
263
 
259
 
        return sbCommand.toString();
 
264
        return sbStatement.toString();
260
265
    }
261
266
 
262
267
    /**
267
272
     * @param line first line read
268
273
     *
269
274
     * @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
275
     */
275
276
    private static String getWholeFunction(final BufferedReader reader,
276
277
            final String line) {
277
278
        final String firstLine = line;
278
 
        final StringBuilder sbCommand = new StringBuilder();
 
279
        final StringBuilder sbStatement = new StringBuilder(1000);
279
280
        String newLine = line;
280
 
        Pattern endOfFunctionPattern = null;
 
281
        String endOfFunction = null;
 
282
        boolean ignoreFirstOccurence = true;
281
283
        boolean searchForSemicolon = false;
 
284
        boolean nextIsSeparator = false;
282
285
 
283
286
        while (newLine != null) {
284
 
            if (!searchForSemicolon && (endOfFunctionPattern == null)) {
285
 
                final Matcher matcher =
286
 
                        PATTERN_END_OF_FUNCTION.matcher(newLine);
287
 
 
288
 
                if (matcher.matches()) {
289
 
                    String endOfFunction = matcher.group(1);
290
 
 
291
 
                    if (endOfFunction.charAt(0) == '\'') {
292
 
                        endOfFunction = "'";
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();
 
287
            if (endOfFunction == null) {
 
288
                boolean previousWasWhitespace = true;
 
289
 
 
290
                for (int i = 0; i < newLine.length(); i++) {
 
291
                    final char chr = newLine.charAt(i);
 
292
 
 
293
                    if (Character.isWhitespace(chr)) {
 
294
                        previousWasWhitespace = true;
 
295
                        continue;
 
296
                    } else if (nextIsSeparator) {
 
297
                        if (chr == '\'') {
 
298
                            endOfFunction = "'";
 
299
                        } else {
 
300
                            endOfFunction = newLine.substring(
 
301
                                    i, newLine.indexOf('$', i + 1) + 1);
 
302
                        }
 
303
 
 
304
                        break;
 
305
                    } else if (previousWasWhitespace
 
306
                            && Character.toUpperCase(chr) == 'A'
 
307
                            && Character.toUpperCase(newLine.charAt(i + 1)) == 'S'
 
308
                            && (i + 2 == newLine.length()
 
309
                            || Character.isWhitespace(newLine.charAt(i + 2)))) {
 
310
                        i += 2;
 
311
                        nextIsSeparator = true;
 
312
                    } else {
 
313
                        previousWasWhitespace = false;
 
314
                    }
311
315
                }
312
316
            }
313
317
 
314
 
            sbCommand.append(newLine);
315
 
            sbCommand.append('\n');
316
 
 
317
 
            if (searchForSemicolon && newLine.trim().endsWith(";")) {
318
 
                break;
 
318
            sbStatement.append(newLine);
 
319
            sbStatement.append('\n');
 
320
 
 
321
            if (endOfFunction != null) {
 
322
                // count occurences
 
323
                if (!searchForSemicolon) {
 
324
                    int count = ignoreFirstOccurence ? -1 : 0;
 
325
                    int pos = newLine.indexOf(endOfFunction);
 
326
                    ignoreFirstOccurence = false;
 
327
 
 
328
                    while (pos != -1) {
 
329
                        count++;
 
330
                        pos = newLine.indexOf(
 
331
                                endOfFunction, pos + endOfFunction.length());
 
332
                    }
 
333
 
 
334
                    if (count % 2 == 1) {
 
335
                        searchForSemicolon = true;
 
336
                    }
 
337
                }
 
338
 
 
339
                if (searchForSemicolon && newLine.trim().endsWith(";")) {
 
340
                    break;
 
341
                }
319
342
            }
320
343
 
321
344
            try {
322
345
                newLine = reader.readLine();
323
346
            } catch (final IOException ex) {
324
 
                throw new FileException(FileException.CANNOT_READ_FILE, ex);
 
347
                throw new FileException(
 
348
                        Resources.getString("CannotReadFile"), ex);
325
349
            }
326
350
 
327
351
            if (newLine == null) {
328
352
                throw new RuntimeException(
329
 
                        "Cannot find end of function: " + firstLine);
330
 
            }
331
 
 
332
 
            if (!searchForSemicolon && (endOfFunctionPattern != null)
333
 
                    && endOfFunctionPattern.matcher(newLine).matches()) {
334
 
                searchForSemicolon = true;
 
353
                        Resources.getString("CannotFindEndOfFunction") + ": "
 
354
                        + firstLine);
335
355
            }
336
356
        }
337
357
 
338
 
        return sbCommand.toString();
 
358
        return sbStatement.toString();
339
359
    }
340
360
 
341
361
    /**
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
 
362
     * Strips comment from statement line.
 
363
     *
 
364
     * @param statement statement
 
365
     *
 
366
     * @return if comment was found then statement without the comment,
 
367
     * otherwise the original statement
348
368
     */
349
 
    private static String stripComment(final String command) {
350
 
        String result = command;
 
369
    private static String stripComment(final String statement) {
 
370
        String result = statement;
351
371
        int pos = result.indexOf("--");
352
372
 
353
373
        while (pos >= 0) {