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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
| **DML** | `INSERT` · `UPDATE` · `UPSERT` · `MERGE` · `DELETE` · `TRUNCATE TABLE` |
| **DDL** | `CREATE …` · `ALTER …` · `DROP …` |
| **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` |
| **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints; enable with `parser.withDialect(Dialect.INFORMIX)` |
| **Salesforce SOQL** | `INCLUDES` · `EXCLUDES` |

Beyond statement shapes, the grammar handles nested sub-selects, bind parameters (`?`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public enum Dialect {
AdjacentStringLiterals.WHITESPACE,
Feature.allowDoubleQuotedStrings,
Feature.allowBackslashEscapeCharacter), SNOWFLAKE(
Feature.allowBackslashEscapeCharacter);
Feature.allowBackslashEscapeCharacter), INFORMIX;

private final Set<Feature> lexerFeatures;
private final AdjacentStringLiterals adjacentStringLiterals;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,12 @@ public void setEnforced(Boolean enforced) {
@Override
public String toString() {
StringBuilder b = new StringBuilder();
if (isUseConstraintKeyword() || getName() != null) {
b.append("CONSTRAINT");
if (getName() != null) {
b.append(" ").append(getName());
}
b.append(" ");
}
appendConstraintPrefixTo(b);
b.append("CHECK (").append(expression).append(")");
if (enforced != null) {
b.append(enforced ? " ENFORCED" : " NOT ENFORCED");
}
appendConstraintSuffixTo(b);
appendConstraintAttributesTo(b);
return b.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ public String toString() {
.append(PlainSelect.getStringList(getReferencedColumnNames(), true, true));
referentialActions.forEach(b::append);
}
appendConstraintSuffixTo(b);
appendConstraintAttributesTo(b);
return b.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,45 @@ public class NamedConstraint extends Index {

private String indexName;
private boolean useConstraintKeyword;
private ConstraintNamePosition constraintNamePosition = ConstraintNamePosition.BEFORE;

/** Position of the constraint symbol relative to its definition. */
public enum ConstraintNamePosition {
BEFORE, AFTER
}

public ConstraintNamePosition getConstraintNamePosition() {
return constraintNamePosition;
}

public void setConstraintNamePosition(ConstraintNamePosition position) {
constraintNamePosition = java.util.Objects.requireNonNull(position, "position");
}

public NamedConstraint withConstraintNamePosition(ConstraintNamePosition position) {
setConstraintNamePosition(position);
return this;
}

/** Appends the leading keyword and, for the usual syntax, the constraint name. */
public void appendConstraintPrefixTo(StringBuilder builder) {
boolean leadingName = getName() != null
&& constraintNamePosition == ConstraintNamePosition.BEFORE;
if (useConstraintKeyword || leadingName) {
builder.append("CONSTRAINT");
if (leadingName) {
builder.append(' ').append(getName());
}
builder.append(' ');
}
}

/** Appends an Informix constraint name after the complete constraint definition. */
public void appendConstraintSuffixTo(StringBuilder builder) {
if (constraintNamePosition == ConstraintNamePosition.AFTER && getName() != null) {
builder.append(" CONSTRAINT ").append(getName());
}
}

/**
* Returns the optional index name declared after the constraint type. This is distinct from
Expand All @@ -44,9 +83,6 @@ public void setUseConstraintKeyword(boolean useConstraintKeyword) {
@Override
public String toString() {
String idxSpecText = PlainSelect.getStringList(getIndexSpec(), false, false);
String head = useConstraintKeyword || getName() != null
? "CONSTRAINT" + (getName() != null ? " " + getName() : "") + " "
: "";
String keyword = getIndexKeyword() != null
&& !getType().toUpperCase(java.util.Locale.ROOT)
.endsWith(getIndexKeyword().toUpperCase(java.util.Locale.ROOT))
Expand All @@ -61,9 +97,12 @@ public String toString() {
: " " + PlainSelect.getStringList(getColumnsNames(), true, true))
+
(!"".equals(idxSpecText) ? " " + idxSpecText : "");
StringBuilder sql = new StringBuilder(head).append(tail);
StringBuilder sql = new StringBuilder();
appendConstraintPrefixTo(sql);
sql.append(tail);
appendConstraintOptionsTo(sql);
if (getKind() != Kind.FOREIGN_KEY) {
appendConstraintSuffixTo(sql);
appendConstraintAttributesTo(sql);
}
return sql.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,7 @@ private void deParseExclude(ExcludeConstraint constraint) {
}

private void deParseCheck(CheckConstraint constraint) {
if (constraint.getName() != null || constraint.isUseConstraintKeyword()) {
builder.append("CONSTRAINT");
if (constraint.getName() != null) {
builder.append(' ').append(constraint.getName());
}
builder.append(' ');
}
constraint.appendConstraintPrefixTo(builder);
builder.append("CHECK (");
if (constraint.getExpression() != null) {
constraint.getExpression().accept(expressionVisitor, null);
Expand All @@ -111,6 +105,7 @@ private void deParseCheck(CheckConstraint constraint) {
if (constraint.getEnforced() != null) {
builder.append(constraint.getEnforced() ? " ENFORCED" : " NOT ENFORCED");
}
constraint.appendConstraintSuffixTo(builder);
constraint.appendConstraintAttributesTo(builder);
}
}
39 changes: 39 additions & 0 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -14403,6 +14403,36 @@ DefaultConstraint DefaultConstraintSpec():
{ return constraint; }
}

/** Parses Informix's ADD CONSTRAINT definition [CONSTRAINT name]. */
NamedConstraint InformixConstraint():
{
NamedConstraint constraint;
Token type;
List<String> columns;
String name;
}
{
<K_CONSTRAINT>
Comment thread
manticore-projects marked this conversation as resolved.
(
( type=<K_PRIMARY> <K_KEY> | type=<K_UNIQUE> )
columns=ColumnsNamesList() {
constraint = new NamedConstraint()
.withType(type.kind == K_PRIMARY ? "PRIMARY KEY" : type.image)
.withColumnsNames(columns);
}
|
constraint=ForeignKeySpec(null)
|
constraint=CheckConstraintSpec(null)
)
[ <K_CONSTRAINT> name=RelObjectName() { constraint.setName(name); } ]
{
constraint.setUseConstraintKeyword(true);
constraint.setConstraintNamePosition(NamedConstraint.ConstraintNamePosition.AFTER);
return constraint;
}
}

/**
* Parses ADD/ALTER CONSTRAINT clause within AlterExpression.
* Handles: CONSTRAINT [UNIQUE [KEY|INDEX]] name columns
Expand Down Expand Up @@ -14703,6 +14733,15 @@ AlterExpression AlterExpressionAddAlterModify():
alterExp.setIndex(index);
}
|
LOOKAHEAD(<K_CONSTRAINT> (<K_PRIMARY> <K_KEY> | <K_FOREIGN> <K_KEY>
| <K_UNIQUE> | <K_CHECK>) "(",
{ Dialect.INFORMIX.name().equals(getAsString(Feature.dialect)) })
index=InformixConstraint() {
requireDdlSyntax(alterExp.getOperation() == AlterOperation.ADD,
"Informix constraint definitions require ADD");
alterExp.setIndex(index);
}
|
LOOKAHEAD({ isTableIndexAhead() }) index=TableIndexSpec(false) {
alterExp.setIndex(index);
if (index.getKind() == Index.Kind.PRIMARY_KEY) {
Expand Down
11 changes: 11 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -711,9 +711,20 @@ One grammar covers every supported RDBMS, but a few pieces of syntax mean differ
- ``withDoubleQuotedStrings``, ``withBackslashEscapeCharacter``, any-whitespace rule for adjacent string literals
* - ``SNOWFLAKE``
- ``withBackslashEscapeCharacter`` only, double quotes stay quoted identifiers
* - ``INFORMIX``
- Informix ``ALTER TABLE ... ADD CONSTRAINT`` definitions with optional trailing constraint names

Features set explicitly *after* the preset win over it.

Informix's constraint form requires an explicit dialect selection:

.. code-block:: java

Statement stmt = CCJSqlParserUtil.parse(
"ALTER TABLE child ADD CONSTRAINT FOREIGN KEY (id) "
+ "REFERENCES parent(id) CONSTRAINT fk_child",
parser -> parser.withDialect(Dialect.INFORMIX));

The individual features
------------------------------

Expand Down
Loading
Loading