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
@@ -0,0 +1,57 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.alter;

import java.util.Objects;
import net.sf.jsqlparser.statement.create.table.ConstraintAttributes;

/** Changes an existing constraint using the attributes shared with CREATE and ADD. */
public class AlterConstraintAttributes extends AlterExpression {
private ConstraintAttributes attributes = new ConstraintAttributes();

public AlterConstraintAttributes() {
setOperation(AlterOperation.ALTER);
setConstraintType("CONSTRAINT");
}

public ConstraintAttributes getAttributes() {
return attributes;
}

public void setAttributes(ConstraintAttributes attributes) {
this.attributes = Objects.requireNonNull(attributes, "attributes");
}

@Override
public String getConstraintName() {
return getConstraintSymbol();
}

@Override
public void setConstraintName(String name) {
setConstraintSymbol(name);
}

@Override
public boolean isEnforced() {
return Boolean.TRUE.equals(attributes.getEnforced());
}

@Override
public void setEnforced(boolean enforced) {
attributes.setEnforced(enforced);
}

@Override
protected void appendBody(StringBuilder builder) {
builder.append("ALTER CONSTRAINT ").append(getConstraintSymbol());
attributes.appendTo(builder);
}
}
35 changes: 31 additions & 4 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1543,11 +1543,16 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
}

private boolean isPostgreSqlConstraintAttributeAhead() {
int kind = getToken(1).kind;
int next = getToken(2).kind;
return kind == K_DEFERRABLE || kind == K_ENFORCED || isKeywordAhead("INITIALLY")
return isPostgreSqlConstraintAttributeAhead(1);
}

private boolean isPostgreSqlConstraintAttributeAhead(int offset) {
int kind = getToken(offset).kind;
int next = getToken(offset + 1).kind;
return kind == K_DEFERRABLE || kind == K_ENFORCED
|| "INITIALLY".equalsIgnoreCase(getToken(offset).image)
|| kind == K_NOT && (next == K_DEFERRABLE || next == K_ENFORCED
|| "VALID".equalsIgnoreCase(getToken(2).image));
|| "VALID".equalsIgnoreCase(getToken(offset + 1).image));
}

