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

« back to all changes in this revision

Viewing changes to src/main/java/cz/startnet/utils/pgdiff/parsers/AlterTableParser.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.parsers;
2
7
 
3
8
import cz.startnet.utils.pgdiff.schema.PgColumn;
4
9
import cz.startnet.utils.pgdiff.schema.PgConstraint;
5
10
import cz.startnet.utils.pgdiff.schema.PgDatabase;
 
11
import cz.startnet.utils.pgdiff.schema.PgSchema;
 
12
import cz.startnet.utils.pgdiff.schema.PgSequence;
6
13
import cz.startnet.utils.pgdiff.schema.PgTable;
7
 
 
8
 
import java.util.regex.Matcher;
9
 
import java.util.regex.Pattern;
 
14
import cz.startnet.utils.pgdiff.schema.PgView;
 
15
import java.util.ArrayList;
 
16
import java.util.List;
10
17
 
11
18
/**
12
 
 * Parses ALTER TABLE commands.
 
19
 * Parses ALTER TABLE statements.
13
20
 *
14
21
 * @author fordfrog
15
22
 */
16
23
public class AlterTableParser {
17
24
 
18
25
    /**
19
 
     * Pattern for matching ALTER TABLE ... OWNER TO ...;.
20
 
     */
21
 
    private static final Pattern PATTERN_OWNER = Pattern.compile(
22
 
            "^ALTER[\\s]+TABLE[\\s]+.*[\\s]+OWNER[\\s]+TO[\\s]+.*;$",
23
 
            Pattern.CASE_INSENSITIVE);
24
 
    /**
25
 
     * Pattern for matching table name and optional definition.
26
 
     */
27
 
    private static final Pattern PATTERN_START = Pattern.compile(
28
 
            "ALTER[\\s]+TABLE[\\s]+(?:ONLY[\\s]+)?\"?([^\\s\"]+)\"?"
29
 
            + "(?:[\\s]+)?(.+)?", Pattern.CASE_INSENSITIVE);
30
 
    /**
31
 
     * Pattern for matching of trailing definition of ALTER TABLE
32
 
     * command.
33
 
     */
34
 
    private static final Pattern PATTERN_TRAILING_DEF = Pattern.compile(
35
 
            "(CLUSTER[\\s]+ON|ALTER[\\s]+COLUMN)[\\s]+\"?([^\\s;\"]+)\"?"
36
 
            + "(?:[\\s]+SET[\\s]+STATISTICS[\\s]+)?(\\d+)?;?",
37
 
            Pattern.CASE_INSENSITIVE);
38
 
    /**
39
 
     * Pattern for matching ADD CONSTRAINT row.
40
 
     */
41
 
    private static final Pattern PATTERN_ADD_CONSTRAINT = Pattern.compile(
42
 
            "^ADD[\\s]+CONSTRAINT[\\s]+\"?([^\\s\"]+)\"?[\\s]+(.*)$",
43
 
            Pattern.CASE_INSENSITIVE);
44
 
    /**
45
 
     * Pattern for matching ADD FOREIGN KEY row.
46
 
     */
47
 
    private static final Pattern PATTERN_ADD_FOREIGN_KEY = Pattern.compile(
48
 
            "^ADD[\\s]+(FOREIGN[\\s]+KEY[\\s]+\\(([^\\s]+)\\)[\\s]+.*)$",
49
 
            Pattern.CASE_INSENSITIVE);
50
 
    /**
51
 
     * Pattern for matching ALTER COLUMN ... SET DEFAULT ...
52
 
     */
53
 
    private static final Pattern PATTERN_SET_DEFAULT = Pattern.compile(
54
 
            "^ALTER[\\s]+COLUMN[\\s]+\"?([^\\s\"]+)\"?[\\s]+SET[\\s]+"
55
 
            + "DEFAULT[\\s]+(.*)$", Pattern.CASE_INSENSITIVE);
56
 
    /**
57
 
     * Pattern for checking whether string is ALTER COLUMN.
58
 
     */
59
 
    private static final Pattern PATTERN_ALTER_COLUMN =
60
 
