Skip to content

Commit 82bed50

Browse files
authored
fix: preserve PostgreSQL identity sequence names (#2687)
Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 0f58f7f commit 82bed50

3 files changed

Lines changed: 109 additions & 4 deletions

File tree

‎src/main/java/net/sf/jsqlparser/schema/Sequence.java‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public Sequence addParameters(Collection<? extends Parameter> parameters) {
187187
* The available parameters to a sequence
188188
*/
189189
public enum ParameterType {
190-
INCREMENT_BY, INCREMENT, START_WITH, START, RESTART_WITH, MAXVALUE, NOMAXVALUE, MINVALUE, NOMINVALUE, CYCLE, NOCYCLE, CACHE, NOCACHE, ORDER, NOORDER, KEEP, NOKEEP, SESSION, GLOBAL, NO_MINVALUE, NO_MAXVALUE, NO_CYCLE;
190+
INCREMENT_BY, INCREMENT, START_WITH, START, RESTART_WITH, MAXVALUE, NOMAXVALUE, MINVALUE, NOMINVALUE, CYCLE, NOCYCLE, CACHE, NOCACHE, ORDER, NOORDER, KEEP, NOKEEP, SESSION, GLOBAL, NO_MINVALUE, NO_MAXVALUE, NO_CYCLE, SEQUENCE_NAME;
191191

192192
public static ParameterType from(String type) {
193193
return Enum.valueOf(ParameterType.class, type.toUpperCase(Locale.ROOT));
@@ -201,6 +201,7 @@ public static class Parameter implements java.io.Serializable {
201201

202202
private final ParameterType option;
203203
private Long value;
204+
private Sequence sequenceName;
204205

205206
public Parameter(ParameterType option) {
206207
this.option = option;
@@ -214,6 +215,20 @@ public ParameterType getOption() {
214215
return option;
215216
}
216217

218+
/** The sequence named by an identity declaration's SEQUENCE NAME option. */
219+
public Sequence getSequenceName() {
220+
return sequenceName;
221+
}
222+
223+
public void setSequenceName(Sequence sequenceName) {
224+
this.sequenceName = sequenceName;
225+
}
226+
227+
public Parameter withSequenceName(Sequence sequenceName) {
228+
setSequenceName(sequenceName);
229+
return this;
230+
}
231+
217232
@Override
218233
public String toString() {
219234
return formatParameter();
@@ -225,6 +240,8 @@ public void setValue(Long value) {
225240

226241
public String formatParameter() {
227242
switch (option) {
243+
case SEQUENCE_NAME:
244+
return "SEQUENCE NAME " + sequenceName.getFullyQualifiedName();
228245
case INCREMENT_BY:
229246
return prefix("INCREMENT BY");
230247
case INCREMENT:

‎src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14217,7 +14217,9 @@ IdentityDefinition IdentityDefinition():
1421714217
Token token;
1421814218
IdentityDefinition.GenerationMode mode;
1421914219
IdentityDefinition identity;
14220-
List<Sequence.Parameter> parameters;
14220+
List<Sequence.Parameter> parameters = new ArrayList<Sequence.Parameter>();
14221+
Sequence.Parameter parameter;
14222+
Sequence sequenceName;
1422114223
}
1422214224
{
1422314225
token=<S_IDENTIFIER> {
@@ -14226,8 +14228,18 @@ IdentityDefinition IdentityDefinition():
1422614228
( <K_ALWAYS> { mode = IdentityDefinition.GenerationMode.ALWAYS; }
1422714229
| <K_BY> <K_DEFAULT> { mode = IdentityDefinition.GenerationMode.BY_DEFAULT; } )
1422814230
<K_AS> <K_IDENTITY> { identity = new IdentityDefinition(mode); }
14229-
[ LOOKAHEAD(2) "(" parameters=SequenceParameters() ")" {
14230-
requireDdlSyntax(!parameters.isEmpty(), "Expected an identity sequence parameter");
14231+
[ LOOKAHEAD(1) "("
14232+
(
14233+
(
14234+
LOOKAHEAD(<K_SEQUENCE> <K_NAME>)
14235+
<K_SEQUENCE> <K_NAME> sequenceName=Sequence()
14236+
{ parameter = new Sequence.Parameter(Sequence.ParameterType.SEQUENCE_NAME)
14237+
.withSequenceName(sequenceName); }
14238+
| parameter=SequenceParameter()
14239+
)
14240+
{ parameters.add(parameter); }
14241+
)+
14242+
")" {
1423114243
identity.setParameters(parameters);
1423214244
} ]
1423314245
{ return identity; }
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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.create;
11+
12+
import static org.junit.jupiter.api.Assertions.*;
13+
import net.sf.jsqlparser.JSQLParserException;
14+
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
15+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
16+
import net.sf.jsqlparser.schema.Sequence;
17+
import net.sf.jsqlparser.statement.Statement;
18+
import net.sf.jsqlparser.statement.alter.Alter;
19+
import net.sf.jsqlparser.statement.create.table.CreateTable;
20+
import net.sf.jsqlparser.statement.create.table.IdentityDefinition;
21+
import net.sf.jsqlparser.util.deparser.StatementDeParser;
22+
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.params.ParameterizedTest;
24+
import org.junit.jupiter.params.provider.ValueSource;
25+
26+
class IdentitySequenceNameTest {
27+
@ParameterizedTest
28+
@ValueSource(strings = {"ids", "public.ids", "\"schema.name\".\"seq.name\""})
29+
void sharesIdentityOptionsAcrossCreateAndAlter(String name) throws JSQLParserException {
30+
String definition = "GENERATED BY DEFAULT AS IDENTITY (START 4 SEQUENCE NAME " + name
31+
+ " CACHE 2 NO CYCLE)";
32+
CreateTable create = (CreateTable) parse("CREATE TABLE t (id INT " + definition + ")");
33+
IdentityDefinition identity = create.getColumnDefinitions().get(0).getColumnOptions().get(0)
34+
.getIdentityDefinition();
35+
Alter alter = (Alter) parse("ALTER TABLE t ALTER COLUMN id ADD " + definition);
36+
IdentityDefinition altered = alter.getAlterExpressions().get(0).getColDataTypeList().get(0)
37+
.getIdentityAlterations().get(0).getIdentityDefinition();
38+
for (IdentityDefinition current : new IdentityDefinition[] {identity, altered}) {
39+
assertEquals(4, current.getParameters().size());
40+
Sequence.Parameter named = current.getParameters().get(1);
41+
assertEquals(Sequence.ParameterType.SEQUENCE_NAME, named.getOption());
42+
assertEquals(name, named.getSequenceName().getFullyQualifiedName());
43+
assertNull(named.getValue());
44+
named.getSequenceName().setName("renamed");
45+
}
46+
assertTrue(create.toString().contains("renamed"));
47+
assertTrue(alter.toString().contains("renamed"));
48+
roundTrip(create);
49+
roundTrip(alter);
50+
}
51+
52+
@Test
53+
void preservesNameOnlyAndDoesNotExtendStandaloneSequenceOptions() throws JSQLParserException {
54+
String sql = "CREATE TABLE t (id INT GENERATED ALWAYS AS IDENTITY (SEQUENCE NAME ids))";
55+
assertEquals(sql, parse(sql).toString());
56+
roundTrip(parse(sql));
57+
for (String invalid : new String[] {
58+
"CREATE TABLE t (id INT GENERATED ALWAYS AS IDENTITY (SEQUENCE NAME))",
59+
"CREATE TABLE t (id INT GENERATED ALWAYS AS IDENTITY ())",
60+
"CREATE SEQUENCE ids SEQUENCE NAME other_ids",
61+
"ALTER TABLE t ALTER COLUMN id SET SEQUENCE NAME other_ids"}) {
62+
assertThrows(JSQLParserException.class, () -> parse(invalid), invalid);
63+
}
64+
}
65+
66+
private static Statement parse(String sql) throws JSQLParserException {
67+
return CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.POSTGRESQL));
68+
}
69+
70+
private static void roundTrip(Statement statement) throws JSQLParserException {
71+
StringBuilder out = new StringBuilder();
72+
statement.accept(new StatementDeParser(out));
73+
assertEquals(statement.toString(), out.toString());
74+
assertEquals(out.toString(), parse(out.toString()).toString());
75+
}
76+
}

0 commit comments

Comments
 (0)