Skip to content

Commit f230f4c

Browse files
committed
feat: parse PostgreSQL base type definitions, ALTER TYPE SET and ALTER RULE
CREATE TYPE name (INPUT = fn, OUTPUT = fn, ...) and ALTER TYPE name SET (property = value, ...) failed at the parenthesis, and ALTER RULE name ON table RENAME TO new_name was an unsupported statement. CreateType gets a BaseTypeDefinition next to the composite, enum and range definitions, and AlterType the action SET_PROPERTIES with getProperties(). Both hold DefinitionElement items read by one PostgreSqlDefinition() rule that follows the definition of PostgreSQL's grammar: any label, and an optional DefinitionArgument that is a type (which is how the grammar reads a function name), a reserved keyword, an operator, a number, a string constant or NONE. Property names and the kind of value a property takes are not checked, as PostgreSQL checks them when it runs the statement. ALTER RULE parses to AlterRule with the rule name, the table and the new name, and is wired into the deparser, visitors, table finder, validation (alterRule, enabled for PostgreSQL) and feature analysis. It is the only ALTER RULE form, so other text after ALTER RULE is now a parse error. Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent e27929f commit f230f4c

25 files changed

Lines changed: 1197 additions & 9 deletions

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
158158
| **Routine changes** | `ALTER FUNCTION` · `ALTER PROCEDURE` · `ALTER ROUTINE` · PostgreSQL `ALTER TRIGGER` |
159159
| **MySQL metadata statements** | `SHOW [FULL] COLUMNS`/`FIELDS` · `SHOW TABLE STATUS` · `SHOW CREATE TABLE`/`VIEW`/`PROCEDURE`/`FUNCTION`/`TRIGGER`/`EVENT`/`DATABASE`/`USER` |
160160
| **PostgreSQL administration** | `CREATE`/`ALTER EVENT TRIGGER` · `REINDEX` · `SECURITY LABEL` |
161+
| **PostgreSQL types and rules** | `CREATE TYPE` (shell, composite, enum, range and base types) · `ALTER TYPE` · `CREATE RULE` · `ALTER RULE` |
161162
| **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints; enable with `parser.withDialect(Dialect.INFORMIX)` |
162163
| **Salesforce SOQL** | `INCLUDES` · `EXCLUDES` |
163164