            Pattern.compile("ALTER[\\s]+COLUMN", Pattern.CASE_INSENSITIVE);
61
 
 
62
 
    /**
63
26
     * Creates a new instance of AlterTableParser.
64
27
     */
65
28
    private AlterTableParser() {
66
 
        super();
67
29
    }
68
30
 
69
31
    /**
70
 
     * Parses ALTER TABLE command.
 
32
     * Parses ALTER TABLE statement.
71
33
     *
72
34
     * @param database database
73
 
     * @param command ALTER TABLE command
74
 
     *
75
 
     * @throws ParserException Thrown if problem occured while parsing DDL.
76
 
     */
77
 
    public static void parse(final PgDatabase database, final String command) {
78
 
        if (!PATTERN_OWNER.matcher(command).matches()) {
79
 
            String line = command;
80
 
            final Matcher matcher = PATTERN_START.matcher(line);
81
 
            final String tableName;
82
 
 
83
 
            if (matcher.find()) {
84
 
                tableName = matcher.group(1).trim();
85
 
            } else {
86
 
                throw new ParserException(
87
 
                        ParserException.CANNOT_PARSE_COMMAND + line);
88
 
            }
89
 
 
90
 
            final PgTable table =
91
 
                    database.getSchema(
92
 
                    ParserUtils.getSchemaName(tableName, database)).getTable(
93
 
                    ParserUtils.getObjectName(tableName));
94
 
            line = ParserUtils.removeLastSemicolon(matcher.group(2));
95
 
 
96
 
            if (PATTERN_TRAILING_DEF.matcher(line).matches()) {
97
 
                parseTraillingDef(table, line.trim());
98
 
            } else {
99
 
                parseRows(table, line);
100
 
            }
101
 
        }
102
 
    }
103
 
 
104
 
    /**
105
 
     * Parses all rows in ALTER TABLE command.
106
 
     *
107
 
     * @param table table being parsed
108
 
     * @param commands commands
109
 
     *
110
 
     * @throws ParserException Thrown if problem occured while parsing DDL.
111
 
     */
112
 
    private static void parseRows(final PgTable table, final String commands) {
113
 
        String line = commands;
114
 
        String subCommand = null;
115
 
 
116
 
        while (line.length() > 0) {
117
 
            try {
118
 
                final int commandEnd = ParserUtils.getCommandEnd(line, 0);
119
 
                subCommand = line.substring(0, commandEnd).trim();
120
 
                line = (commandEnd >= line.length()) ? ""
121
 
                        : line.substring(commandEnd + 1);
122
 
 
123
 
                Matcher matcher = PATTERN_ADD_CONSTRAINT.matcher(subCommand);
124
 
 
125
 
                if (matcher.matches()) {
126
 
                    final String constraintName = matcher.group(1).trim();
127
 
                    final PgConstraint constraint =
128
 
                            new PgConstraint(constraintName);
129
 
                    table.addConstraint(constraint);
130
 
                    constraint.setDefinition(matcher.group(2).trim());
131
 
                    constraint.setTableName(table.getName());
132
 
                    subCommand = "";
133
 
                }
134
 
 
135
 
                if (subCommand.length() > 0) {
136
 
                    matcher = PATTERN_ADD_FOREIGN_KEY.matcher(subCommand);
137
 
 
138
 
                    if (matcher.matches()) {
139
 
                        final String columnName = matcher.group(2).trim();
140
 
                        final String constraintName =
141
 
                                table.getName() + "_" + columnName + "_fkey";
142
 
                        final PgConstraint constraint =
143
 
                                new PgConstraint(constraintName);
144
 
                        table.addConstraint(constraint);
145
 
                        constraint.setDefinition(matcher.group(1).trim());
146
 
                        constraint.setTableName(table.getName());
147
 
                        subCommand = "";
148
 
                    }
149
 
                }
150
 
 
151
 
                if (subCommand.length() > 0) {
152
 
                    matcher = PATTERN_SET_DEFAULT.matcher(subCommand);
153
 
 
154
 
                    if (matcher.matches()) {
155
 
                        final String columnName = matcher.group(1).trim();
156
 
                        final String defaultValue = matcher.group(2).trim();
157
 
 
158
 
                        if (table.containsColumn(columnName)) {
159
 
                            final PgColumn column = table.getColumn(columnName);
160
 
                            column.setDefaultValue(defaultValue);
161
 
                        } else {
162
 
                            throw new ParserException(
163
 
                                    "Cannot find column '" + columnName
164
 
                                    + " 'in table '" + table.getName() + "'");
165
 
                        }
166
 
 
167
 
                        subCommand = "";
168
 
                    }
169
 
                }
170
 
            } catch (RuntimeException ex) {
171
 
                throw new ParserException(
172
 
                        "Cannot parse ALTER TABLE '" + table.getName()
173
 
                        + "', line '" + subCommand + "'", ex);
174
 
            }
175
 
 
176
 
            if (subCommand.length() > 0) {
177
 
                throw new ParserException(
178
 
                        "Don't know how to parse: " + subCommand);
179
 
            }
180
 
        }
181
 
    }
182
 
 
183
 
