Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import net.sf.jsqlparser.statement.ReferentialAction;
import net.sf.jsqlparser.statement.ReferentialAction.Action;
import net.sf.jsqlparser.statement.ReferentialAction.Type;
import net.sf.jsqlparser.statement.select.PlainSelect;

/** The target and actions of a column- or table-level foreign-key reference. */
public class ForeignKeyReference implements Serializable {
Expand All @@ -33,6 +32,7 @@ public enum MatchType {
private Table table;
private List<String> referencedColumnNames;
private MatchType matchType;
private boolean usingPeriod;
private ConstraintAttributes constraintAttributes;
private final Set<ReferentialAction> referentialActions = new LinkedHashSet<>(2);

Expand All @@ -47,6 +47,20 @@ public void setConstraintAttributes(ConstraintAttributes constraintAttributes) {
this.constraintAttributes = constraintAttributes;
}

/** Whether the final explicitly referenced column is prefixed with PERIOD. */
public boolean isUsingPeriod() {
return usingPeriod;
}

public void setUsingPeriod(boolean usingPeriod) {
this.usingPeriod = usingPeriod;
}

public ForeignKeyReference withUsingPeriod(boolean usingPeriod) {
setUsingPeriod(usingPeriod);
return this;
}

public Table getTable() {
return table;
}
Expand Down Expand Up @@ -136,7 +150,17 @@ public ForeignKeyReference addReferencedColumnNames(
public String toString() {
StringBuilder builder = new StringBuilder("REFERENCES ").append(table);
if (referencedColumnNames != null) {
builder.append(PlainSelect.getStringList(referencedColumnNames, true, true));
builder.append('(');
for (int i = 0; i < referencedColumnNames.size(); i++) {
if (i > 0) {
builder.append(", ");
}
if (usingPeriod && i == referencedColumnNames.size() - 1) {
builder.append("PERIOD ");
}
builder.append(referencedColumnNames.get(i));
}
builder.append(')');
}
if (matchType != null) {
builder.append(" MATCH ").append(matchType);
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/net/sf/jsqlparser/statement/create/table/Index.java
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ public enum NullOrdering {
private SortOrder sortOrder;
private NullOrdering nullOrdering;
private ExclusionOperator exclusionOperator;
private boolean withoutOverlaps;
private boolean period;

public String getExclusionOperator() {
return exclusionOperator == null ? null : exclusionOperator.toString();
Expand Down Expand Up @@ -466,6 +468,34 @@ public ColumnParams(Expression expression, List<String> params) {
this.expression = expression;
}

/** Marks the final key of a PostgreSQL temporal PRIMARY KEY or UNIQUE constraint. */
public boolean isWithoutOverlaps() {
return withoutOverlaps;
}

public void setWithoutOverlaps(boolean withoutOverlaps) {
this.withoutOverlaps = withoutOverlaps;
}

public ColumnParams withWithoutOverlaps(boolean withoutOverlaps) {
setWithoutOverlaps(withoutOverlaps);
return this;
}

/** Marks the final referencing column of a temporal foreign key. */
public boolean isPeriod() {
return period;
}

public void setPeriod(boolean period) {
this.period = period;
}

public ColumnParams withPeriod(boolean period) {
setPeriod(period);
return this;
}

public String getColumnName() {
return expression != null ? expression.toString() : columnName;
}
Expand Down Expand Up @@ -569,6 +599,9 @@ public String toString() {

/** Renders expression keys through the caller's expression printer. */
public void appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
if (period) {
builder.append("PERIOD ");
}
if (expression != null) {
if (expressionParenthesized) {
builder.append('(');
Expand All @@ -588,6 +621,9 @@ public void appendTo(StringBuilder builder, Consumer<Expression> expressionPrint
if (exclusionOperator != null) {
builder.append(" WITH ").append(exclusionOperator);
}
if (withoutOverlaps) {
builder.append(" WITHOUT OVERLAPS");
}
}

private void appendParams(StringBuilder builder) {
Expand Down
66 changes: 57 additions & 9 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -13835,7 +13835,7 @@ Index.ColumnParams IndexColumnWithParams(): {
|| (Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect))
&& getToken(1).kind != K_ASC && getToken(1).kind != K_DESC
&& getToken(1).kind != K_NULLS && getToken(1).kind != K_COLLATE
&& getToken(1).kind != K_WITH) })
&& getToken(1).kind != K_WITH && getToken(1).kind != K_WITHOUT) })
operatorClass=IndexKeyAttributeName()
[ LOOKAHEAD(2) operatorClassParameters=PostgreSqlIndexOptions() ]
{
Expand All @@ -13852,7 +13852,7 @@ Index.ColumnParams IndexColumnWithParams(): {
{ if (!postgres) { columnParams.add(sortOrder.image); } } ]
[ LOOKAHEAD(2) <K_NULLS> (nullOrdering=<K_FIRST> | nullOrdering=<K_LAST>)
{ if (!postgres) { columnParams.add("NULLS"); columnParams.add(nullOrdering.image); } } ]
( LOOKAHEAD(2, { !Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect)) && getToken(1).kind != K_WITH }) parameter=CreateParameter() { columnParams.addAll(parameter); } )*
( LOOKAHEAD(2, { !Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect)) && getToken(1).kind != K_WITH && getToken(1).kind != K_WITHOUT }) parameter=CreateParameter() { columnParams.addAll(parameter); } )*
{
column = expression != null
? new Index.ColumnParams(expression,
Expand Down Expand Up @@ -13913,7 +13913,11 @@ List<Index.Option> PostgreSqlIndexOptions():
{ return options; }
}

List<Index.ColumnParams> IndexColumnsWithParamsList() : {
List<Index.ColumnParams> IndexColumnsWithParamsList():
{ List<Index.ColumnParams> columns; }
{ columns=IndexKeyColumns(false) { return columns; } }

List<Index.ColumnParams> IndexKeyColumns(boolean temporal) : {
List<Index.ColumnParams> colNames = new ArrayList<Index.ColumnParams>();
Index.ColumnParams column = null;
}
Expand All @@ -13932,6 +13936,10 @@ List<Index.ColumnParams> IndexColumnsWithParamsList() : {
}
)*

[ <K_WITHOUT> <K_OVERLAPS> {
requireDdlSyntax(temporal, "WITHOUT OVERLAPS requires a PRIMARY KEY or UNIQUE constraint");
column.setWithoutOverlaps(true);
} ]
")"

{ return colNames; }
Expand Down Expand Up @@ -14709,7 +14717,7 @@ Index TableIndexSpec(boolean createContext):
typeToken=<K_PRIMARY> keywordToken=<K_KEY>
clustering=SqlServerIndexClustering()
[ LOOKAHEAD({ clustering == null && getToken(1).kind != OPENING_BRACKET }) indexName=RelObjectName() ]
columns=IndexColumnsWithParamsList()
columns=IndexKeyColumns(true)
TableIndexOptions(createContext, indexOptions)
{
index = new NamedConstraint()
Expand All @@ -14728,11 +14736,12 @@ Index TableIndexSpec(boolean createContext):
[ LOOKAHEAD({ clustering == null && getToken(1).kind != OPENING_BRACKET
&& getToken(1).kind != K_USING }) indexName=RelObjectName() ]
[ using=UsingIndexType() ]
columns=IndexColumnsWithParamsList()
columns=IndexKeyColumns(true)
TableIndexOptions(createContext, indexOptions)
{
// MySQL ALTER preserves both the constraint symbol and the index name.
if (createContext || Dialect.MYSQL.name().equals(getAsString(Feature.dialect))) {
// PostgreSQL and MySQL ALTER preserve named constraints separately from index names.
if (createContext || Dialect.MYSQL.name().equals(getAsString(Feature.dialect))
|| Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect))) {
index = new NamedConstraint()
.withIndexName(indexName)
.withType(typeToken.image
Expand Down Expand Up @@ -15870,16 +15879,55 @@ void ReferentialActionSpec(ForeignKeyReference reference):
}
}

/** Shares column/period boundaries on both sides of a foreign key. */
List<Index.ColumnParams> ForeignKeyColumns(boolean allowParameters):
{
List<Index.ColumnParams> columns = new ArrayList<Index.ColumnParams>();
Index.ColumnParams column;
}
{
"(" column=ForeignKeyColumn(allowParameters) { columns.add(column); }
( "," {
requireDdlSyntax(!column.isPeriod(), "PERIOD must mark the final foreign key column");
} column=ForeignKeyColumn(allowParameters) { columns.add(column); } )* ")"
{ return columns; }
}

Index.ColumnParams ForeignKeyColumn(boolean allowParameters):
{
boolean period = false;
String name;
List<String> parameters = null;
}
{
[ LOOKAHEAD({ isKeywordAhead("PERIOD") && getToken(2).kind != K_COMMA
&& getToken(2).kind != CLOSING_BRACKET })
ContextualKeyword("PERIOD") { period = true; } ]
name=RelObjectName()
[ parameters=CreateParameter() {
requireDdlSyntax(allowParameters && !period, "Expected a foreign key column name");
} ]
{ return new Index.ColumnParams(name, parameters).withPeriod(period); }
}

ForeignKeyReference ForeignKeyReferenceSpec(boolean columnContext):
{
ForeignKeyReference reference = new ForeignKeyReference();
ForeignKeyReference.MatchType matchType;
Token matchToken;
List<String> refColNames = null;
List<Index.ColumnParams> referencedColumns;
Table fkTable;
}
{
<K_REFERENCES> fkTable=Table() [ LOOKAHEAD(2) refColNames=ColumnsNamesList() ]
<K_REFERENCES> fkTable=Table()
[ LOOKAHEAD(2) referencedColumns=ForeignKeyColumns(false) {
refColNames = new ArrayList<String>();
for (Index.ColumnParams column : referencedColumns) {
refColNames.add(column.getColumnName());
}
reference.setUsingPeriod(referencedColumns.get(referencedColumns.size() - 1).isPeriod());
} ]
{
reference.setTable(fkTable);
reference.setReferencedColumnNames(refColNames);
Expand Down Expand Up @@ -15948,7 +15996,7 @@ ForeignKeyIndex ForeignKeySpec(String constraintName):
{
tk=<K_FOREIGN> tk2=<K_KEY>
[ LOOKAHEAD(2) indexName=RelObjectName() { fkIndex.setIndexName(indexName); } ]
colNames = ColumnNamesWithParamsList()
colNames = ForeignKeyColumns(true)
{
if (constraintName != null) { fkIndex.setName(constraintName); }
fkIndex.withType(tk.image + " " + tk2.image).withColumns(colNames);
Expand Down
Loading
Loading