private boolean isMySqlStatisticsOptionAhead() {
Expand Down Expand Up @@ -18222,6 +18227,24 @@ AlterExpressionPartition AlterExpressionPostgreSqlPartitionOp():
* Dispatcher production for all ALTER TABLE expression types.
* Delegates to focused sub-productions for each operation category.
*/
AlterConstraintAttributes AlterConstraintAttributes():
{
AlterConstraintAttributes action = new AlterConstraintAttributes();
String name;
ConstraintAttributes attributes;
}
{
<K_ALTER> <K_CONSTRAINT> name=RelObjectName()
attributes=PostgreSqlConstraintAttributeList(null)
{
requireDdlSyntax(attributes != null && !attributes.isNotValid(),
"ALTER CONSTRAINT requires attributes and does not accept NOT VALID");
action.setConstraintSymbol(name);
action.setAttributes(attributes);
return action;
}
}

AlterExpression AlterExpression():
{
AlterExpression alterExp = null;
Expand All @@ -18243,6 +18266,9 @@ AlterExpression AlterExpression():
|
LOOKAHEAD({ isMySqlStatisticsOptionAhead() || isKeywordAhead("ROW_FORMAT") })
alterExp=AlterSharedTableOption()
|
LOOKAHEAD({ getToken(1).kind == K_ALTER && getToken(2).kind == K_CONSTRAINT
&& isPostgreSqlConstraintAttributeAhead(4) }) alterExp=AlterConstraintAttributes()
|
LOOKAHEAD({ isPostgreSqlRelationActionAhead() }) alterExp=PostgreSqlRelationAction() {
requireDdlSyntax(((RelationAlterAction) alterExp).getColumnNumber() == null,
Expand Down Expand Up @@ -18423,6 +18449,7 @@ AlterExpression AlterExpression():
i++;
}

requireDdlSyntax(i > 0, "Expected an ALTER TABLE action");
alterExp.setOptionalSpecifier( optionalSpecifier.toString() );
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.alter;

import static org.junit.jupiter.api.Assertions.*;

import java.util.Set;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.create.table.ConstraintAttributes;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.util.TablesNamesFinder;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class PostgreSqlConstraintAttributesChangeTest {
@ParameterizedTest
@ValueSource(strings = {"DEFERRABLE", "NOT DEFERRABLE", "INITIALLY DEFERRED",
"INITIALLY IMMEDIATE", "DEFERRABLE INITIALLY DEFERRED",
"INITIALLY DEFERRED DEFERRABLE", "ENFORCED", "NOT ENFORCED",
"DEFERRABLE INITIALLY DEFERRED NOT ENFORCED",
"NOT ENFORCED INITIALLY IMMEDIATE NOT DEFERRABLE"})
void sharesCreateAddAndAlterAttributes(String attributes) throws JSQLParserException {
String sql = "ALTER TABLE t ALTER CONSTRAINT fk " + attributes + ", ADD COLUMN z INT";
Alter table = (Alter) parse(sql);
AlterConstraintAttributes change = assertInstanceOf(AlterConstraintAttributes.class,
table.getAlterExpressions().get(0));
assertEquals(AlterOperation.ALTER, change.getOperation());
assertEquals("CONSTRAINT", change.getConstraintType());
assertEquals("fk", change.getConstraintSymbol());
assertEquals("fk", change.getConstraintName());
String declaration = "CONSTRAINT fk FOREIGN KEY(id) REFERENCES p(id) " + attributes;
CreateTable create = (CreateTable) parse("CREATE TABLE t(id INT, "
+ declaration + ")");
Alter add = (Alter) parse("ALTER TABLE t ADD " + declaration);
assertEquals(create.getIndexes().get(0).getConstraintAttributes().toString(),
change.getAttributes().toString());
assertEquals(
add.getAlterExpressions().get(0).getIndex().getConstraintAttributes().toString(),
change.getAttributes().toString());
assertEquals(2, table.getAlterExpressions().size());
roundTrip(table);
assertEquals(2, CCJSqlParserUtil.parseStatements(sql + "; SELECT 1").size());
}

@Test
void legacyAndStructuredMutationsStayConsistent() throws JSQLParserException {
Alter table = (Alter) parse("ALTER TABLE t ALTER CONSTRAINT fk ENFORCED");
AlterConstraintAttributes action =
(AlterConstraintAttributes) table.getAlterExpressions().get(0);
action.setConstraintName("\"new fk\"");
assertEquals("\"new fk\"", action.getConstraintSymbol());
action.setEnforced(false);
assertEquals(Boolean.FALSE, action.getAttributes().getEnforced());
action.getAttributes().setDeferrable(true);
action.getAttributes().setInitially(ConstraintAttributes.Initially.DEFERRED);
assertEquals(
"ALTER TABLE t ALTER CONSTRAINT \"new fk\" DEFERRABLE INITIALLY DEFERRED NOT ENFORCED",
table.toString());
action.getAttributes().setEnforced(true);
assertTrue(action.isEnforced());
assertEquals(Set.of("t"), new TablesNamesFinder().getTables(table));
roundTrip(table);
}

@ParameterizedTest
@ValueSource(strings = {"NOT VALID", "DEFERRABLE NOT VALID", "INITIALLY UNKNOWN",
"DEFERRABLE NOT DEFERRABLE", "ENFORCED NOT ENFORCED",
"INITIALLY DEFERRED INITIALLY IMMEDIATE", "DEFERRABLE,"})
void rejectsWrongOrDuplicateAlterAttributes(String attributes) {
assertThrows(JSQLParserException.class,
() -> parse("ALTER TABLE t ALTER CONSTRAINT fk " + attributes));
}

private static net.sf.jsqlparser.statement.Statement parse(String sql)
throws JSQLParserException {
return CCJSqlParserUtil.parse(sql,
parser -> parser.withDialect(
net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect.POSTGRESQL));
}

private static void roundTrip(Alter table) throws JSQLParserException {
StringBuilder output = new StringBuilder();
table.accept(new StatementDeParser(output));
assertEquals(table.toString(), output.toString());
assertEquals(output.toString(), parse(output.toString()).toString());
}
}
Loading