    /**
184
 
     * Parses trailling definition.
185
 
     *
186
 
     * @param table table being parsed
187
 
     * @param traillingDef trailling definition
188
 
     */
189
 
    private static void parseTraillingDef(final PgTable table,
190
 
            final String traillingDef) {
191
 
        final Matcher matcher = PATTERN_TRAILING_DEF.matcher(traillingDef);
192
 
 
193
 
        if (matcher.matches()) {
194
 
            if (PATTERN_ALTER_COLUMN.matcher(
195
 
                    matcher.group(1).trim()).matches()) {
196
 
                //Stats
197
 
                final String columnName = matcher.group(2).trim();
198
 
                final Integer value = Integer.valueOf(matcher.group(3).trim());
199
 
                final PgColumn col = table.getColumn(columnName);
200
 
                col.setStatistics(value);
201
 
            } else {
202
 
                //Cluster
203
 
                final String indexName = matcher.group(2).trim();
204
 
                table.setClusterIndexName(indexName);
 
35
     * @param statement ALTER TABLE statement
 
36
     */
 
37
    public static void parse(final PgDatabase database,
 
38
            final String statement) {
 
39
        final Parser parser = new Parser(statement);
 
40
        parser.expect("ALTER", "TABLE");
 
41
        parser.expectOptional("ONLY");
 
42
 
 
43
        final String tableName = parser.parseIdentifier();
 
44
        final String schemaName =
 
45
                ParserUtils.getSchemaName(tableName, database);
 
46
        final PgSchema schema = database.getSchema(schemaName);
 
47
        final String objectName = ParserUtils.getObjectName(tableName);
 
48
 
 
49
        final PgTable table = schema.getTable(objectName);
 
50
 
 
51
        if (table == null) {
 
52
            final PgView view = schema.getView(objectName);
 
53
 
 
54
            if (view != null) {
 
55
                parseView(parser, view);
 
56
                return;
 
57
            }
 
58
 
 
59
            final PgSequence sequence = schema.getSequence(objectName);
 
60
 
 
61
            if (sequence != null) {
 
62
                parseSequence(parser, sequence);
 
63
                return;
 
64
            }
 
65
        }
 
66
 
 
67
        while (!parser.expectOptional(";")) {
 
68
            if (parser.expectOptional("ALTER")) {
 
69
                parseAlterColumn(parser, table);
 
70
            } else if (parser.expectOptional("CLUSTER", "ON")) {
 
71
                table.setClusterIndexName(
 
72
                        ParserUtils.getObjectName(parser.parseIdentifier()));
 
73
            } else if (parser.expectOptional("OWNER", "TO")) {
 
74
                // we do not parse this one so we just consume the expression
 
75
                parser.getExpression();
 
76
            } else if (parser.expectOptional("ADD")) {
 
77
                if (parser.expectOptional("FOREIGN", "KEY")) {
 
78
                    parseAddForeignKey(parser, table);
 
79
                } else if (parser.expectOptional("CONSTRAINT")) {
 
80
                    parseAddConstraint(parser, table);
 
81
                } else {
 
82
                    parser.throwUnsupportedCommand();
 
83
                }
 
84
            } else if (parser.expectOptional("ENABLE")) {
 
85
                parseEnable(parser);
 
86
            } else if (parser.expectOptional("DISABLE")) {
 
87
                parseDisable(parser);
 
88
            } else {
 
89
                parser.throwUnsupportedCommand();
 
90
            }
 
91
 
 
92
            if (parser.expectOptional(";")) {
 
93
                break;
 
94
            } else {
 
95
                parser.expect(",");
 
96
            }
 
97
        }
 
98
    }
 
99
 
 
100
    /**
 
101
     * Parses ENABLE statements.
 
102
     *
 
103
     * @param parser parser
 
104
     */
 
105
    private static void parseEnable(final Parser parser) {
 
106
        if (parser.expectOptional("REPLICA")) {
 
107
            if (parser.expectOptional("TRIGGER")) {
 
108
                parser.parseIdentifier();
 
109
            } else if (parser.expectOptional("RULE")) {
 
110
                parser.parseIdentifier();
 
111
            } else {
 
112
                parser.throwUnsupportedCommand();
 
113
            }
 
114
        } else if (parser.expectOptional("ALWAYS")) {
 
115
            if (parser.expectOptional("TRIGGER")) {
 
116
                parser.parseIdentifier();
 
117
            } else if (parser.expectOptional("RULE")) {
 
118
                parser.parseIdentifier();
 
119
            } else {
 
120
                parser.throwUnsupportedCommand();
 
121
            }
 
122
        }
 
123
    }
 
124
 
 
125
    /**
 
126
     * Parses DISABLE statements.
 
127
     *
 
128
     * @param parser parser
 
129
     */
 
130
    private static void parseDisable(final Parser parser) {
 
131
        if (parser.expectOptional("TRIGGER")) {
 
132
            parser.parseIdentifier();
 
133
        } else if (parser.expectOptional("RULE")) {
 
134
            parser.parseIdentifier();
 
135
        } else {
 
136
            parser.throwUnsupportedCommand();
 
137
        }
 
138
    }
 
139
 
 
140
    /**
 
141
     * Parses ADD CONSTRAINT action.
 
142
     *
 
143
     * @param parser parser
 
144
     * @param table pg table
 
145
     */
 
146
    private static void parseAddConstraint(final Parser parser,
 
147
            final PgTable table) {
 
148
        final String constraintName =
 
149
                ParserUtils.getObjectName(parser.parseIdentifier());
 
150
        final PgConstraint constraint = new PgConstraint(constraintName);
 
151
        table.addConstraint(constraint);
 
152
        constraint.setDefinition(parser.getExpression());
 
153
        constraint.setTableName(table.getName());
 
154
    }
 
155
 
 
156
    /**
 
157
     * Parses ALTER COLUMN action.
 
158
     *
 
159
     * @param parser parser
 
160
     * @param table pg table
 
161
     */
 
162
    private static void parseAlterColumn(final Parser parser,
 
163
            final PgTable table) {
 
164
        parser.expectOptional("COLUMN");
 
165
 
 
166
        final String columnName =
 
167
                ParserUtils.getObjectName(parser.parseIdentifier());
 
168
 
 
169
        if (parser.expectOptional("SET")) {
 
170
            if (parser.expectOptional("STATISTICS")) {
 
171
                final PgColumn column = table.getColumn(columnName);
 
172
                column.setStatistics(parser.parseInteger());
 
173
            } else if (parser.expectOptional("DEFAULT")) {
 
174
                final String defaultValue = parser.getExpression();
 
175
 
 
176
                if (table.containsColumn(columnName)) {
 
177
                    final PgColumn column = table.getColumn(columnName);
 
178
                    column.setDefaultValue(defaultValue);
 
179
                } else {
 
180
                    throw new ParserException("Cannot find column '"
 
181
                            + columnName + " 'in table '" + table.getName()
 
182
                            + "'");
 
183
                }
 
184
            } else if (parser.expectOptional("STORAGE")) {
 
185
                final PgColumn column = table.getColumn(columnName);
 
186
 
 
187
                if (parser.expectOptional("PLAIN")) {
 
188
                    column.setStorage("PLAIN");
 
189
                } else if (parser.expectOptional("EXTERNAL")) {
 
190
                    column.setStorage("EXTERNAL");
 
191
                } else if (parser.expectOptional("EXTENDED")) {
 
192
                    column.setStorage("EXTENDED");
 
193
                } else if (parser.expectOptional("MAIN")) {
 
194
                    column.setStorage("MAIN");
 
195
                } else {
 
196
                    parser.throwUnsupportedCommand();
 
197
                }
 
198
            } else {
 
199
                parser.throwUnsupportedCommand();
 
200
            }
 
201
        } else {
 
202
            parser.throwUnsupportedCommand();
 
203
        }
 
204
    }
 
205
 
 
206
    /**
 
207
     * Parses ADD FOREIGN KEY action.
 
208
     *
 
209
     * @param parser parser
 
210
     * @param table pg table
 
211
     */
 
212
    private static void parseAddForeignKey(final Parser parser,
 
213
            final PgTable table) {
 
214
        final List<String> columnNames = new ArrayList<String>(1);
 
215
        parser.expect("(");
 
216
 
 
217
        while (!parser.expectOptional(")")) {
 
218
            columnNames.add(
 
219
                    ParserUtils.getObjectName(parser.parseIdentifier()));
 
220
 
 
221
            if (parser.expectOptional(")")) {
 
222
                break;
 
223
            } else {
 
224
                parser.expect(",");
 
225
            }
 
226
        }
 
227
 
 
228
        final String constraintName = ParserUtils.generateName(
 
229
                table.getName() + "_", columnNames, "_fkey");
 
230
        final PgConstraint constraint =
 
231
                new PgConstraint(constraintName);
 
232
        table.addConstraint(constraint);
 
233
        constraint.setDefinition(parser.getExpression());
 
234
        constraint.setTableName(table.getName());
 
235
    }
 
236
 
 
237
    /**
 
238
     * Parses ALTER TABLE view.
 
239
     * 
 
240
     * @param parser parser
 
241
     * @param view view
 
242
     */
 
243
    private static void parseView(final Parser parser, final PgView view) {
 
244
        while (!parser.expectOptional(";")) {
 
245
            if (parser.expectOptional("ALTER")) {
 
246
                parser.expectOptional("COLUMN");
 
247
 
 
248
                final String columnName =
 
249
                        ParserUtils.getObjectName(parser.parseIdentifier());
 
250
 
 
251
                if (parser.expectOptional("SET", "DEFAULT")) {
 
252
                    final String expression = parser.getExpression();
 
253
                    view.addColumnDefaultValue(columnName, expression);
 
254
                } else if (parser.expectOptional("DROP", "DEFAULT")) {
 
255
                    view.removeColumnDefaultValue(columnName);
 
256
                } else {
 
257
                    parser.throwUnsupportedCommand();
 
258
                }
 
259
            } else if (parser.expectOptional("OWNER", "TO")) {
 
260
                // we do not support OWNER TO so just consume the rest
 
261
                parser.getExpression();
 
262
            } else {
 
263
                parser.throwUnsupportedCommand();
 
264
            }
 
265
        }
 
266
    }
 
267
 
 
268
    /**
 
269
     * Parses ALTER TABLE sequence.
 
270
     *
 
271
     * @param parser parser
 
272
     * @param sequence sequence
 
273
     */
 
274
    private static void parseSequence(final Parser parser,
 
275
            final PgSequence sequence) {
 
276
        while (!parser.expectOptional(";")) {
 
277
            if (parser.expectOptional("OWNER", "TO")) {
 
278
                // we do not support OWNER TO so just consume the rest
 
279
                parser.getExpression();
 
280
            } else {
 
281
                parser.throwUnsupportedCommand();
205
282
            }
206
283
        }
207
284
    }