Skip to content
Open
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 @@ -24,6 +24,8 @@ public class CheckConstraint extends NamedConstraint {

private Boolean enforced;

private boolean noInherit;

public CheckConstraint() {
setKind(Kind.CHECK);
}
Expand All @@ -44,6 +46,20 @@ public void setExpression(Expression expression) {
this.expression = expression;
}

/** Whether PostgreSQL should keep this CHECK from being inherited by child tables. */
public boolean isNoInherit() {
return noInherit;
}

public void setNoInherit(boolean noInherit) {
this.noInherit = noInherit;
}

public CheckConstraint withNoInherit(boolean noInherit) {
setNoInherit(noInherit);
return this;
}

public Boolean getEnforced() {
return enforced;
}
Expand All @@ -62,6 +78,9 @@ public void appendTo(StringBuilder b, Consumer<Expression> expressionPrinter) {
expressionPrinter.accept(expression);
}
b.append(')');
if (noInherit) {
b.append(" NO INHERIT");
}
if (enforced != null) {
b.append(enforced ? " ENFORCED" : " NOT ENFORCED");
}
Expand Down
13 changes: 12 additions & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -14132,6 +14132,7 @@ ColumnOption ColumnDefinitionOption(): {
ColumnOption option;
IdentityDefinition identity;
NamedConstraint constraint;
String constraintName = null;
Expression defaultExpression;
GeneratedColumnDefinition generated;
ObjectNames collationNames;
Expand Down Expand Up @@ -14171,6 +14172,12 @@ ColumnOption ColumnDefinitionOption(): {
LOOKAHEAD(<K_PRIMARY> <K_KEY>) <K_PRIMARY> <K_KEY>
{ option = ColumnOption.constraint(new NamedConstraint().withType("PRIMARY KEY")); }
|
LOOKAHEAD([ <K_CONSTRAINT> RelObjectName() ] <K_CHECK>,
{ Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect)) })
[ <K_CONSTRAINT> constraintName=RelObjectName() ]
constraint=CheckConstraintSpec(constraintName)
{ option = ColumnOption.constraint(constraint); }
|
LOOKAHEAD(<K_UNIQUE>) constraint=ColumnUniqueConstraint()
{ option = ColumnOption.constraint(constraint); }
|
Expand Down Expand Up @@ -15759,17 +15766,21 @@ CheckConstraint CheckConstraintSpec(String constraintName):
{
Expression exp = null;
Boolean enforced = null;
boolean noInherit = false;
CheckConstraint checkConstraint;
}
{
<K_CHECK> "(" exp = Expression() ")"
[ LOOKAHEAD({ Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect))
&& getToken(1).kind == K_NO && "INHERIT".equalsIgnoreCase(getToken(2).image) })
<K_NO> TypeDdlKeyword("INHERIT") { noInherit = true; } ]
[ LOOKAHEAD(2)
[ <K_NOT> { enforced = false; } ]
<K_ENFORCED> { if (enforced == null) { enforced = true; } }
]
{
checkConstraint = new CheckConstraint().withName(constraintName).withExpression(exp)
.withEnforced(enforced);
.withEnforced(enforced).withNoInherit(noInherit);
checkConstraint.setKind(Index.Kind.CHECK);
return checkConstraint;
}
Expand Down
13 changes: 13 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,19 @@ The fastest way to learn the object model is to look at it. Paste your SQL into
Read that as a map: each line is a getter away. ``select.getSelectItems()``, ``select.getFromItem()``, ``select.getWhere()``. Once the tree gets deeper than a couple of levels, stop casting by hand and use :ref:`Use the Visitor Patterns`.


PostgreSQL CHECK inheritance
----------------------------

With ``Dialect.POSTGRESQL``, CREATE and ALTER CHECK constraints support
``NO INHERIT``. ``CheckConstraint.isNoInherit()`` reports this flag; use
``setNoInherit`` or ``withNoInherit`` to change it. The check expression remains
an editable ``Expression`` visited by the existing table traversal and deparsers.