‎src/main/java/net/sf/jsqlparser/parser/feature/Feature.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,12 @@ public enum Feature {
945945
* @see net.sf.jsqlparser.statement.alter.AlterTrigger
946946
*/
947947
alterTrigger,
948+
/**
949+
* PostgreSQL "ALTER RULE" statement is allowed
950+
*
951+
* @see net.sf.jsqlparser.statement.alter.AlterRule
952+
*/
953+
alterRule,
948954
/**
949955
* SQL block starting with "BEGIN" and ends with "END" statement is allowed
950956
*
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement;
11+
12+
import java.io.Serializable;
13+
import java.util.Objects;
14+
import net.sf.jsqlparser.expression.Expression;
15+
import net.sf.jsqlparser.expression.StringValue;
16+
import net.sf.jsqlparser.statement.create.table.ColDataType;
17+
18+
/**
19+
* The value of a {@link DefinitionElement}: PostgreSQL's {@code def_arg}. The grammar reads a name,
20+
* such as a function or a storage strategy, as a type name, so it is a {@link Kind#TYPE} argument.
21+
*/
22+
public final class DefinitionArgument implements Serializable {
23+
public enum Kind {
24+
/** A type or a name, see {@link #getDataType()}. */
25+
TYPE,
26+
/** A reserved keyword such as {@code TRUE} or {@code NULL}, see {@link #getKeyword()}. */
27+
KEYWORD,
28+
/** An operator, see {@link #getOperator()}. */
29+
OPERATOR,
30+
/** A number with an optional sign, see {@link #getNumber()}. */
31+
NUMBER,
32+
/** A string constant, see {@link #getString()}. */
33+
STRING,
34+
/** The keyword {@code NONE}. */
35+
NONE
36+
}
37+
38+
private final Kind kind;
39+
private ColDataType dataType;
40+
private boolean setOf;
41+
private boolean typeReference;
42+
private String keyword;
43+
private String operator;
44+
private boolean operatorKeyword;
45+
private Expression number;
46+
private StringValue string;
47+
48+
private DefinitionArgument(Kind kind) {
49+
this.kind = kind;
50+
}
51+
52+
public static DefinitionArgument type(ColDataType dataType) {
53+
return type(dataType, false, false);
54+
}
55+
56+
/**
57+
* A type argument.
58+
*
59+
* @param dataType the type or name
60+
* @param setOf whether {@code SETOF} precedes the type
61+
* @param typeReference whether {@code %TYPE} follows the name of a column
62+
* @return the argument
63+
*/
64+
public static DefinitionArgument type(ColDataType dataType, boolean setOf,
65+
boolean typeReference) {
66+
DefinitionArgument argument = new DefinitionArgument(Kind.TYPE);
67+
argument.dataType = Objects.requireNonNull(dataType);
68+
argument.setOf = setOf;
69+
argument.typeReference = typeReference;
70+
return argument;
71+
}
72+
73+
public static DefinitionArgument keyword(String keyword) {
74+
DefinitionArgument argument = new DefinitionArgument(Kind.KEYWORD);
75+
argument.keyword = Objects.requireNonNull(keyword);
76+
return argument;
77+
}
78+
79+
/**
80+
* An operator argument.
81+
*
82+
* @param operator the operator, with its schema when written as {@code OPERATOR(schema.op)}
83+
* @param operatorKeyword whether the operator is written as {@code OPERATOR(...)}
84+
* @return the argument
85+
*/
86+
public static DefinitionArgument operator(String operator, boolean operatorKeyword) {
87+
DefinitionArgument argument = new DefinitionArgument(Kind.OPERATOR);
88+
argument.operator = Objects.requireNonNull(operator);
89+
argument.operatorKeyword = operatorKeyword;
90+
return argument;
91+
}
92+
93+
public static DefinitionArgument number(Expression number) {
94+
DefinitionArgument argument = new DefinitionArgument(Kind.NUMBER);
95+
argument.number = Objects.requireNonNull(number);
96+
return argument;
97+
}
98+
99+
public static DefinitionArgument string(StringValue string) {
100+
DefinitionArgument argument = new DefinitionArgument(Kind.STRING);
101+
argument.string = Objects.requireNonNull(string);
102+
return argument;
103+
}
104+
105+
public static DefinitionArgument none() {
106+
return new DefinitionArgument(Kind.NONE);
107+
}
108+
109+
public Kind getKind() {
110+
return kind;
111+
}
112+
113+
/**
114+
* The type of a {@link Kind#TYPE} argument. A plain name, such as the function of
115+
* {@code INPUT = box3_in}, is the type's {@link ColDataType#getDataType() name}.
116+
*
117+
* @return the type, or {@code null} for another kind
118+
*/
119+
public ColDataType getDataType() {
120+
return dataType;
121+
}
122+
123+
public boolean isSetOf() {
124+
return setOf;
125+
}
126+
127+
/**
128+
* Whether {@code %TYPE} follows the name, which then names a column.
129+
*
130+
* @return {@code true} for {@code table.column%TYPE}
131+
*/
132+
public boolean isTypeReference() {
133+
return typeReference;
134+
}
135+
136+
public String getKeyword() {
137+
return keyword;
138+
}
139+
140+
public String getOperator() {
141+
return operator;
142+
}
143+
144+
public boolean isOperatorKeyword() {
145+
return operatorKeyword;
146+
}
147+
148+
/**
149+
* The number of a {@link Kind#NUMBER} argument: a long or double value, inside a signed
150+
* expression when a sign is written.
151+
*
152+
* @return the number, or {@code null} for another kind
153+
*/
154+
public Expression getNumber() {
155+
return number;
156+
}
157+
158+
public StringValue getString() {
159+
return string;
160+
}
161+
162+
@Override
163+
public String toString() {
164+
switch (kind) {
165+
case TYPE:
166+
return (setOf ? "SETOF " : "") + dataType + (typeReference ? "%TYPE" : "");
167+
case KEYWORD:
168+
return keyword;
169+
case OPERATOR:
170+
return operatorKeyword ? "OPERATOR(" + operator + ")" : operator;
171+
case NUMBER:
172+
return number.toString();
173+
case STRING:
174+
return string.toString();
175+
default:
176+
return "NONE";
177+
}
178+
}
179+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement;
11+
12+
import java.io.Serializable;
13+
import java.util.List;
14+
import java.util.stream.Collectors;
15+
16+
/**
17+
* One element of a PostgreSQL definition list, {@code name [= value]}, as in
18+
* {@code CREATE TYPE name (INPUT = fn, PASSEDBYVALUE)} or
19+
* {@code ALTER TYPE name SET (SEND = NONE)}. The name keeps its spelling and quoting.
20+
*/
21+
public class DefinitionElement implements Serializable {
22+
private String name;
23+
private DefinitionArgument argument;
24+
25+
public DefinitionElement() {
26+
// empty constructor
27+
}
28+
29+
public DefinitionElement(String name, DefinitionArgument argument) {
30+
this.name = name;
31+
this.argument = argument;
32+
}
33+
34+
public String getName() {
35+
return name;
36+
}
37+
38+
public void setName(String name) {
39+
this.name = name;
40+
}
41+
42+
/**
43+
* The value after the equals sign.
44+
*
45+
* @return the argument, or {@code null} for an element without a value
46+
*/
47+
public DefinitionArgument getArgument() {
48+
return argument;
49+
}
50+
51+
public void setArgument(DefinitionArgument argument) {
52+
this.argument = argument;
53+
}
54+
55+
/**
56+
* Renders a definition list in parentheses.
57+
*
58+
* @param elements the elements
59+
* @return the list, such as {@code (INPUT = fn, PASSEDBYVALUE)}
60+
*/
61+
public static String toString(List<DefinitionElement> elements) {
62+
return elements.stream().map(Object::toString).collect(Collectors.joining(", ", "(", ")"));
63+
}
64+
65+
@Override
66+
public String toString() {
67+
return argument == null ? name : name + " = " + argument;
68+
}
69+
}

‎src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import net.sf.jsqlparser.statement.alter.AlterPolicy;
3737
import net.sf.jsqlparser.statement.alter.AlterPublication;
3838
import net.sf.jsqlparser.statement.alter.AlterRelation;
39+
import net.sf.jsqlparser.statement.alter.AlterRule;
3940
import net.sf.jsqlparser.statement.alter.AlterServer;
4041
import net.sf.jsqlparser.statement.alter.AlterSession;
4142
import net.sf.jsqlparser.statement.alter.AlterStatistics;
@@ -1489,4 +1490,9 @@ public <S> Void visit(CreateCollation statement, S context) {
14891490
return schemaOnly();
14901491
}
14911492

1493+
1494+
@Override
1495+
public <S> Void visit(AlterRule statement, S context) {
1496+
return schemaOnly();
1497+
}
14921498
}

‎src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import net.sf.jsqlparser.statement.notify.NotifyStatement;
2525
import net.sf.jsqlparser.statement.create.collation.CreateCollation;
2626
import net.sf.jsqlparser.statement.alter.AlterCollation;
27+
import net.sf.jsqlparser.statement.alter.AlterRule;
2728
import net.sf.jsqlparser.statement.alter.AlterRoutine;
2829
import net.sf.jsqlparser.statement.alter.AlterTrigger;
2930
import net.sf.jsqlparser.statement.alter.AlterPolicy;
@@ -1119,4 +1120,12 @@ default <S> T visit(AlterTrigger statement, S context) {
11191120
default void visit(AlterTrigger statement) {
11201121
visit(statement, null);
11211122
}
1123+
1124+
default <S> T visit(AlterRule statement, S context) {
1125+
return null;
1126+
}
1127+
1128+
default void visit(AlterRule statement) {
1129+
visit(statement, null);
1130+
}
11221131
}

‎src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package net.sf.jsqlparser.statement;
1111

1212
import net.sf.jsqlparser.statement.alter.AlterRelation;
13+
import net.sf.jsqlparser.statement.alter.AlterRule;
1314
import net.sf.jsqlparser.statement.alter.AlterTablespace;
1415
import net.sf.jsqlparser.statement.alter.AlterPolicy;
1516
import net.sf.jsqlparser.statement.alter.AlterRoutine;
@@ -1193,4 +1194,10 @@ public <S> T visit(AlterTrigger statement, S context) {
11931194
return null;
11941195
}
11951196

1197+
1198+
@Override
1199+
public <S> T visit(AlterRule statement, S context) {
1200+
fromItemVisitor.visitFromItem(statement.getTable(), context);
1201+
return null;
1202+
}
11961203
}

0 commit comments

Comments
 (0)