Skip to content

Commit dba1eba

Browse files
authored
feat: model PostgreSQL table inheritance references (#2692)
Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent b47e6af commit dba1eba

5 files changed

Lines changed: 113 additions & 0 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class CreateTable implements Statement {
3838
private DuplicateHandling duplicateHandling;
3939
private Table likeTable;
4040
private Table cloneTable;
41+
private List<Table> inherits;
4142
private ColDataType ofType;
4243
private boolean selectParenthesis;
4344
private boolean useAsKeyword = true;
@@ -347,6 +348,27 @@ public void setPartitionBound(PartitionBound partitionBound) {
347348
this.partitionBound = partitionBound;
348349
}
349350

351+
/** PostgreSQL parent tables, in declaration order; null if INHERITS is absent. */
352+
public List<Table> getInherits() {
353+
return inherits;
354+
}
355+
356+
public void setInherits(List<Table> inherits) {
357+
this.inherits = inherits;
358+
}
359+
360+
public CreateTable withInherits(List<Table> inherits) {
361+
setInherits(inherits);
362+
return this;
363+
}
364+
365+
/** Shared rendering of the structured parent references. */
366+
public void appendInheritanceTo(StringBuilder builder) {
367+
if (inherits != null) {
368+
builder.append(" INHERITS ").append(PlainSelect.getStringList(inherits, true, true));
369+
}
370+
}
371+
350372
public boolean isSelectParenthesis() {
351373
return selectParenthesis;
352374
}
@@ -369,6 +391,7 @@ public String toString() {
369391
StringBuilder b = new StringBuilder();
370392
appendCreateClause(b);
371393
appendColumnDefinitions(b);
394+
appendInheritanceTo(b);
372395
if (partitionBound != null) {
373396
b.append(" ").append(partitionBound);
374397
}

‎src/main/java/net/sf/jsqlparser/util/TableDefinitionTraversal.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ public static void visit(CreateTable table, Consumer<Expression> expressions,
109109
}
110110
});
111111
}
112+
if (table.getInherits() != null) {
113+
table.getInherits().forEach(parent -> accept(parent, tables));
114+
}
112115
accept(table.getTrailingLikeTable(), tables);
113116
accept(table.getPartitionOf(), tables);
114117
visit(table.getPartitioning(), expressions);

‎src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public void deParse(CreateTable createTable) {
106106
builder.append(")");
107107
}
108108

109+
createTable.appendInheritanceTo(builder);
109110
if (createTable.getPartitionBound() != null) {
110111
builder.append(' ');
111112
createTable.getPartitionBound().appendTo(builder,

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15054,6 +15054,8 @@ CreateTable CreateTable(boolean isUsingOrReplace):
1505415054
Table partitionOfTable = null;
1505515055
PartitionBound partitionBound = null;
1505615056
ColDataType ofType = null;
15057+
Table parent;
15058+
List<Table> parents = new ArrayList<Table>();
1505715059
boolean withData;
1505815060
}
1505915061
{
@@ -15078,6 +15080,8 @@ CreateTable CreateTable(boolean isUsingOrReplace):
1507815080
[ LOOKAHEAD(2) <K_PARTITION> <K_OF> partitionOfTable=Table()
1507915081
{ createTable.setPartitionOf(partitionOfTable); } ]
1508015082
[ LOOKAHEAD(2) (
15083+
LOOKAHEAD("(" ")") "(" ")" { createTable.setTableElements(tableElements); }
15084+
|
1508115085
LOOKAHEAD(3) (
1508215086
"(" tableColumn=RelObjectName() { columns.add(tableColumn); } ("," tableColumn=RelObjectName() { columns.add(tableColumn); } )* ")"
1508315087
)
@@ -15094,6 +15098,10 @@ CreateTable CreateTable(boolean isUsingOrReplace):
1509415098
)
1509515099
)
1509615100
]
15101+
[ LOOKAHEAD({ isKeywordAhead("INHERITS") }) ContextualKeyword("INHERITS")
15102+
"(" parent=Table() { parents.add(parent); }
15103+
( "," parent=Table() { parents.add(parent); } )* ")"
15104+
{ createTable.setInherits(parents); } ]
1509715105
[ LOOKAHEAD({ partitionOfTable != null })
1509815106
partitionBound=PostgreSqlPartitionBound()
1509915107
{ createTable.setPartitionBound(partitionBound); } ]
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2019 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 java.util.ArrayList;
14+
import java.util.List;
15+
import net.sf.jsqlparser.JSQLParserException;
16+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
17+
import net.sf.jsqlparser.schema.Table;
18+
import net.sf.jsqlparser.statement.create.table.CreateTable;
19+
import net.sf.jsqlparser.util.TableDefinitionTraversal;
20+
import net.sf.jsqlparser.util.TablesNamesFinder;
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 PostgreSqlTableInheritanceTest {
27+
@ParameterizedTest
28+
@ValueSource(strings = {"parent", "public.parent", "public.parent, other.parent2",
29+
"\"Odd.Schema\".\"Parent.Name\""})
30+
void modelsAndVisitsParentTables(String parents) throws JSQLParserException {
31+
for (String definition : new String[] {"()", "(extra INT)", "(LIKE source, extra INT)"}) {
32+
CreateTable table = (CreateTable) CCJSqlParserUtil.parse(
33+
"CREATE TABLE child " + definition + " INHERITS (" + parents
34+
+ ") WITH (fillfactor = 80)");
35+
List<Table> inherited = table.getInherits();
36+
assertEquals(parents.split(", ").length, inherited.size());
37+
assertEquals(parents.split(", ")[0], inherited.get(0).getFullyQualifiedName());
38+
List<Table> visited = new ArrayList<>();
39+
TableDefinitionTraversal.visit(table, e -> {
40+
}, visited::add);
41+
for (Table parent : inherited) {
42+
assertTrue(visited.stream().anyMatch(t -> t == parent));
43+
assertTrue(new TablesNamesFinder().getTables(table)
44+
.contains(parent.getFullyQualifiedName()));
45+
}
46+
roundTrip(table);
47+
inherited.set(0, new Table("replacement"));
48+
assertTrue(table.toString().contains("INHERITS (replacement"));
49+
roundTrip(table);
50+
}
51+
}
52+
53+
@Test
54+
void preservesEmptyDefinitionsAndCanRemoveInheritance() throws JSQLParserException {
55+
CreateTable table =
56+
(CreateTable) CCJSqlParserUtil.parse("CREATE TABLE child () INHERITS (parent)");
57+
assertNotNull(table.getTableElements());
58+
assertTrue(table.getTableElements().isEmpty());
59+
table.setInherits(null);
60+
assertEquals("CREATE TABLE child ()", table.toString());
61+
roundTrip(table);
62+
assertEquals(2, CCJSqlParserUtil.parseStatements(table + "; SELECT 1").size());
63+
}
64+
65+
@ParameterizedTest
66+
@ValueSource(strings = {"()", "(parent,)", "(parent AS p)", "(parent + 1)"})
67+
void rejectsMalformedParentLists(String parents) {
68+
assertThrows(JSQLParserException.class,
69+
() -> CCJSqlParserUtil.parse("CREATE TABLE child () INHERITS " + parents));
70+
}
71+
72+
private static void roundTrip(CreateTable table) throws JSQLParserException {
73+
StringBuilder out = new StringBuilder();
74+
table.accept(new StatementDeParser(out));
75+
assertEquals(table.toString(), out.toString());
76+
assertEquals(out.toString(), CCJSqlParserUtil.parse(out.toString()).toString());
77+
}
78+
}

0 commit comments

Comments
 (0)