For ``ALTER TABLE t ADD CHECK (id > 0) NO INHERIT NOT VALID``, the two options
are independent: ``isNoInherit()`` is true and
``getConstraintAttributes().isNotValid()`` is true. Clearing one flag preserves
the other when the statement is rendered.

DROP INDEX owners
-----------------

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*-
* #%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.ArrayList;
import java.util.List;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.create.table.CheckConstraint;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.util.TableDefinitionTraversal;
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 PostgreSqlCheckNoInheritTest {
@ParameterizedTest
@ValueSource(strings = {
"ALTER TABLE t ADD CONSTRAINT ck CHECK (id > 0) NO INHERIT",
"ALTER TABLE t ADD CHECK (id > 0) NO INHERIT NOT VALID",
"ALTER TABLE t ADD CONSTRAINT ck CHECK (id > 0) NO INHERIT NOT VALID, ADD COLUMN extra INT",
"CREATE TABLE t (id INT, CONSTRAINT ck CHECK (id > 0) NO INHERIT)",
"CREATE TABLE t (id INT CHECK (id > 0) NO INHERIT)",
"CREATE TABLE t (id INT CONSTRAINT ck CHECK (id > 0) NO INHERIT)"})
void roundTripsSharedCreateAndAlterCheck(String sql) throws JSQLParserException {
Statement statement = parse(sql);
assertEquals(sql, statement.toString());
roundTrip(statement);
}

@Test
void exposesIndependentInheritanceAndValidationFlags() throws JSQLParserException {
Alter alter = (Alter) parse(
"ALTER TABLE t ADD CONSTRAINT ck CHECK (id > 0) NO INHERIT NOT VALID");
CheckConstraint constraint =
(CheckConstraint) alter.getAlterExpressions().get(0).getIndex();
assertTrue(constraint.isNoInherit());
assertTrue(constraint.getConstraintAttributes().isNotValid());
constraint.setNoInherit(false);
constraint.setExpression(CCJSqlParserUtil.parseExpression("id > 10"));
assertEquals("ALTER TABLE t ADD CONSTRAINT ck CHECK (id > 10) NOT VALID", alter.toString());
roundTrip(alter);
List<Expression> visited = new ArrayList<>();
TableDefinitionTraversal.visit(alter.getAlterExpressions().get(0), visited::add, table -> {
});
assertEquals(List.of(constraint.getExpression()), visited);
alter.getAlterExpressions().get(0).setIndex(new CheckConstraint().withName("new_ck")
.withExpression(CCJSqlParserUtil.parseExpression("id > 20")).withNoInherit(true));
assertEquals("ALTER TABLE t ADD CONSTRAINT new_ck CHECK (id > 20) NO INHERIT",
alter.toString());
roundTrip(alter);
}

@Test
void preservesExistingCheckOptions() throws JSQLParserException {
CreateTable table = (CreateTable) parse("CREATE TABLE t (id INT, CHECK (id > 0))");
assertFalse(((CheckConstraint) table.getIndexes().get(0)).isNoInherit());
for (String suffix : new String[] {"", " ENFORCED", " NOT ENFORCED"}) {
String sql = "CREATE TABLE t (id INT, CHECK (id > 0)" + suffix + ")";
assertEquals(sql, CCJSqlParserUtil.parse(sql).toString());
assertEquals(sql,
CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.MYSQL)).toString());
}
}

private static Statement parse(String sql) throws JSQLParserException {
return CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.POSTGRESQL));
}

private static void roundTrip(Statement statement) throws JSQLParserException {
StringBuilder sql = new StringBuilder();
statement.accept(new StatementDeParser(sql), null);
assertEquals(statement.toString(), sql.toString());
assertEquals(sql.toString(), parse(sql.toString()).toString());
}
}
Loading