maven-site-plugin
3.12.1
diff --git a/src/main/java/net/sf/jsqlparser/expression/DoubleValue.java b/src/main/java/net/sf/jsqlparser/expression/DoubleValue.java
index 43e072dcf..910ad722e 100644
--- a/src/main/java/net/sf/jsqlparser/expression/DoubleValue.java
+++ b/src/main/java/net/sf/jsqlparser/expression/DoubleValue.java
@@ -24,6 +24,9 @@ public DoubleValue() {
}
public DoubleValue(final String value) {
+ if (value == null || value.length() == 0) {
+ throw new IllegalArgumentException("value can neither be null nor empty.");
+ }
String val = value;
if (val.charAt(0) == '+') {
val = val.substring(1);
diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java
index a93efedb5..7c8a25c3b 100644
--- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java
+++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java
@@ -64,6 +64,8 @@
import net.sf.jsqlparser.statement.select.UnPivot;
import net.sf.jsqlparser.statement.select.WithItem;
+import java.util.Optional;
+
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.UncommentedEmptyMethodBody"})
public class ExpressionVisitorAdapter
implements ExpressionVisitor, PivotVisitor, SelectItemVisitor {
@@ -281,14 +283,7 @@ public void visit(Column column) {
@Override
public void visit(ParenthesedSelect selectBody) {
- if (selectVisitor != null) {
- if (selectBody.getWithItemsList() != null) {
- for (WithItem item : selectBody.getWithItemsList()) {
- item.accept(selectVisitor);
- }
- }
- selectBody.accept(selectVisitor);
- }
+ visit((Select) selectBody);
if (selectBody.getPivot() != null) {
selectBody.getPivot().accept(this);
}
@@ -382,11 +377,19 @@ public void visit(AnalyticExpression expr) {
element.getExpression().accept(this);
}
}
-
if (expr.getWindowElement() != null) {
- expr.getWindowElement().getRange().getStart().getExpression().accept(this);
- expr.getWindowElement().getRange().getEnd().getExpression().accept(this);
- expr.getWindowElement().getOffset().getExpression().accept(this);
+ /*
+ * Visit expressions from the range and offset of the window element. Do this using
+ * optional chains, because several things down the tree can be null e.g. the
+ * expression. So, null-safe versions of e.g.:
+ * expr.getWindowElement().getOffset().getExpression().accept(this);
+ */
+ Optional.ofNullable(expr.getWindowElement().getRange()).map(WindowRange::getStart)
+ .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this));
+ Optional.ofNullable(expr.getWindowElement().getRange()).map(WindowRange::getEnd)
+ .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this));
+ Optional.ofNullable(expr.getWindowElement().getOffset())
+ .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this));
}
}
@@ -653,7 +656,14 @@ public void visit(GeometryDistance geometryDistance) {
@Override
public void visit(Select selectBody) {
-
+ if (selectVisitor != null) {
+ if (selectBody.getWithItemsList() != null) {
+ for (WithItem item : selectBody.getWithItemsList()) {
+ item.accept(selectVisitor);
+ }
+ }
+ selectBody.accept(selectVisitor);
+ }
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java b/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java
index a03d230ff..aaedebc68 100644
--- a/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java
+++ b/src/main/java/net/sf/jsqlparser/expression/JdbcParameter.java
@@ -11,20 +11,43 @@
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* A '?' in a statement or a ?<number> e.g. ?4
*/
public class JdbcParameter extends ASTNodeAccessImpl implements Expression {
+ private String parameterCharacter = "?";
private Integer index;
private boolean useFixedIndex = false;
- public JdbcParameter() {
- }
+ public JdbcParameter() {}
- public JdbcParameter(Integer index, boolean useFixedIndex) {
+ public JdbcParameter(Integer index, boolean useFixedIndex, String parameterCharacter) {
this.index = index;
this.useFixedIndex = useFixedIndex;
+ this.parameterCharacter = parameterCharacter;
+
+ // This is needed for Parameters starting with "$" like "$2"
+ // Those will contain the index in the parameterCharacter
+ final Pattern pattern = Pattern.compile("(\\$)(\\d*)");
+ final Matcher matcher = pattern.matcher(parameterCharacter);
+ if (matcher.find() && matcher.groupCount() == 2) {
+ this.useFixedIndex = true;
+ this.parameterCharacter = matcher.group(1);
+ this.index = Integer.valueOf(matcher.group(2));
+ }
+ }
+
+ public String getParameterCharacter() {
+ return parameterCharacter;
+ }
+
+ public JdbcParameter setParameterCharacter(String parameterCharacter) {
+ this.parameterCharacter = parameterCharacter;
+ return this;
}
public Integer getIndex() {
@@ -50,7 +73,7 @@ public void accept(ExpressionVisitor expressionVisitor) {
@Override
public String toString() {
- return useFixedIndex ? "?" + index : "?";
+ return useFixedIndex ? parameterCharacter + index : parameterCharacter;
}
public JdbcParameter withIndex(Integer index) {
diff --git a/src/main/java/net/sf/jsqlparser/expression/LongValue.java b/src/main/java/net/sf/jsqlparser/expression/LongValue.java
index 014802fb5..730a8871e 100644
--- a/src/main/java/net/sf/jsqlparser/expression/LongValue.java
+++ b/src/main/java/net/sf/jsqlparser/expression/LongValue.java
@@ -26,6 +26,9 @@ public LongValue() {
}
public LongValue(final String value) {
+ if (value == null || value.length() == 0) {
+ throw new IllegalArgumentException("value can neither be null nor empty.");
+ }
String val = value;
if (val.charAt(0) == '+') {
val = val.substring(1);
diff --git a/src/main/java/net/sf/jsqlparser/expression/TimeValue.java b/src/main/java/net/sf/jsqlparser/expression/TimeValue.java
index 100945147..f87970fa1 100644
--- a/src/main/java/net/sf/jsqlparser/expression/TimeValue.java
+++ b/src/main/java/net/sf/jsqlparser/expression/TimeValue.java
@@ -25,6 +25,9 @@ public TimeValue() {
}
public TimeValue(String value) {
+ if (value == null || value.length() == 0) {
+ throw new IllegalArgumentException("value can neither be null nor empty.");
+ }
this.value = Time.valueOf(value.substring(1, value.length() - 1));
}
diff --git a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java
index 771402b1a..13ebcd781 100644
--- a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java
+++ b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java
@@ -9,12 +9,12 @@
*/
package net.sf.jsqlparser.parser;
-import java.util.ArrayList;
-import java.util.List;
-
import net.sf.jsqlparser.parser.feature.Feature;
import net.sf.jsqlparser.parser.feature.FeatureConfiguration;
+import java.util.ArrayList;
+import java.util.List;
+
public abstract class AbstractJSqlParser {
protected int jdbcParameterIndex = 0;
@@ -26,7 +26,7 @@ public P withSquareBracketQuotation(boolean allowSquareBracketQuotation) {
}
public P withAllowComplexParsing(boolean allowComplexParsing) {
- return withFeature(Feature.allowComplexParsing, allowComplexParsing);
+ return withFeature(Feature.allowComplexParsing, allowComplexParsing);
}
public P withUnsupportedStatements(boolean allowUnsupportedStatements) {
@@ -40,7 +40,7 @@ public P withTimeOut(long timeOutMillSeconds) {
public P withBackslashEscapeCharacter(boolean allowBackslashEscapeCharacter) {
return withFeature(Feature.allowBackslashEscapeCharacter, allowBackslashEscapeCharacter);
}
-
+
public P withFeature(Feature f, boolean enabled) {
getConfiguration().setValue(f, enabled);
return me();
diff --git a/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java
index 5ea6af47f..eb6e8b2ce 100644
--- a/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java
+++ b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java
@@ -79,6 +79,10 @@ public static Statement parse(String sql) throws JSQLParserException {
public static Statement parse(String sql, Consumer consumer)
throws JSQLParserException {
+ if (sql == null || sql.isEmpty()) {
+ return null;
+ }
+
ExecutorService executorService = Executors.newSingleThreadExecutor();
Statement statement = null;
try {
@@ -92,8 +96,11 @@ public static Statement parse(String sql, Consumer consumer)
public static Statement parse(String sql, ExecutorService executorService,
Consumer consumer)
throws JSQLParserException {
- Statement statement = null;
+ if (sql == null || sql.isEmpty()) {
+ return null;
+ }
+ Statement statement = null;
// first, try to parse fast and simple
CCJSqlParser parser = newParser(sql);
if (consumer != null) {
@@ -122,6 +129,10 @@ public static Statement parse(String sql, ExecutorService executorService,
}
public static CCJSqlParser newParser(String sql) {
+ if (sql == null || sql.isEmpty()) {
+ return null;
+ }
+
return new CCJSqlParser(new StringProvider(sql));
}
@@ -134,6 +145,10 @@ public static CCJSqlParser newParser(InputStream is, String encoding) throws IOE
}
public static Node parseAST(String sql) throws JSQLParserException {
+ if (sql == null || sql.isEmpty()) {
+ return null;
+ }
+
CCJSqlParser parser = newParser(sql);
try {
parser.Statement();
@@ -162,11 +177,19 @@ public static Statement parse(InputStream is, String encoding) throws JSQLParser
}
public static Expression parseExpression(String expression) throws JSQLParserException {
+ if (expression == null || expression.isEmpty()) {
+ return null;
+ }
+
return parseExpression(expression, true);
}
public static Expression parseExpression(String expression, boolean allowPartialParse)
throws JSQLParserException {
+ if (expression == null || expression.isEmpty()) {
+ return null;
+ }
+
return parseExpression(expression, allowPartialParse, p -> {
});
}
@@ -174,8 +197,11 @@ public static Expression parseExpression(String expression, boolean allowPartial
@SuppressWarnings("PMD.CyclomaticComplexity")
public static Expression parseExpression(String expressionStr, boolean allowPartialParse,
Consumer consumer) throws JSQLParserException {
- Expression expression = null;
+ if (expressionStr == null || expressionStr.isEmpty()) {
+ return null;
+ }
+ Expression expression = null;
// first, try to parse fast and simple
try {
CCJSqlParser parser = newParser(expressionStr).withAllowComplexParsing(false);
@@ -225,6 +251,9 @@ public static Expression parseExpression(String expressionStr, boolean allowPart
* @see #parseCondExpression(String, boolean)
*/
public static Expression parseCondExpression(String condExpr) throws JSQLParserException {
+ if (condExpr == null || condExpr.isEmpty()) {
+ return null;
+ }
return parseCondExpression(condExpr, true);
}
@@ -238,6 +267,9 @@ public static Expression parseCondExpression(String condExpr) throws JSQLParserE
*/
public static Expression parseCondExpression(String condExpr, boolean allowPartialParse)
throws JSQLParserException {
+ if (condExpr == null || condExpr.isEmpty()) {
+ return null;
+ }
return parseCondExpression(condExpr, allowPartialParse, p -> {
});
}
@@ -245,8 +277,11 @@ public static Expression parseCondExpression(String condExpr, boolean allowParti
@SuppressWarnings("PMD.CyclomaticComplexity")
public static Expression parseCondExpression(String conditionalExpressionStr,
boolean allowPartialParse, Consumer consumer) throws JSQLParserException {
- Expression expression = null;
+ if (conditionalExpressionStr == null || conditionalExpressionStr.isEmpty()) {
+ return null;
+ }
+ Expression expression = null;
// first, try to parse fast and simple
try {
CCJSqlParser parser =
@@ -323,11 +358,19 @@ public Statement call() throws ParseException {
* @return the statements parsed
*/
public static Statements parseStatements(String sqls) throws JSQLParserException {
+ if (sqls == null || sqls.isEmpty()) {
+ return null;
+ }
+
return parseStatements(sqls, null);
}
public static Statements parseStatements(String sqls, Consumer consumer)
throws JSQLParserException {
+ if (sqls == null || sqls.isEmpty()) {
+ return null;
+ }
+
ExecutorService executorService = Executors.newSingleThreadExecutor();
final Statements statements = parseStatements(sqls, executorService, consumer);
executorService.shutdown();
@@ -343,8 +386,11 @@ public static Statements parseStatements(String sqls, Consumer con
public static Statements parseStatements(String sqls, ExecutorService executorService,
Consumer consumer)
throws JSQLParserException {
- Statements statements = null;
+ if (sqls == null || sqls.isEmpty()) {
+ return null;
+ }
+ Statements statements = null;
CCJSqlParser parser = newParser(sqls);
if (consumer != null) {
consumer.accept(parser);
diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/Alter.java b/src/main/java/net/sf/jsqlparser/statement/alter/Alter.java
index 8257aabac..0aabfc181 100644
--- a/src/main/java/net/sf/jsqlparser/statement/alter/Alter.java
+++ b/src/main/java/net/sf/jsqlparser/statement/alter/Alter.java
@@ -24,6 +24,8 @@ public class Alter implements Statement {
private Table table;
private boolean useOnly = false;
+ private boolean useTableIfExists = false;
+
private List alterExpressions;
public Table getTable() {
@@ -42,6 +44,19 @@ public void setUseOnly(boolean useOnly) {
this.useOnly = useOnly;
}
+ public boolean isUseTableIfExists() {
+ return useTableIfExists;
+ }
+
+ public void setUseTableIfExists(boolean useTableIfExists) {
+ this.useTableIfExists = useTableIfExists;
+ }
+
+ public Alter withUseTableIfExists(boolean useTableIfExists) {
+ this.useTableIfExists = useTableIfExists;
+ return this;
+ }
+
public void addAlterExpression(AlterExpression alterExpression) {
if (alterExpressions == null) {
alterExpressions = new ArrayList();
@@ -71,7 +86,7 @@ public String toString() {
b.append("ONLY ");
}
- if (alterExpressions.size()>0 && alterExpressions.get(0).getOperation()==AlterOperation.RENAME_TABLE && alterExpressions.get(0).isUsingIfExists()) {
+ if (useTableIfExists) {
b.append("IF EXISTS ");
}
@@ -108,13 +123,15 @@ public Alter withAlterExpressions(List alterExpressions) {
}
public Alter addAlterExpressions(AlterExpression... alterExpressions) {
- List collection = Optional.ofNullable(getAlterExpressions()).orElseGet(ArrayList::new);
+ List collection =
+ Optional.ofNullable(getAlterExpressions()).orElseGet(ArrayList::new);
Collections.addAll(collection, alterExpressions);
return this.withAlterExpressions(collection);
}
public Alter addAlterExpressions(Collection extends AlterExpression> alterExpressions) {
- List collection = Optional.ofNullable(getAlterExpressions()).orElseGet(ArrayList::new);
+ List collection =
+ Optional.ofNullable(getAlterExpressions()).orElseGet(ArrayList::new);
collection.addAll(alterExpressions);
return this.withAlterExpressions(collection);
}
diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java
index f31de0da3..0a99c97d5 100644
--- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java
+++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java
@@ -68,6 +68,10 @@ public class AlterExpression implements Serializable {
private boolean useBrackets = false;
+ private String truncatePartitionName = null;
+
+ private boolean useIfNotExists = false;
+
public Index getOldIndex() {
return oldIndex;
}
@@ -402,6 +406,32 @@ public void setUk(boolean uk) {
this.uk = uk;
}
+ public String getTruncatePartitionName() {
+ return truncatePartitionName;
+ }
+
+ public void setTruncatePartitionName(String truncatePartitionName) {
+ this.truncatePartitionName = truncatePartitionName;
+ }
+
+ public AlterExpression withTruncatePartitionName(String truncatePartitionName) {
+ this.truncatePartitionName = truncatePartitionName;
+ return this;
+ }
+
+ public boolean isUseIfNotExists() {
+ return useIfNotExists;
+ }
+
+ public void setUseIfNotExists(boolean useIfNotExists) {
+ this.useIfNotExists = useIfNotExists;
+ }
+
+ public AlterExpression withUserIfNotExists(boolean userIfNotExists) {
+ this.useIfNotExists = userIfNotExists;
+ return this;
+ }
+
@Override
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity",
"PMD.ExcessiveMethodLength", "PMD.SwitchStmtsShouldHaveDefault"})
@@ -441,6 +471,9 @@ public String toString() {
&& !pkColumns.isEmpty()) {
// Oracle Multi Column Drop
b.append("DROP (").append(PlainSelect.getStringList(pkColumns)).append(')');
+ } else if (operation == AlterOperation.TRUNCATE_PARTITION
+ && truncatePartitionName != null) {
+ b.append("TRUNCATE PARTITION ").append(truncatePartitionName);
} else {
if (operation == AlterOperation.COMMENT_WITH_EQUAL_SIGN) {
b.append("COMMENT =").append(" ");
@@ -475,6 +508,10 @@ public String toString() {
if (hasColumn) {
b.append("COLUMN ");
}
+ if (useIfNotExists
+ && operation == AlterOperation.ADD) {
+ b.append("IF NOT EXISTS ");
+ }
}
if (useBrackets && colDataTypeList.size() == 1) {
b.append(" ( ");
@@ -736,7 +773,8 @@ public ColumnDataType(
@Override
public String toString() {
- return getColumnName() + (withType ? " TYPE " : " ") + toStringDataTypeAndSpec();
+ return getColumnName() + (withType ? " TYPE " : getColDataType() == null ? "" : " ")
+ + toStringDataTypeAndSpec();
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java
index adddaa14d..3d0ead731 100644
--- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java
+++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java
@@ -10,7 +10,7 @@
package net.sf.jsqlparser.statement.alter;
public enum AlterOperation {
- ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, COMMENT_WITH_EQUAL_SIGN, UNSPECIFIC;
+ ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, COMMENT_WITH_EQUAL_SIGN, UNSPECIFIC, TRUNCATE_PARTITION;
public static AlterOperation from(String operation) {
return Enum.valueOf(AlterOperation.class, operation.toUpperCase());
diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/ColumnDefinition.java b/src/main/java/net/sf/jsqlparser/statement/create/table/ColumnDefinition.java
index f19962d8e..90a0e0e40 100644
--- a/src/main/java/net/sf/jsqlparser/statement/create/table/ColumnDefinition.java
+++ b/src/main/java/net/sf/jsqlparser/statement/create/table/ColumnDefinition.java
@@ -69,9 +69,10 @@ public String toString() {
}
public String toStringDataTypeAndSpec() {
- return colDataType + (columnSpecs != null && !columnSpecs.isEmpty()
- ? " " + PlainSelect.getStringList(columnSpecs, false, false)
- : "");
+ return (colDataType == null ? "" : colDataType)
+ + (columnSpecs != null && !columnSpecs.isEmpty()
+ ? " " + PlainSelect.getStringList(columnSpecs, false, false)
+ : "");
}
public ColumnDefinition withColumnName(String columnName) {
diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java b/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java
index 8381e50af..034902cca 100644
--- a/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java
+++ b/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java
@@ -26,6 +26,7 @@ public class CreateView implements Statement {
private ExpressionList columnNames = null;
private boolean materialized = false;
private ForceOption force = ForceOption.NONE;
+ private boolean secure = false;
private TemporaryOption temp = TemporaryOption.NONE;
private AutoRefreshOption autoRefresh = AutoRefreshOption.NONE;
private boolean withReadOnly = false;
@@ -88,6 +89,14 @@ public void setForce(ForceOption force) {
this.force = force;
}
+ public boolean isSecure() {
+ return secure;
+ }
+
+ public void setSecure(boolean secure) {
+ this.secure = secure;
+ }
+
public TemporaryOption getTemporary() {
return temp;
}
@@ -127,6 +136,9 @@ public String toString() {
sql.append("OR REPLACE ");
}
appendForceOptionIfApplicable(sql);
+ if (secure) {
+ sql.append("SECURE ");
+ }
if (temp != TemporaryOption.NONE) {
sql.append(temp.name()).append(" ");
diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/TemporaryOption.java b/src/main/java/net/sf/jsqlparser/statement/create/view/TemporaryOption.java
index 4caae850e..c543af966 100644
--- a/src/main/java/net/sf/jsqlparser/statement/create/view/TemporaryOption.java
+++ b/src/main/java/net/sf/jsqlparser/statement/create/view/TemporaryOption.java
@@ -10,7 +10,7 @@
package net.sf.jsqlparser.statement.create.view;
public enum TemporaryOption {
- NONE, TEMP, TEMPORARY;
+ NONE, TEMP, TEMPORARY, VOLATILE;
public static TemporaryOption from(String option) {
return Enum.valueOf(TemporaryOption.class, option.toUpperCase());
diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java
index 0be4d2528..ed2c9ee50 100644
--- a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java
+++ b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java
@@ -26,6 +26,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
public class Merge implements Statement {
@@ -37,9 +38,43 @@ public class Merge implements Statement {
private MergeInsert mergeInsert;
private MergeUpdate mergeUpdate;
private boolean insertFirst = false;
+ private List operations;
private OutputClause outputClause;
+ private void deriveOperationsFromStandardClauses() {
+ List operations = new ArrayList<>();
+ if (insertFirst) {
+ Optional.ofNullable(mergeInsert).ifPresent(operations::add);
+ Optional.ofNullable(mergeUpdate).ifPresent(operations::add);
+ } else {
+ Optional.ofNullable(mergeUpdate).ifPresent(operations::add);
+ Optional.ofNullable(mergeInsert).ifPresent(operations::add);
+ }
+ this.operations = operations;
+ }
+
+ private void deriveStandardClausesFromOperations() {
+ List applicableOperations =
+ Optional.ofNullable(operations).orElse(Collections.emptyList()).stream()
+ .filter(o -> o instanceof MergeUpdate || o instanceof MergeInsert)
+ .collect(Collectors.toList());
+ mergeUpdate = applicableOperations.stream()
+ .filter(o -> o instanceof MergeUpdate)
+ .map(MergeUpdate.class::cast)
+ .findFirst()
+ .orElse(null);
+ mergeInsert = applicableOperations.stream()
+ .filter(o -> o instanceof MergeInsert)
+ .map(MergeInsert.class::cast)
+ .findFirst()
+ .orElse(null);
+ insertFirst = applicableOperations.stream()
+ .findFirst()
+ .map(o -> o instanceof MergeInsert)
+ .orElse(false);
+ }
+
public List getWithItemsList() {
return withItemsList;
}
@@ -129,12 +164,22 @@ public void setOnCondition(Expression onCondition) {
this.onCondition = onCondition;
}
+ public List getOperations() {
+ return operations;
+ }
+
+ public void setOperations(List operations) {
+ this.operations = operations;
+ deriveStandardClausesFromOperations();
+ }
+
public MergeInsert getMergeInsert() {
return mergeInsert;
}
- public void setMergeInsert(MergeInsert insert) {
- this.mergeInsert = insert;
+ public void setMergeInsert(MergeInsert mergeInsert) {
+ this.mergeInsert = mergeInsert;
+ deriveOperationsFromStandardClauses();
}
public MergeUpdate getMergeUpdate() {
@@ -143,6 +188,7 @@ public MergeUpdate getMergeUpdate() {
public void setMergeUpdate(MergeUpdate mergeUpdate) {
this.mergeUpdate = mergeUpdate;
+ deriveOperationsFromStandardClauses();
}
@Override
@@ -156,6 +202,7 @@ public boolean isInsertFirst() {
public void setInsertFirst(boolean insertFirst) {
this.insertFirst = insertFirst;
+ deriveOperationsFromStandardClauses();
}
public OutputClause getOutputClause() {
@@ -193,16 +240,8 @@ public String toString() {
b.append(" ON ");
b.append(onCondition);
- if (insertFirst && mergeInsert != null) {
- b.append(mergeInsert);
- }
-
- if (mergeUpdate != null) {
- b.append(mergeUpdate);
- }
-
- if (!insertFirst && mergeInsert != null) {
- b.append(mergeInsert);
+ if (operations != null && !operations.isEmpty()) {
+ operations.forEach(b::append);
}
if (outputClause != null) {
diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java
new file mode 100644
index 000000000..fee068a99
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java
@@ -0,0 +1,47 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2024 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement.merge;
+
+import net.sf.jsqlparser.expression.Expression;
+
+import java.io.Serializable;
+
+public class MergeDelete implements Serializable, MergeOperation {
+ private Expression andPredicate;
+
+ public Expression getAndPredicate() {
+ return andPredicate;
+ }
+
+ public void setAndPredicate(Expression andPredicate) {
+ this.andPredicate = andPredicate;
+ }
+
+ public MergeDelete withAndPredicate(Expression andPredicate) {
+ this.setAndPredicate(andPredicate);
+ return this;
+ }
+
+ @Override
+ public void accept(MergeOperationVisitor mergeOperationVisitor) {
+ mergeOperationVisitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append(" WHEN MATCHED");
+ if (andPredicate != null) {
+ b.append(" AND ").append(andPredicate.toString());
+ }
+ b.append(" THEN DELETE");
+ return b.toString();
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java
index f6affa3ef..95b69bbf3 100644
--- a/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java
+++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java
@@ -18,7 +18,7 @@
import java.util.Collection;
import java.util.Optional;
-public class MergeInsert implements Serializable {
+public class MergeInsert implements Serializable, MergeOperation {
private Expression andPredicate;
private ExpressionList columns;
@@ -57,6 +57,11 @@ public void setWhereCondition(Expression whereCondition) {
this.whereCondition = whereCondition;
}
+ @Override
+ public void accept(MergeOperationVisitor mergeOperationVisitor) {
+ mergeOperationVisitor.visit(this);
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder();
diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperation.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperation.java
new file mode 100644
index 000000000..6a4a9ceff
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperation.java
@@ -0,0 +1,17 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2024 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement.merge;
+
+/**
+ * Marker interface to cover {@link MergeDelete}, {@link MergeUpdate} and {@link MergeInsert}
+ */
+public interface MergeOperation {
+ void accept(MergeOperationVisitor mergeOperationVisitor);
+}
diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitor.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitor.java
new file mode 100644
index 000000000..5a0f217d6
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitor.java
@@ -0,0 +1,19 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2024 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement.merge;
+
+public interface MergeOperationVisitor {
+
+ void visit(MergeDelete mergeDelete);
+
+ void visit(MergeUpdate mergeUpdate);
+
+ void visit(MergeInsert mergeInsert);
+}
diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitorAdapter.java
new file mode 100644
index 000000000..80883192c
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeOperationVisitorAdapter.java
@@ -0,0 +1,28 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2024 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement.merge;
+
+@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"})
+public class MergeOperationVisitorAdapter implements MergeOperationVisitor {
+ @Override
+ public void visit(MergeDelete mergeDelete) {
+
+ }
+
+ @Override
+ public void visit(MergeUpdate mergeUpdate) {
+
+ }
+
+ @Override
+ public void visit(MergeInsert mergeInsert) {
+
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java
index 38e9261a2..c744ac4f2 100644
--- a/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java
+++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java
@@ -15,7 +15,7 @@
import java.io.Serializable;
import java.util.List;
-public class MergeUpdate implements Serializable {
+public class MergeUpdate implements Serializable, MergeOperation {
private List updateSets;
private Expression andPredicate;
@@ -61,6 +61,11 @@ public void setDeleteWhereCondition(Expression deleteWhereCondition) {
this.deleteWhereCondition = deleteWhereCondition;
}
+ @Override
+ public void accept(MergeOperationVisitor mergeOperationVisitor) {
+ mergeOperationVisitor.visit(this);
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder();
diff --git a/src/main/java/net/sf/jsqlparser/statement/select/AllTableColumns.java b/src/main/java/net/sf/jsqlparser/statement/select/AllTableColumns.java
index 429ebcd35..cda2e8136 100644
--- a/src/main/java/net/sf/jsqlparser/statement/select/AllTableColumns.java
+++ b/src/main/java/net/sf/jsqlparser/statement/select/AllTableColumns.java
@@ -9,6 +9,7 @@
*/
package net.sf.jsqlparser.statement.select;
+import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
@@ -50,4 +51,9 @@ public AllTableColumns withTable(Table table) {
public StringBuilder appendTo(StringBuilder builder) {
return super.appendTo(table.appendTo(builder).append("."));
}
+
+ @Override
+ public void accept(ExpressionVisitor expressionVisitor) {
+ expressionVisitor.visit(this);
+ }
}
diff --git a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java
index 72a4e530c..2745dae6d 100644
--- a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java
+++ b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java
@@ -65,6 +65,12 @@ public class PlainSelect extends Select {
*/
private boolean isUsingFinal = false;
+ private boolean isUsingOnly = false;
+
+ private boolean useWithNoLog = false;
+
+ private Table intoTempTable = null;
+
@Deprecated
public boolean isUseBrackets() {
return false;
@@ -213,6 +219,45 @@ public PlainSelect withUsingFinal(boolean usingFinal) {
return this;
}
+ public boolean isUsingOnly() {
+ return isUsingOnly;
+ }
+
+ public void setUsingOnly(boolean usingOnly) {
+ isUsingOnly = usingOnly;
+ }
+
+ public PlainSelect withUsingOnly(boolean usingOnly) {
+ this.setUsingOnly(usingOnly);
+ return this;
+ }
+
+ public boolean isUseWithNoLog() {
+ return useWithNoLog;
+ }
+
+ public void setUseWithNoLog(boolean useWithNoLog) {
+ this.useWithNoLog = useWithNoLog;
+ }
+
+ public PlainSelect withUseWithNoLog(boolean useWithNoLog) {
+ this.setUseWithNoLog(useWithNoLog);
+ return this;
+ }
+
+ public Table getIntoTempTable() {
+ return intoTempTable;
+ }
+
+ public void setIntoTempTable(Table intoTempTable) {
+ this.intoTempTable = intoTempTable;
+ }
+
+ public PlainSelect withIntoTempTable(Table intoTempTable) {
+ this.setIntoTempTable(intoTempTable);
+ return this;
+ }
+
@Override
public void accept(SelectVisitor selectVisitor) {
selectVisitor.visit(this);
@@ -439,7 +484,11 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) {
}
if (fromItem != null) {
- builder.append(" FROM ").append(fromItem);
+ builder.append(" FROM ");
+ if (isUsingOnly) {
+ builder.append("ONLY ");
+ }
+ builder.append(fromItem);
if (lateralViews != null) {
for (LateralView lateralView : lateralViews) {
builder.append(" ").append(lateralView);
@@ -510,6 +559,12 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) {
builder.append(" WHERE ").append(where);
}
}
+ if (intoTempTable != null) {
+ builder.append(" INTO TEMP ").append(intoTempTable);
+ }
+ if (useWithNoLog) {
+ builder.append(" WITH NO LOG");
+ }
return builder;
}
diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
index 7a94ceff7..4dc760925 100644
--- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
+++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
@@ -176,6 +176,7 @@
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.upsert.Upsert;
+
/**
* Find all used tables within an select statement.
*
@@ -294,15 +295,7 @@ public void visit(PlainSelect plainSelect) {
plainSelect.getFromItem().accept(this);
}
- if (plainSelect.getJoins() != null) {
- for (Join join : plainSelect.getJoins()) {
- join.getFromItem().accept(this);
- join.getRightItem().accept(this);
- for (Expression expression : join.getOnExpressions()) {
- expression.accept(this);
- }
- }
- }
+ visitJoins(plainSelect.getJoins());
if (plainSelect.getWhere() != null) {
plainSelect.getWhere().accept(this);
}
@@ -807,15 +800,7 @@ public void visit(Delete delete) {
}
}
- if (delete.getJoins() != null) {
- for (Join join : delete.getJoins()) {
- join.getFromItem().accept(this);
- join.getRightItem().accept(this);
- for (Expression expression : join.getOnExpressions()) {
- expression.accept(this);
- }
- }
- }
+ visitJoins(delete.getJoins());
if (delete.getWhere() != null) {
delete.getWhere().accept(this);
@@ -1033,6 +1018,26 @@ public void visit(UseStatement use) {
@Override
public void visit(ParenthesedFromItem parenthesis) {
parenthesis.getFromItem().accept(this);
+ // support join keyword in fromItem
+ visitJoins(parenthesis.getJoins());
+ }
+
+ /**
+ * visit join block
+ *
+ * @param parenthesis join sql block
+ */
+ private void visitJoins(List parenthesis) {
+ if (parenthesis == null) {
+ return;
+ }
+ for (Join join : parenthesis) {
+ join.getFromItem().accept(this);
+ join.getRightItem().accept(this);
+ for (Expression expression : join.getOnExpressions()) {
+ expression.accept(this);
+ }
+ }
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java
index 1f5662e68..c0afab53e 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java
@@ -53,6 +53,9 @@ public void deParse(CreateView createView) {
default:
// nothing
}
+ if (createView.isSecure()) {
+ buffer.append("SECURE ");
+ }
if (createView.getTemporary() != TemporaryOption.NONE) {
buffer.append(createView.getTemporary().name()).append(" ");
}
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java
index 494444348..ad82ac582 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java
@@ -328,7 +328,7 @@ public void visit(IsBooleanExpression isBooleanExpression) {
@Override
public void visit(JdbcParameter jdbcParameter) {
- buffer.append("?");
+ buffer.append(jdbcParameter.getParameterCharacter());
if (jdbcParameter.isUseFixedIndex()) {
buffer.append(jdbcParameter.getIndex());
}
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/MergeDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/MergeDeParser.java
new file mode 100644
index 000000000..dfcf83399
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/MergeDeParser.java
@@ -0,0 +1,118 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2024 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.util.deparser;
+
+import net.sf.jsqlparser.statement.merge.*;
+import net.sf.jsqlparser.statement.select.WithItem;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class MergeDeParser extends AbstractDeParser implements MergeOperationVisitor {
+ private final ExpressionDeParser expressionDeParser;
+
+ private final SelectDeParser selectDeParser;
+
+ public MergeDeParser(ExpressionDeParser expressionDeParser, SelectDeParser selectDeParser,
+ StringBuilder buffer) {
+ super(buffer);
+ this.expressionDeParser = expressionDeParser;
+ this.selectDeParser = selectDeParser;
+ }
+
+ @Override
+ void deParse(Merge merge) {
+ List withItemsList = merge.getWithItemsList();
+ if (withItemsList != null && !withItemsList.isEmpty()) {
+ buffer.append("WITH ");
+ for (Iterator iter = withItemsList.iterator(); iter.hasNext();) {
+ iter.next().accept(expressionDeParser);
+ if (iter.hasNext()) {
+ buffer.append(",");
+ }
+ buffer.append(" ");
+ }
+ }
+
+ buffer.append("MERGE ");
+ if (merge.getOracleHint() != null) {
+ buffer.append(merge.getOracleHint()).append(" ");
+ }
+ buffer.append("INTO ");
+ merge.getTable().accept(selectDeParser);
+
+ buffer.append(" USING ");
+ merge.getFromItem().accept(selectDeParser);
+
+ buffer.append(" ON ");
+ merge.getOnCondition().accept(expressionDeParser);
+
+ List operations = merge.getOperations();
+ if (operations != null && !operations.isEmpty()) {
+ operations.forEach(operation -> operation.accept(this));
+ }
+
+ if (merge.getOutputClause() != null) {
+ merge.getOutputClause().appendTo(buffer);
+ }
+ }
+
+ @Override
+ public void visit(MergeDelete mergeDelete) {
+ buffer.append(" WHEN MATCHED");
+ if (mergeDelete.getAndPredicate() != null) {
+ buffer.append(" AND ");
+ mergeDelete.getAndPredicate().accept(expressionDeParser);
+ }
+ buffer.append(" THEN DELETE");
+ }
+
+ @Override
+ public void visit(MergeUpdate mergeUpdate) {
+ buffer.append(" WHEN MATCHED");
+ if (mergeUpdate.getAndPredicate() != null) {
+ buffer.append(" AND ");
+ mergeUpdate.getAndPredicate().accept(expressionDeParser);
+ }
+ buffer.append(" THEN UPDATE SET ");
+ deparseUpdateSets(mergeUpdate.getUpdateSets(), buffer, expressionDeParser);
+
+ if (mergeUpdate.getWhereCondition() != null) {
+ buffer.append(" WHERE ");
+ mergeUpdate.getWhereCondition().accept(expressionDeParser);
+ }
+
+ if (mergeUpdate.getDeleteWhereCondition() != null) {
+ buffer.append(" DELETE WHERE ");
+ mergeUpdate.getDeleteWhereCondition().accept(expressionDeParser);
+ }
+ }
+
+ @Override
+ public void visit(MergeInsert mergeInsert) {
+ buffer.append(" WHEN NOT MATCHED");
+ if (mergeInsert.getAndPredicate() != null) {
+ buffer.append(" AND ");
+ mergeInsert.getAndPredicate().accept(expressionDeParser);
+ }
+ buffer.append(" THEN INSERT ");
+ if (mergeInsert.getColumns() != null) {
+ mergeInsert.getColumns().accept(expressionDeParser);
+ }
+ buffer.append(" VALUES ");
+ mergeInsert.getValues().accept(expressionDeParser);
+
+ if (mergeInsert.getWhereCondition() != null) {
+ buffer.append(" WHERE ");
+ mergeInsert.getWhereCondition().accept(expressionDeParser);
+ }
+ }
+
+}
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java
index a6b718baf..d2a53028c 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java
@@ -210,6 +210,9 @@ public void visit(PlainSelect plainSelect) {
if (plainSelect.getFromItem() != null) {
buffer.append(" FROM ");
+ if (plainSelect.isUsingOnly()) {
+ buffer.append("ONLY ");
+ }
plainSelect.getFromItem().accept(this);
if (plainSelect.getFromItem() instanceof Table) {
@@ -317,6 +320,12 @@ public void visit(PlainSelect plainSelect) {
if (plainSelect.getForXmlPath() != null) {
buffer.append(" FOR XML PATH(").append(plainSelect.getForXmlPath()).append(")");
}
+ if (plainSelect.getIntoTempTable() != null) {
+ buffer.append(" INTO TEMP ").append(plainSelect.getIntoTempTable());
+ }
+ if (plainSelect.isUseWithNoLog()) {
+ buffer.append(" WITH NO LOG");
+ }
}
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java
index f5326ae41..f9d314642 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java
@@ -9,8 +9,6 @@
*/
package net.sf.jsqlparser.util.deparser;
-import java.util.Iterator;
-import java.util.List;
import java.util.stream.Collectors;
import net.sf.jsqlparser.statement.Block;
import net.sf.jsqlparser.statement.Commit;
@@ -50,12 +48,9 @@
import net.sf.jsqlparser.statement.execute.Execute;
import net.sf.jsqlparser.statement.grant.Grant;
import net.sf.jsqlparser.statement.insert.Insert;
-import net.sf.jsqlparser.statement.merge.Merge;
-import net.sf.jsqlparser.statement.merge.MergeInsert;
-import net.sf.jsqlparser.statement.merge.MergeUpdate;
+import net.sf.jsqlparser.statement.merge.*;
import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement;
import net.sf.jsqlparser.statement.select.Select;
-import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.statement.show.ShowIndexStatement;
import net.sf.jsqlparser.statement.show.ShowTablesStatement;
import net.sf.jsqlparser.statement.truncate.Truncate;
@@ -203,83 +198,7 @@ public void visit(ResetStatement reset) {
@SuppressWarnings({"PMD.CyclomaticComplexity"})
@Override
public void visit(Merge merge) {
- List withItemsList = merge.getWithItemsList();
- if (withItemsList != null && !withItemsList.isEmpty()) {
- buffer.append("WITH ");
- for (Iterator iter = withItemsList.iterator(); iter.hasNext();) {
- iter.next().accept(expressionDeParser);
- if (iter.hasNext()) {
- buffer.append(",");
- }
- buffer.append(" ");
- }
- }
-
- buffer.append("MERGE ");
- if (merge.getOracleHint() != null) {
- buffer.append(merge.getOracleHint()).append(" ");
- }
- buffer.append("INTO ");
- merge.getTable().accept(selectDeParser);
-
- buffer.append(" USING ");
- merge.getFromItem().accept(selectDeParser);
-
- buffer.append(" ON ");
- merge.getOnCondition().accept(expressionDeParser);
-
- MergeInsert mergeInsert = merge.getMergeInsert();
- MergeUpdate mergeUpdate = merge.getMergeUpdate();
- if (merge.isInsertFirst() && mergeInsert != null) {
- deparseMergeInsert(mergeInsert);
- }
-
- if (mergeUpdate != null) {
- buffer.append(" WHEN MATCHED");
- if (mergeUpdate.getAndPredicate() != null) {
- buffer.append(" AND ");
- mergeUpdate.getAndPredicate().accept(expressionDeParser);
- }
- buffer.append(" THEN UPDATE SET ");
- deparseUpdateSets(mergeUpdate.getUpdateSets(), buffer, expressionDeParser);
-
- if (mergeUpdate.getWhereCondition() != null) {
- buffer.append(" WHERE ");
- mergeUpdate.getWhereCondition().accept(expressionDeParser);
- }
-
- if (mergeUpdate.getDeleteWhereCondition() != null) {
- buffer.append(" DELETE WHERE ");
- mergeUpdate.getDeleteWhereCondition().accept(expressionDeParser);
- }
- }
-
- if (!merge.isInsertFirst() && mergeInsert != null) {
- deparseMergeInsert(mergeInsert);
- }
-
- if (merge.getOutputClause() != null) {
- merge.getOutputClause().appendTo(buffer);
- }
- }
-
- private void deparseMergeInsert(MergeInsert mergeInsert) {
- buffer.append(" WHEN NOT MATCHED");
- if (mergeInsert.getAndPredicate() != null) {
- buffer.append(" AND ");
- mergeInsert.getAndPredicate().accept(expressionDeParser);
- }
- buffer.append(" THEN INSERT ");
- if (mergeInsert.getColumns() != null) {
- mergeInsert.getColumns().accept(expressionDeParser);
- }
- buffer.append(" VALUES ");
- mergeInsert.getValues().accept(expressionDeParser);
-
- if (mergeInsert.getWhereCondition() != null) {
- buffer.append(" WHERE ");
- mergeInsert.getWhereCondition().accept(expressionDeParser);
- }
+ new MergeDeParser(expressionDeParser, selectDeParser, buffer).deParse(merge);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java
index 0b2dd5711..e9e0765cc 100644
--- a/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java
+++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/MergeValidator.java
@@ -10,14 +10,14 @@
package net.sf.jsqlparser.util.validation.validator;
import net.sf.jsqlparser.parser.feature.Feature;
-import net.sf.jsqlparser.statement.merge.Merge;
+import net.sf.jsqlparser.statement.merge.*;
import net.sf.jsqlparser.statement.update.UpdateSet;
import net.sf.jsqlparser.util.validation.ValidationCapability;
/**
* @author gitmotte
*/
-public class MergeValidator extends AbstractValidator {
+public class MergeValidator extends AbstractValidator implements MergeOperationVisitor {
@Override
@@ -26,20 +26,32 @@ public void validate(Merge merge) {
validateFeature(c, Feature.merge);
}
validateOptionalExpression(merge.getOnCondition());
- // validateOptionalExpression(merge.getFromItem());
- if (merge.getMergeInsert() != null) {
- validateOptionalExpressions(merge.getMergeInsert().getColumns());
- validateOptionalExpressions(merge.getMergeInsert().getValues());
- }
- if (merge.getMergeUpdate() != null) {
- for (UpdateSet updateSet : merge.getMergeUpdate().getUpdateSets()) {
- validateOptionalExpressions(updateSet.getColumns());
- validateOptionalExpressions(updateSet.getValues());
- }
- validateOptionalExpression(merge.getMergeUpdate().getDeleteWhereCondition());
- validateOptionalExpression(merge.getMergeUpdate().getWhereCondition());
+ if (merge.getOperations() != null) {
+ merge.getOperations().forEach(operation -> operation.accept(this));
}
validateOptionalFromItems(merge.getFromItem());
}
+ @Override
+ public void visit(MergeDelete mergeDelete) {
+ validateOptionalExpression(mergeDelete.getAndPredicate());
+ }
+
+ @Override
+ public void visit(MergeUpdate mergeUpdate) {
+ validateOptionalExpression(mergeUpdate.getAndPredicate());
+ for (UpdateSet updateSet : mergeUpdate.getUpdateSets()) {
+ validateOptionalExpressions(updateSet.getColumns());
+ validateOptionalExpressions(updateSet.getValues());
+ }
+ validateOptionalExpression(mergeUpdate.getDeleteWhereCondition());
+ validateOptionalExpression(mergeUpdate.getWhereCondition());
+ }
+
+ @Override
+ public void visit(MergeInsert mergeInsert) {
+ validateOptionalExpression(mergeInsert.getAndPredicate());
+ validateOptionalExpressions(mergeInsert.getColumns());
+ validateOptionalExpressions(mergeInsert.getValues());
+ }
}
diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
index 3144b3103..880804cd5 100644
--- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
+++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
@@ -391,6 +391,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
|
|
|
+|
|
|
|
@@ -426,6 +427,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
("CURRENT" ( "_" | (" ")+ ) "TIMESTAMP")
| ("CURRENT" ( "_" | (" ")+ ) "TIME")
| ("CURRENT" ( "_" | (" ")+ ) "DATE")
+ | ("CURRENT" ( "_" | (" ")+ ) "TIMEZONE")
) ( "()" )?>
|
|
@@ -460,6 +462,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
|
|
|
+|
|
|
|
@@ -528,6 +531,8 @@ SPECIAL_TOKEN:
TOKEN:
{
+
+|
()*>
| <#LETTER:
| | [ "$" , "#", "_" ] // Not SQL:2016 compliant!
@@ -1701,8 +1706,7 @@ Statement Merge( List with ) : {
Table table;
FromItem fromItem;
Expression condition;
- MergeUpdate update;
- MergeInsert insert;
+ List operations;
OutputClause outputClause;
}
{
@@ -1710,39 +1714,60 @@ Statement Merge( List with ) : {
fromItem = FromItem() { merge.setFromItem(fromItem); }
condition = Expression() { merge.setOnCondition(condition); }
- [
- ( LOOKAHEAD(2) update = MergeUpdateClause() { merge.setMergeUpdate(update); }
- [ insert = MergeInsertClause() { merge.setMergeInsert(insert); } ]
- | insert = MergeInsertClause() { merge.withMergeInsert(insert).withInsertFirst(true); }
- [ update = MergeUpdateClause() { merge.setMergeUpdate(update); } ]
- )
- ]
+ operations = MergeOperations() { merge.setOperations(operations); }
[ outputClause = OutputClause() { merge.setOutputClause(outputClause); } ]
{ return merge.withWithItemsList(with); }
}
-MergeUpdate MergeUpdateClause() : {
- MergeUpdate mu = new MergeUpdate();
- List updateSets;
- Expression predicate;
- Expression condition;
+List MergeOperations() : {
+ List operationsList = new ArrayList();
+ MergeOperation operation;
+}
+{
+ (
+ LOOKAHEAD(2) operation = MergeWhenMatched() { operationsList.add(operation); }
+ |
+ operation = MergeWhenNotMatched() { operationsList.add(operation); }
+ )*
+ { return operationsList; }
+}
+
+MergeOperation MergeWhenMatched() : {
+ Expression predicate = null;
+ MergeOperation operation;
}
{
- [ predicate = Expression() { mu.setAndPredicate(predicate); } ]
-
-
- updateSets = UpdateSets() { mu.setUpdateSets(updateSets); }
+ [ predicate = Expression() ]
+
+ (
+ operation = MergeDeleteClause(predicate) | operation = MergeUpdateClause(predicate)
+ )
+ { return operation; }
+}
+
+MergeOperation MergeDeleteClause(Expression predicate) : {
+ MergeDelete md = new MergeDelete().withAndPredicate(predicate);
+}
+{
+ { return md; }
+}
+MergeOperation MergeUpdateClause(Expression predicate) : {
+ MergeUpdate mu = new MergeUpdate().withAndPredicate(predicate);
+ List updateSets;
+ Expression condition;
+}
+{
+ updateSets = UpdateSets() { mu.setUpdateSets(updateSets); }
[ condition = Expression() { mu.setWhereCondition(condition); }]
[ condition = Expression() { mu.setDeleteWhereCondition(condition); } ]
-
{ return mu; }
}
-MergeInsert MergeInsertClause() : {
+MergeOperation MergeWhenNotMatched() : {
MergeInsert mi = new MergeInsert();
Expression predicate;
ExpressionList columns;
@@ -1817,7 +1842,7 @@ String RelObjectNameWithoutValue() :
{ Token tk = null; }
{
( tk= | tk= | tk= | tk= | tk= | tk= | tk=
- | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="ALGORITHM" | tk="ALTER" | tk="ANALYZE" | tk="APPLY" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="AT" | tk="AUTHORIZATION" | tk="AUTO" | tk="BEGIN" | tk="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | tk="BROWSE" | tk="BUFFERS" | tk="BY" | tk="BYTE" | tk="BYTES" | tk="CACHE" | tk="CALL" | tk="CASCADE" | tk="CASE" | tk="CAST" | tk="CHANGE" | tk="CHANGES" | tk="CHAR" | tk="CHARACTER" | tk="CHECKPOINT" | tk="CLOSE" | tk="COLLATE" | tk="COLUMN" | tk="COLUMNS" | tk="COMMENT" | tk="COMMIT" | tk="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | tk="DATABASE" | tk="DDL" | tk="DECLARE" | tk="DEFAULT" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DOMAIN" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="ELEMENTS" | tk="EMIT" | tk="ENABLE" | tk="END" | tk="ESCAPE" | tk="EXCLUDE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXPLICIT" | tk="EXTENDED" | tk="EXTRACT" | tk="FALSE" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAXVALUE" | tk="MEMBER" | tk="MERGE" | tk="MINVALUE" | tk="MODIFY" | tk="MOVEMENT" | tk="NEXT" | tk="NO" | tk="NOCACHE" | tk="NOKEEP" | tk="NOLOCK" | tk="NOMAXVALUE" | tk="NOMINVALUE" | tk="NOORDER" | tk="NOTHING" | tk="NOTNULL" | tk="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="OVER" | tk="OVERLAPS" | tk="PARALLEL" | tk="PARENT" | tk="PARTITION" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PRECEDING" | tk="PRECISION" | tk="PRIMARY" | tk="PRIOR" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="RAW" | tk="READ" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REFRESH" | tk="REGISTER" | tk="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROLLUP" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="STORED" | tk="STRING" | tk="SUSPEND" | tk="SWITCH" | tk="SYNONYM" | tk="SYSTEM" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TO" | tk="TRIGGER" | tk="TRUE" | tk="TRUNCATE" | tk="TUMBLING" | tk="TYPE" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VERBOSE" | tk="VIEW" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLDATA" | tk="XMLSCHEMA" | tk="XMLTEXT" | tk="XSINIL" | tk="YAML" | tk="YES" | tk="ZONE" )
+ | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="ALGORITHM" | tk="ALTER" | tk="ANALYZE" | tk="APPLY" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="AT" | tk="AUTHORIZATION" | tk="AUTO" | tk="BEGIN" | tk="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | tk="BROWSE" | tk="BUFFERS" | tk="BY" | tk="BYTE" | tk="BYTES" | tk="CACHE" | tk="CALL" | tk="CASCADE" | tk="CASE" | tk="CAST" | tk="CHANGE" | tk="CHANGES" | tk="CHAR" | tk="CHARACTER" | tk="CHECKPOINT" | tk="CLOSE" | tk="COLLATE" | tk="COLUMN" | tk="COLUMNS" | tk="COMMENT" | tk="COMMIT" | tk="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | tk="DATABASE" | tk="DDL" | tk="DECLARE" | tk="DEFAULT" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DOMAIN" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="ELEMENTS" | tk="EMIT" | tk="ENABLE" | tk="END" | tk="ESCAPE" | tk="EXCLUDE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXPLICIT" | tk="EXTENDED" | tk="EXTRACT" | tk="FALSE" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAXVALUE" | tk="MEMBER" | tk="MERGE" | tk="MINVALUE" | tk="MODIFY" | tk="MOVEMENT" | tk="NEXT" | tk="NO" | tk="NOCACHE" | tk="NOKEEP" | tk="NOLOCK" | tk="NOMAXVALUE" | tk="NOMINVALUE" | tk="NOORDER" | tk="NOTHING" | tk="NOTNULL" | tk="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="OVER" | tk="OVERLAPS" | tk="PARALLEL" | tk="PARENT" | tk="PARTITION" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PRECEDING" | tk="PRECISION" | tk="PRIMARY" | tk="PRIOR" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="RAW" | tk="READ" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REFRESH" | tk="REGISTER" | tk="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROLLUP" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SECURE" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="STORED" | tk="STRING" | tk="SUSPEND" | tk="SWITCH" | tk="SYNONYM" | tk="SYSTEM" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TIMEZONE" | tk="TO" | tk="TRIGGER" | tk="TRUE" | tk="TRUNCATE" | tk="TUMBLING" | tk="TYPE" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VERBOSE" | tk="VIEW" | tk="VOLATILE" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLDATA" | tk="XMLSCHEMA" | tk="XMLTEXT" | tk="XSINIL" | tk="YAML" | tk="YES" | tk="ZONE" )
{ return tk.image; }
}
@@ -2217,6 +2242,7 @@ PlainSelect PlainSelect() #PlainSelect:
boolean noWait = false;
String windowName = null;
WindowDefinition winDef;
+ Table intoTempTable = null;
}
{
@@ -2257,6 +2283,10 @@ PlainSelect PlainSelect() #PlainSelect:
[ lateralViews=LateralViews() ]
[ LOOKAHEAD(2) joins=JoinsList() ]
]
+ [ LOOKAHEAD(3) { plainSelect.setUsingOnly(true); } fromItem=FromItem()
+ [ lateralViews=LateralViews() ]
+ [ LOOKAHEAD(2) joins=JoinsList() ]
+ ]
// Clickhouse FINAL as shown at https://clickhouse.com/docs/en/operations/settings/settings#final
[ LOOKAHEAD(2) { plainSelect.setUsingFinal(true); } ]
@@ -2298,6 +2328,8 @@ PlainSelect PlainSelect() #PlainSelect:
| { plainSelect.setSkipLocked(true); }) ]
]
[ LOOKAHEAD() optimize = OptimizeFor() { plainSelect.setOptimizeFor(optimize); } ]
+ [ LOOKAHEAD(3) intoTempTable = Table() { plainSelect.setIntoTempTable(intoTempTable);} ]
+ [ LOOKAHEAD(3) { plainSelect.setUseWithNoLog(true); } ]
{
plainSelect.setSelectItems(selectItems);
plainSelect.setFromItem(fromItem);
@@ -3097,27 +3129,20 @@ OrderByElement OrderByElement():
}
}
-JdbcParameter SimpleJdbcParameter() : {
+JdbcParameter JdbcParameter() : {
+ Token tk;
JdbcParameter retval;
}
{
- "?" { retval = new JdbcParameter(++jdbcParameterIndex, false); }
- [ LOOKAHEAD(2) token = { retval.setUseFixedIndex(true); retval.setIndex(Integer.valueOf(token.image)); } ]
- {
- return retval;
- }
-}
+ ( tk="?" | tk= )
+ { retval = new JdbcParameter(++jdbcParameterIndex, false, tk.image); }
-JdbcNamedParameter SimpleJdbcNamedParameter() : {
- String name;
-}
-{
- ":" name = RelObjectNameExt()
- {
- return new JdbcNamedParameter(token.image);
- }
+ [ LOOKAHEAD(2) token = { retval.setUseFixedIndex(true); retval.setIndex(Integer.valueOf(token.image)); } ]
+
+ { return retval; }
}
+
Limit LimitWithOffset() #LimitWithOffset:
{
Limit limit = new Limit();
@@ -3294,7 +3319,7 @@ Top Top():
(
token= { top.setExpression(new LongValue(token.image)); }
|
- jdbc = SimpleJdbcParameter() { top.setExpression(jdbc); }
+ jdbc = JdbcParameter() { top.setExpression(jdbc); }
/*"?" { top.setExpression(new JdbcParameter(++jdbcParameterIndex, false)); } [ LOOKAHEAD(2) token = { ((JdbcParameter)(top.getExpression())).setUseFixedIndex(true); ((JdbcParameter)(top.getExpression())).setIndex(Integer.valueOf(token.image)); } ]*/
|
":" { top.setExpression(new JdbcNamedParameter()); } [ LOOKAHEAD(2) token = { ((JdbcNamedParameter)top.getExpression()).setName(token.image); } ]
@@ -3325,7 +3350,7 @@ Skip Skip():
(
token= { skip.setRowCount(Long.parseLong(token.image)); }
| token= { skip.setVariable(token.image); }
- | jdbc = SimpleJdbcParameter() { skip.setJdbcParameter(jdbc); }
+ | jdbc = JdbcParameter() { skip.setJdbcParameter(jdbc); }
/* "?" { skip.setJdbcParameter(new JdbcParameter(++jdbcParameterIndex, false)); } [ LOOKAHEAD(2) token = { skip.getJdbcParameter().setUseFixedIndex(true); skip.getJdbcParameter().setIndex(Integer.valueOf(token.image)); } ] */
)
{
@@ -3367,7 +3392,7 @@ First First():
|
token= { first.setVariable(token.image); }
|
- jdbc = SimpleJdbcParameter() { first.setJdbcParameter(jdbc); }
+ jdbc = JdbcParameter() { first.setJdbcParameter(jdbc); }
)
{
return first;
@@ -3642,8 +3667,6 @@ Expression Between(Expression leftExpression) :
(
LOOKAHEAD( 3 ) betweenExpressionStart = ParenthesedSelect()
|
- LOOKAHEAD( SimpleFunction() ) betweenExpressionStart = SimpleFunction()
- |
LOOKAHEAD( RegularCondition() ) betweenExpressionStart = RegularCondition()
|
betweenExpressionStart = SimpleExpression()
@@ -3653,8 +3676,6 @@ Expression Between(Expression leftExpression) :
(
LOOKAHEAD( 3 ) betweenExpressionEnd = ParenthesedSelect()
|
- LOOKAHEAD( SimpleFunction() ) betweenExpressionEnd = SimpleFunction()
- |
LOOKAHEAD( RegularCondition() ) betweenExpressionEnd = RegularCondition()
|
betweenExpressionEnd = SimpleExpression()
@@ -4206,9 +4227,9 @@ Expression PrimaryExpression() #PrimaryExpression:
| LOOKAHEAD(3, {!interrupted}) retval=CaseWhenExpression()
- | LOOKAHEAD(3) retval = SimpleJdbcParameter()
+ | retval = JdbcParameter()
- | LOOKAHEAD(2) retval=JdbcNamedParameter()
+ | LOOKAHEAD(2) retval =JdbcNamedParameter()
| LOOKAHEAD(3) retval=UserVariable()
@@ -4332,6 +4353,7 @@ Expression PrimaryExpression() #PrimaryExpression:
if (not) {
retval = new NotExpression(retval, exclamationMarkNot);
}
+ linkAST(retval, jjtThis);
return retval;
}
}
@@ -4728,7 +4750,7 @@ IntervalExpression IntervalExpression() : {
{
{ interval = new IntervalExpression(); }
- ["-" {signed=true;}] (token= | token= | token= | LOOKAHEAD(SimpleJdbcParameter()) expr = SimpleJdbcParameter() | expr = JdbcNamedParameter() | LOOKAHEAD(Function()) expr = Function() | expr = Column())
+ ["-" {signed=true;}] (token= | token= | token= | LOOKAHEAD(JdbcParameter()) expr = JdbcParameter() | expr = JdbcNamedParameter() | LOOKAHEAD(Function()) expr = Function() | expr = Column())
{
if (expr != null) {
if (signed) expr = new SignedExpression('-', expr);
@@ -5069,9 +5091,9 @@ FullTextSearch FullTextSearch() : {
(
againstValue= { fs.setAgainstValue(new StringValue(againstValue.image)); }
|
- jdbcParameter=SimpleJdbcParameter() { fs.setAgainstValue( jdbcParameter ); }
+ jdbcParameter=JdbcParameter() { fs.setAgainstValue( jdbcParameter ); }
|
- jdbcNamedParameter=SimpleJdbcNamedParameter() { fs.setAgainstValue( jdbcNamedParameter ); }
+ jdbcNamedParameter=JdbcNamedParameter() { fs.setAgainstValue( jdbcNamedParameter ); }
)
[
(
@@ -5136,8 +5158,11 @@ Function SpecialStringFunctionWithNamedParameters() :
// useful for parsing nested functions fast
Function SimpleFunction():
{
+ Function function = new Function();
List name;
Expression expr=null;
+ Expression attributeExpression = null;
+ Column attributeColumn = null;
}
{
name = RelObjectNameList()
@@ -5159,11 +5184,23 @@ Function SimpleFunction():
]
")"
{
- Function function = new Function().withName(name);
+ function.setName(name);
if (expr!=null) {
function.setParameters(expr);
}
+ }
+
+ [ "." (
+ // tricky lookahead since we do need to support the following constructs
+ // schema.f1().f2() - Function with Function Column
+ // schema.f1().f2.f3 - Function with Attribute Column
+ LOOKAHEAD( Function() ) attributeExpression=Function() { function.setAttribute(attributeExpression); }
+ |
+ attributeColumn=Column() { function.setAttribute(attributeColumn); }
+ )
+ ]
+ {
return function;
}
}
@@ -5714,6 +5751,7 @@ ColDataType ColDataType():
| tk=
| tk=
| tk=
+ | tk=
) { schema = tk.image; }
[ "." type = RelObjectName() { schema += "." + type; } ]
{ colDataType.setDataType(schema); }
@@ -5784,9 +5822,11 @@ CreateView CreateView(boolean isUsingOrReplace):
{ createView.setForce(ForceOption.NO_FORCE); }
| { createView.setForce(ForceOption.FORCE); }
]
+ [ { createView.setSecure(true);} ]
[
{ createView.setTemporary(TemporaryOption.TEMP); }
| { createView.setTemporary(TemporaryOption.TEMPORARY); }
+ | { createView.setTemporary(TemporaryOption.VOLATILE); }
]
[ { createView.setMaterialized(true);} ]
view=Table() { createView.setView(view); }
@@ -6117,9 +6157,9 @@ AlterExpression.ColumnDataType AlterExpressionColumnDataType():
List parameter = null;
}
{
- columnName = RelObjectName()
+ columnName = RelObjectName() { columnSpecs = new ArrayList(); }
( LOOKAHEAD(2) { withType = true; } )?
- dataType = ColDataType() { columnSpecs = new ArrayList(); }
+ ( LOOKAHEAD(2) dataType = ColDataType() )?
( parameter = CreateParameter() { columnSpecs.addAll(parameter); } )*
{
return new AlterExpression.ColumnDataType(columnName, withType, dataType, columnSpecs);
@@ -6228,6 +6268,7 @@ AlterExpression AlterExpression():
AlterExpression.ColumnDropNotNull alterExpressionColumnDropNotNull = null;
AlterExpression.ColumnDropDefault alterExpressionColumnDropDefault = null;
ReferentialAction.Action action = null;
+ String truncatePartitionName = null;
// for captureRest()
List tokens = new LinkedList();
@@ -6263,9 +6304,13 @@ AlterExpression AlterExpression():
[ index = IndexWithComment(index) { alterExp.setIndex(index); } ]
)
|
+ LOOKAHEAD(2) (
+ sk3=RelObjectName() tk= { alterExp.withColumnName(sk3).withCommentText(tk.image); }
+ )
+ |
LOOKAHEAD(3) (
( LOOKAHEAD(2) { alterExp.hasColumn(true); } )?
-
+ [ { alterExp.setUseIfNotExists(true); } ]
(
LOOKAHEAD(4) (
"("
@@ -6405,8 +6450,6 @@ AlterExpression AlterExpression():
)
)
)
- |
- ( sk3=RelObjectName() tk= { alterExp.withColumnName(sk3).withCommentText(tk.image); } )
)
)
|
@@ -6519,6 +6562,8 @@ AlterExpression AlterExpression():
}
)
|
+ LOOKAHEAD(2) { alterExp.setOperation(AlterOperation.TRUNCATE_PARTITION); } truncatePartitionName = RelObjectName() { alterExp.setTruncatePartitionName(truncatePartitionName); }
+ |
tokens = captureRest() {
alterExp.setOperation(AlterOperation.UNSPECIFIC);
StringBuilder optionalSpecifier = new StringBuilder();
@@ -6595,14 +6640,10 @@ Alter AlterTable():
{
[ { alter.setUseOnly(true); } ]
- [ LOOKAHEAD(2) { usingIfExists = true; } ]
-
+ [ LOOKAHEAD(2) { alter.setUseTableIfExists(true); } ]
table=Table() { alter.setTable(table); }
- alterExp=AlterExpression() { if (usingIfExists)
- alter.addAlterExpression( alterExp.withUsingIfExists(true) );
- else
- alter.addAlterExpression(alterExp); }
+ alterExp=AlterExpression() { alter.addAlterExpression(alterExp); }
("," alterExp=AlterExpression() { alter.addAlterExpression(alterExp); } )*
diff --git a/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java b/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java
index 8f1894663..da049cdad 100644
--- a/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java
+++ b/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java
@@ -24,4 +24,10 @@ public void testCastToRowConstructorIssue1267() throws JSQLParserException {
TestUtils.assertExpressionCanBeParsedAndDeparsed("CAST(ROW(dataid, value, calcMark) AS ROW(datapointid CHAR, value CHAR, calcMark CHAR))", true);
TestUtils.assertExpressionCanBeParsedAndDeparsed("CAST(ROW(dataid, value, calcMark) AS testcol)", true);
}
+
+ @Test
+ void testDataKeywordIssue1969() throws Exception {
+ String sqlStr = "SELECT * FROM myschema.myfunction('test'::data.text_not_null)";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
}
diff --git a/src/test/java/net/sf/jsqlparser/expression/DoubleValueTest.java b/src/test/java/net/sf/jsqlparser/expression/DoubleValueTest.java
new file mode 100644
index 000000000..1ef676c6d
--- /dev/null
+++ b/src/test/java/net/sf/jsqlparser/expression/DoubleValueTest.java
@@ -0,0 +1,31 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2019 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.expression;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class DoubleValueTest {
+
+ @Test
+ public void testNullValue() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ new DoubleValue(null);
+ });
+ }
+
+ @Test
+ public void testEmptyValue() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ new DoubleValue("");
+ });
+ }
+}
diff --git a/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java b/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java
index fa301142a..14ae67fed 100644
--- a/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java
+++ b/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java
@@ -15,6 +15,7 @@
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
+import net.sf.jsqlparser.statement.select.AllTableColumns;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectVisitorAdapter;
@@ -241,4 +242,30 @@ public void testRowConstructor() throws JSQLParserException {
"CAST(ROW(dataid, value, calcMark) AS ROW(datapointid CHAR, value CHAR, calcMark CHAR))")
.accept(adapter);
}
+
+ @Test
+ public void testAllTableColumns() throws JSQLParserException {
+ PlainSelect plainSelect = (PlainSelect) CCJSqlParserUtil.parse("select a.* from foo a");
+ final AllTableColumns[] holder = new AllTableColumns[1];
+ Expression from = plainSelect.getSelectItems().get(0).getExpression();
+ from.accept(new ExpressionVisitorAdapter() {
+
+ @Override
+ public void visit(AllTableColumns all) {
+ holder[0] = all;
+ }
+ });
+
+ assertNotNull(holder[0]);
+ assertEquals("a.*", holder[0].toString());
+ }
+
+ @Test
+ public void testAnalyticExpressionWithPartialWindowElement() throws JSQLParserException {
+ ExpressionVisitorAdapter adapter = new ExpressionVisitorAdapter();
+ Expression expression = CCJSqlParserUtil.parseExpression(
+ "SUM(\"Spent\") OVER (PARTITION BY \"ID\" ORDER BY \"Name\" ASC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)");
+
+ expression.accept(adapter);
+ }
}
diff --git a/src/test/java/net/sf/jsqlparser/expression/FunctionTest.java b/src/test/java/net/sf/jsqlparser/expression/FunctionTest.java
index 28ce1d956..d5e132282 100644
--- a/src/test/java/net/sf/jsqlparser/expression/FunctionTest.java
+++ b/src/test/java/net/sf/jsqlparser/expression/FunctionTest.java
@@ -38,4 +38,15 @@ void testCallFunction() throws JSQLParserException {
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}
+ @Test
+ void testChainedFunctions() throws JSQLParserException {
+ String sqlStr =
+ "select f1(a1=1).f2 = 1";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ sqlStr =
+ "select f1(a1=1).f2(b).f2 = 1";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
}
diff --git a/src/test/java/net/sf/jsqlparser/expression/JdbcNamedParameterTest.java b/src/test/java/net/sf/jsqlparser/expression/JdbcNamedParameterTest.java
index f2a4bfe70..2f7c46db7 100644
--- a/src/test/java/net/sf/jsqlparser/expression/JdbcNamedParameterTest.java
+++ b/src/test/java/net/sf/jsqlparser/expression/JdbcNamedParameterTest.java
@@ -11,8 +11,10 @@
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd;
+import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.test.TestUtils;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -39,4 +41,16 @@ void testIssue1785() throws JSQLParserException {
+ "where owner = &myowner";
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}
+
+ @Test
+ void testIssue1970() throws JSQLParserException {
+ String sqlStr = "SELECT a from tbl where col = $2";
+ PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ EqualsTo where = (EqualsTo) select.getWhere();
+ Assertions.assertInstanceOf(JdbcParameter.class, where.getRightExpression());
+
+ JdbcParameter p = (JdbcParameter) where.getRightExpression();
+ Assertions.assertEquals(2, p.getIndex());
+ }
}
diff --git a/src/test/java/net/sf/jsqlparser/expression/LongValueTest.java b/src/test/java/net/sf/jsqlparser/expression/LongValueTest.java
index 91905614f..ab8119392 100644
--- a/src/test/java/net/sf/jsqlparser/expression/LongValueTest.java
+++ b/src/test/java/net/sf/jsqlparser/expression/LongValueTest.java
@@ -11,6 +11,7 @@
import java.math.BigInteger;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
@@ -39,8 +40,22 @@ public void testLargeNumber() {
value.getValue();
fail("should not work");
} catch (Exception e) {
- //expected to fail
+ // expected to fail
}
assertEquals(new BigInteger(largeNumber), value.getBigIntegerValue());
}
+
+ @Test
+ public void testNullStringValue() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ new LongValue((String) null);
+ });
+ }
+
+ @Test
+ public void testEmptyStringValue() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ new LongValue("");
+ });
+ }
}
diff --git a/src/test/java/net/sf/jsqlparser/expression/TimeValueTest.java b/src/test/java/net/sf/jsqlparser/expression/TimeValueTest.java
new file mode 100644
index 000000000..c931dbcd8
--- /dev/null
+++ b/src/test/java/net/sf/jsqlparser/expression/TimeValueTest.java
@@ -0,0 +1,31 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2019 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.expression;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class TimeValueTest {
+
+ @Test
+ public void testNullValue() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ new TimeValue(null);
+ });
+ }
+
+ @Test
+ public void testEmptyValue() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ new TimeValue("");
+ });
+ }
+}
diff --git a/src/test/java/net/sf/jsqlparser/expression/operators/relational/BetweenTest.java b/src/test/java/net/sf/jsqlparser/expression/operators/relational/BetweenTest.java
new file mode 100644
index 000000000..ebe632c17
--- /dev/null
+++ b/src/test/java/net/sf/jsqlparser/expression/operators/relational/BetweenTest.java
@@ -0,0 +1,24 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2024 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.expression.operators.relational;
+
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.test.TestUtils;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class BetweenTest {
+ @Test
+ void testBetweenWithAdditionIssue1948() throws JSQLParserException {
+ String sqlStr = "select col FROM tbl WHERE start_time BETWEEN 1706024185 AND MyFunc() - 734400";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+}
diff --git a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java
index 1ec07b531..b7caf3a64 100644
--- a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java
+++ b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java
@@ -12,6 +12,7 @@
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -507,4 +508,10 @@ void testUnbalancedPosition() {
" from tab";
assertEquals(1122, CCJSqlParserUtil.getUnbalancedPosition(sqlStr));
}
+
+ @Test
+ void testParseEmpty() throws JSQLParserException {
+ assertNull(CCJSqlParserUtil.parse(""));
+ assertNull(CCJSqlParserUtil.parse((String) null));
+ }
}
diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java
index ab07437a6..24995bc3e 100644
--- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java
+++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java
@@ -360,6 +360,34 @@ public void testAlterTableModifyColumn2() throws JSQLParserException {
assertFalse(alterExpression.hasColumn());
}
+ @Test
+ public void testAlterTableModifyColumn3() throws JSQLParserException {
+ Alter alter =
+ (Alter) CCJSqlParserUtil.parse("ALTER TABLE mytable modify col1 NULL");
+ AlterExpression alterExpression = alter.getAlterExpressions().get(0);
+
+ // COLUMN keyword DOES NOT appear in deparsed statement, modify becomes all caps
+ assertStatementCanBeDeparsedAs(alter, "ALTER TABLE mytable MODIFY col1 NULL");
+
+ assertEquals(AlterOperation.MODIFY, alterExpression.getOperation());
+
+ assertFalse(alterExpression.hasColumn());
+ }
+
+ @Test
+ public void testAlterTableModifyColumn4() throws JSQLParserException {
+ Alter alter =
+ (Alter) CCJSqlParserUtil.parse("ALTER TABLE mytable modify col1 DEFAULT 0");
+ AlterExpression alterExpression = alter.getAlterExpressions().get(0);
+
+ // COLUMN keyword DOES NOT appear in deparsed statement, modify becomes all caps
+ assertStatementCanBeDeparsedAs(alter, "ALTER TABLE mytable MODIFY col1 DEFAULT 0");
+
+ assertEquals(AlterOperation.MODIFY, alterExpression.getOperation());
+
+ assertFalse(alterExpression.hasColumn());
+ }
+
@Test
public void testAlterTableAlterColumn() throws JSQLParserException {
// http://www.postgresqltutorial.com/postgresql-change-column-type/
@@ -981,4 +1009,18 @@ public void testAlterColumnSetCommitTimestamp1() throws JSQLParserException {
assertEquals("UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=true)",
type.toString());
}
+
+ @Test
+ public void testIssue1890() throws JSQLParserException {
+ String stmt =
+ "ALTER TABLE xdmiddle.ft_mid_sop_sms_send_list_daily TRUNCATE PARTITION sum_date";
+ assertSqlCanBeParsedAndDeparsed(stmt);
+ }
+
+ @Test
+ public void testIssue1875() throws JSQLParserException {
+ String stmt =
+ "ALTER TABLE IF EXISTS usercenter.dict_surgeries ADD COLUMN IF NOT EXISTS operation_grade_id int8 NULL";
+ assertSqlCanBeParsedAndDeparsed(stmt);
+ }
}
diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java
index 240a22fc3..328b8ecca 100644
--- a/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java
+++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java
@@ -119,6 +119,16 @@ public void testCreateForceView3() throws JSQLParserException {
"CREATE OR REPLACE NO FORCE VIEW view1 AS SELECT a, b FROM testtab");
}
+ @Test
+ public void testCreateSecureView() throws JSQLParserException {
+ assertSqlCanBeParsedAndDeparsed("CREATE SECURE VIEW myview AS SELECT * FROM mytable");
+ }
+
+ @Test
+ public void testCreateVolatileView() throws JSQLParserException {
+ assertSqlCanBeParsedAndDeparsed("CREATE VOLATILE VIEW myview AS SELECT * FROM mytable");
+ }
+
@Test
public void testCreateTemporaryViewIssue604() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("CREATE TEMPORARY VIEW myview AS SELECT * FROM mytable");
diff --git a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java
index edf0baa92..678b8e7f8 100644
--- a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java
+++ b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java
@@ -12,12 +12,20 @@
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
-import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Stream;
import static net.sf.jsqlparser.test.TestUtils.assertOracleHintExists;
import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
/**
*
@@ -126,17 +134,6 @@ public void testMergeUpdateInsertOrderIssue401_2() throws JSQLParserException {
"MERGE INTO a USING dual ON (col3 = ? AND col1 = ? AND col2 = ?) WHEN MATCHED THEN UPDATE SET col4 = col4 + ? WHEN NOT MATCHED THEN INSERT (col1, col2, col3, col4) VALUES (?, ?, ?, ?)");
}
- @Test
- public void testMergeUpdateInsertOrderIssue401_3() throws JSQLParserException {
- try {
- assertSqlCanBeParsedAndDeparsed(
- "MERGE INTO a USING dual ON (col3 = ? AND col1 = ? AND col2 = ?) WHEN MATCHED THEN UPDATE SET col4 = col4 + ? WHEN NOT MATCHED THEN INSERT (col1, col2, col3, col4) VALUES (?, ?, ?, ?) WHEN MATCHED THEN UPDATE SET col4 = col4 + ?");
- fail("syntaxerror parsed");
- } catch (JSQLParserException ex) {
- // expected to fail
- }
- }
-
@Test
public void testOracleHint() throws JSQLParserException {
String sql = "MERGE /*+ SOMEHINT */ INTO bonuses B\n" + "USING (\n"
@@ -165,10 +162,10 @@ public void testInsertMergeWhere() throws JSQLParserException {
Merge merge = (Merge) statement;
MergeInsert mergeInsert = merge.getMergeInsert();
- Assertions.assertThat(mergeInsert.getWhereCondition());
+ assertThat(mergeInsert.getWhereCondition());
MergeUpdate mergeUpdate = merge.getMergeUpdate();
- Assertions.assertThat(mergeUpdate.getWhereCondition());
+ assertThat(mergeUpdate.getWhereCondition());
}
@Test
@@ -265,4 +262,72 @@ void testSnowflakeMergeStatementWithNotMatchedAndPredicate() throws JSQLParserEx
assertSqlCanBeParsedAndDeparsed(sql, true);
}
+
+ @Test
+ void testSnowflakeMergeStatementWithManyWhensAndDelete() throws JSQLParserException {
+ String sql =
+ "MERGE INTO t1 USING t2 ON t1.t1Key = t2.t2Key\n" +
+ " WHEN MATCHED AND t2.marked = 1 THEN DELETE\n" +
+ " WHEN MATCHED AND t2.isNewStatus = 1 THEN UPDATE SET val = t2.newVal, status = t2.newStatus\n"
+ +
+ " WHEN MATCHED THEN UPDATE SET val = t2.newVal\n" +
+ " WHEN NOT MATCHED THEN INSERT (val, status) VALUES (t2.newVal, t2.newStatus)";
+
+ assertSqlCanBeParsedAndDeparsed(sql, true);
+ }
+
+ @ParameterizedTest
+ @MethodSource("deriveOperationsFromStandardClausesCases")
+ void testDeriveOperationsFromStandardClauses(List expectedOperations,
+ MergeUpdate update, MergeInsert insert, boolean insertFirst) {
+ Merge merge = new Merge();
+ merge.setMergeUpdate(update);
+ merge.setMergeInsert(insert);
+ merge.setInsertFirst(insertFirst);
+
+ assertThat(merge.getOperations()).isEqualTo(expectedOperations);
+ }
+
+ private static Stream deriveOperationsFromStandardClausesCases() {
+ MergeUpdate update = mock(MergeUpdate.class);
+ MergeInsert insert = mock(MergeInsert.class);
+
+ return Stream.of(
+ Arguments.of(Arrays.asList(update, insert), update, insert, false),
+ Arguments.of(Arrays.asList(insert, update), update, insert, true));
+ }
+
+ @ParameterizedTest
+ @MethodSource("deriveStandardClausesFromOperationsCases")
+ void testDeriveStandardClausesFromOperations(List operations,
+ MergeUpdate expectedUpdate, MergeInsert expectedInsert, boolean expectedInsertFirst) {
+ Merge merge = new Merge();
+ merge.setOperations(operations);
+
+ assertThat(merge.getMergeUpdate()).isEqualTo(expectedUpdate);
+ assertThat(merge.getMergeInsert()).isEqualTo(expectedInsert);
+ assertThat(merge.isInsertFirst()).isEqualTo(expectedInsertFirst);
+ }
+
+ private static Stream deriveStandardClausesFromOperationsCases() {
+ MergeDelete delete1 = mock(MergeDelete.class);
+ MergeUpdate update1 = mock(MergeUpdate.class);
+ MergeUpdate update2 = mock(MergeUpdate.class);
+ MergeInsert insert1 = mock(MergeInsert.class);
+ MergeInsert insert2 = mock(MergeInsert.class);
+
+ return Stream.of(
+ // just the two standard clauses present
+ Arguments.of(Arrays.asList(update1, insert1), update1, insert1, false),
+ Arguments.of(Arrays.asList(insert1, update1), update1, insert1, true),
+ // some clause(s) missing
+ Arguments.of(Collections.singletonList(update1), update1, null, false),
+ Arguments.of(Collections.singletonList(insert1), null, insert1, true),
+ Arguments.of(Collections.emptyList(), null, null, false),
+ // many clauses (non-standard)
+ Arguments.of(Arrays.asList(update1, update2, delete1, insert1, insert2), update1,
+ insert1, false),
+ Arguments.of(Arrays.asList(insert1, insert2, update1, update2, delete1), update1,
+ insert1, true));
+ }
}
diff --git a/src/test/java/net/sf/jsqlparser/statement/select/DB2Test.java b/src/test/java/net/sf/jsqlparser/statement/select/DB2Test.java
new file mode 100644
index 000000000..2aab3162b
--- /dev/null
+++ b/src/test/java/net/sf/jsqlparser/statement/select/DB2Test.java
@@ -0,0 +1,22 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2024 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement.select;
+
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.test.TestUtils;
+import org.junit.jupiter.api.Test;
+
+public class DB2Test {
+ @Test
+ void testDB2SpecialRegister() throws JSQLParserException {
+ String sqlStr = "SELECT * FROM TABLE1 where COL_WITH_TIMESTAMP <= CURRENT TIMESTAMP - CURRENT TIMEZONE";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+}
diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
index 527313bfe..fd4f28099 100644
--- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
+++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
@@ -5806,4 +5806,17 @@ public void testIssue1907() throws JSQLParserException {
"SELECT * FROM (SELECT year, person, SUM(amount) FROM rentals GROUP BY year, person) t1 ORDER BY year DESC WITH ROLLUP";
assertSqlCanBeParsedAndDeparsed(stmt2);
}
+
+ @Test
+ public void testIssue1908() throws JSQLParserException {
+ // postgresql14
+ String stmt = "SELECT * FROM ONLY sys_business_rule";
+ assertSqlCanBeParsedAndDeparsed(stmt);
+ }
+
+ @Test
+ public void testIssue1833() throws JSQLParserException {
+ String stmt = "SELECT age, name, gender FROM user_info INTO TEMP user_temp WITH NO LOG";
+ assertSqlCanBeParsedAndDeparsed(stmt);
+ }
}
diff --git a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java
index 2f9566088..ab63d1f9f 100644
--- a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java
+++ b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java
@@ -9,17 +9,6 @@
*/
package net.sf.jsqlparser.util;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.io.StringReader;
-import java.util.List;
-import java.util.Set;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.OracleHint;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
@@ -36,6 +25,18 @@
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.util.List;
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
public class TablesNamesFinderTest {
private static final CCJSqlParserManager PARSER_MANAGER = new CCJSqlParserManager();
@@ -522,4 +523,13 @@ void testRefreshMaterializedView() throws JSQLParserException {
Set tableNames6 = TablesNamesFinder.findTables(sqlStr6);
assertThat(tableNames6).isEmpty();
}
+
+ @Test
+ void testFromParenthesesJoin() throws JSQLParserException {
+ String sqlStr = "select * from (t1 left join t2 on t1.id = t2.id) t_select";
+ Set tables = TablesNamesFinder.findTables(sqlStr);
+ assertThat(tables).containsExactly("t1", "t2");
+
+ }
}
+