2
* Copyright 2006 StartNet s.r.o.
4
* Distributed under MIT license
1
6
package cz.startnet.utils.pgdiff.parsers;
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;
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;
12
* Parses ALTER TABLE commands.
19
* Parses ALTER TABLE statements.
16
23
public class AlterTableParser {
19
* Pattern for matching ALTER TABLE ... OWNER TO ...;.
21
private static final Pattern PATTERN_OWNER = Pattern.compile(
22
"^ALTER[\\s]+TABLE[\\s]+.*[\\s]+OWNER[\\s]+TO[\\s]+.*;$",
23
Pattern.CASE_INSENSITIVE);
25
* Pattern for matching table name and optional definition.
27
private static final Pattern PATTERN_START = Pattern.compile(
28
"ALTER[\\s]+TABLE[\\s]+(?:ONLY[\\s]+)?\"?([^\\s\"]+)\"?"
29
+ "(?:[\\s]+)?(.+)?", Pattern.CASE_INSENSITIVE);
31
* Pattern for matching of trailing definition of ALTER TABLE
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);
39
* Pattern for matching ADD CONSTRAINT row.
41
private static final Pattern PATTERN_ADD_CONSTRAINT = Pattern.compile(
42
"^ADD[\\s]+CONSTRAINT[\\s]+\"?([^\\s\"]+)\"?[\\s]+(.*)$",
43
Pattern.CASE_INSENSITIVE);
45
* Pattern for matching ADD FOREIGN KEY row.
47
private static final Pattern PATTERN_ADD_FOREIGN_KEY = Pattern.compile(
48
"^ADD[\\s]+(FOREIGN[\\s]+KEY[\\s]+\\(([^\\s]+)\\)[\\s]+.*)$",
49
Pattern.CASE_INSENSITIVE);
51
* Pattern for matching ALTER COLUMN ... SET DEFAULT ...
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);
57
* Pattern for checking whether string is ALTER COLUMN.
59
private static final Pattern PATTERN_ALTER_COLUMN =
60
Pattern.compile("ALTER[\\s]+COLUMN", Pattern.CASE_INSENSITIVE);
63
26
* Creates a new instance of AlterTableParser.
65
28
private AlterTableParser() {
70
* Parses ALTER TABLE command.
32
* Parses ALTER TABLE statement.
72
34
* @param database database
73
* @param command ALTER TABLE command
75
* @throws ParserException Thrown if problem occured while parsing DDL.
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;
84
tableName = matcher.group(1).trim();
86
throw new ParserException(
87
ParserException.CANNOT_PARSE_COMMAND + line);
92
ParserUtils.getSchemaName(tableName, database)).getTable(
93
ParserUtils.getObjectName(tableName));
94
line = ParserUtils.removeLastSemicolon(matcher.group(2));
96
if (PATTERN_TRAILING_DEF.matcher(line).matches()) {
97
parseTraillingDef(table, line.trim());
99
parseRows(table, line);
105
* Parses all rows in ALTER TABLE command.
107
* @param table table being parsed
108
* @param commands commands
110
* @throws ParserException Thrown if problem occured while parsing DDL.
112
private static void parseRows(final PgTable table, final String commands) {
113
String line = commands;
114
String subCommand = null;
116
while (line.length() > 0) {
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);
123
Matcher matcher = PATTERN_ADD_CONSTRAINT.matcher(subCommand);
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());
135
if (subCommand.length() > 0) {
136
matcher = PATTERN_ADD_FOREIGN_KEY.matcher(subCommand);
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());
151
if (subCommand.length() > 0) {
152
matcher = PATTERN_SET_DEFAULT.matcher(subCommand);
154
if (matcher.matches()) {
155
final String columnName = matcher.group(1).trim();
156
final String defaultValue = matcher.group(2).trim();
158
if (table.containsColumn(columnName)) {
159
final PgColumn column = table.getColumn(columnName);
160
column.setDefaultValue(defaultValue);
162
throw new ParserException(
163
"Cannot find column '" + columnName
164
+ " 'in table '" + table.getName() + "'");
170
} catch (RuntimeException ex) {
171
throw new ParserException(
172
"Cannot parse ALTER TABLE '" + table.getName()
173
+ "', line '" + subCommand + "'", ex);
176
if (subCommand.length() > 0) {
177
throw new ParserException(
178
"Don't know how to parse: " + subCommand);
184
* Parses trailling definition.
186
* @param table table being parsed
187
* @param traillingDef trailling definition
189
private static void parseTraillingDef(final PgTable table,
190
final String traillingDef) {
191
final Matcher matcher = PATTERN_TRAILING_DEF.matcher(traillingDef);
193
if (matcher.matches()) {
194
if (PATTERN_ALTER_COLUMN.matcher(
195
matcher.group(1).trim()).matches()) {
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);
203
final String indexName = matcher.group(2).trim();
204
table.setClusterIndexName(indexName);
35
* @param statement ALTER TABLE statement
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");
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);
49
final PgTable table = schema.getTable(objectName);
52
final PgView view = schema.getView(objectName);
55
parseView(parser, view);
59
final PgSequence sequence = schema.getSequence(objectName);
61
if (sequence != null) {
62
parseSequence(parser, sequence);
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);
82
parser.throwUnsupportedCommand();
84
} else if (parser.expectOptional("ENABLE")) {
86
} else if (parser.expectOptional("DISABLE")) {
89
parser.throwUnsupportedCommand();
92
if (parser.expectOptional(";")) {
101
* Parses ENABLE statements.
103
* @param parser parser
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();
112
parser.throwUnsupportedCommand();
114
} else if (parser.expectOptional("ALWAYS")) {
115
if (parser.expectOptional("TRIGGER")) {
116
parser.parseIdentifier();
117
} else if (parser.expectOptional("RULE")) {
118
parser.parseIdentifier();
120
parser.throwUnsupportedCommand();
126
* Parses DISABLE statements.
128
* @param parser parser
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();
136
parser.throwUnsupportedCommand();
141
* Parses ADD CONSTRAINT action.
143
* @param parser parser
144
* @param table pg table
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());
157
* Parses ALTER COLUMN action.
159
* @param parser parser
160
* @param table pg table
162
private static void parseAlterColumn(final Parser parser,
163
final PgTable table) {
164
parser.expectOptional("COLUMN");
166
final String columnName =
167
ParserUtils.getObjectName(parser.parseIdentifier());
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();
176
if (table.containsColumn(columnName)) {
177
final PgColumn column = table.getColumn(columnName);
178
column.setDefaultValue(defaultValue);
180
throw new ParserException("Cannot find column '"
181
+ columnName + " 'in table '" + table.getName()
184
} else if (parser.expectOptional("STORAGE")) {
185
final PgColumn column = table.getColumn(columnName);
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");
196
parser.throwUnsupportedCommand();
199
parser.throwUnsupportedCommand();
202
parser.throwUnsupportedCommand();
207
* Parses ADD FOREIGN KEY action.
209
* @param parser parser
210
* @param table pg table
212
private static void parseAddForeignKey(final Parser parser,
213
final PgTable table) {
214
final List<String> columnNames = new ArrayList<String>(1);
217
while (!parser.expectOptional(")")) {
219
ParserUtils.getObjectName(parser.parseIdentifier()));
221
if (parser.expectOptional(")")) {
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());
238
* Parses ALTER TABLE view.
240
* @param parser parser
243
private static void parseView(final Parser parser, final PgView view) {
244
while (!parser.expectOptional(";")) {
245
if (parser.expectOptional("ALTER")) {
246
parser.expectOptional("COLUMN");
248
final String columnName =
249
ParserUtils.getObjectName(parser.parseIdentifier());
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);
257
parser.throwUnsupportedCommand();
259
} else if (parser.expectOptional("OWNER", "TO")) {
260
// we do not support OWNER TO so just consume the rest
261
parser.getExpression();
263
parser.throwUnsupportedCommand();
269
* Parses ALTER TABLE sequence.
271
* @param parser parser
272
* @param sequence sequence
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();
281
parser.throwUnsupportedCommand();