diff --git a/src/main/java/net/sf/jsqlparser/statement/AssertStatement.java b/src/main/java/net/sf/jsqlparser/statement/AssertStatement.java
new file mode 100644
index 0000000000..0879faf685
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/statement/AssertStatement.java
@@ -0,0 +1,81 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2026 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement;
+
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+
+/**
+ * BigQuery's {@code ASSERT expression [AS description]}, which fails the script when the expression
+ * does not evaluate to {@code TRUE}.
+ *
+ * @see Debugging
+ * statements
+ */
+public class AssertStatement implements Statement {
+ private Expression expression;
+ private StringValue description;
+
+ public AssertStatement() {}
+
+ public AssertStatement(Expression expression) {
+ this.expression = expression;
+ }
+
+ public AssertStatement(Expression expression, StringValue description) {
+ this.expression = expression;
+ this.description = description;
+ }
+
+ public Expression getExpression() {
+ return expression;
+ }
+
+ public void setExpression(Expression expression) {
+ this.expression = expression;
+ }
+
+ public AssertStatement withExpression(Expression expression) {
+ setExpression(expression);
+ return this;
+ }
+
+ public StringValue getDescription() {
+ return description;
+ }
+
+ public void setDescription(StringValue description) {
+ this.description = description;
+ }
+
+ public AssertStatement withDescription(StringValue description) {
+ setDescription(description);
+ return this;
+ }
+
+ public StringBuilder appendTo(StringBuilder builder) {
+ builder.append("ASSERT ").append(expression);
+ if (description != null) {
+ builder.append(" AS ").append(description);
+ }
+ return builder;
+ }
+
+ @Override
+ public String toString() {
+ return appendTo(new StringBuilder()).toString();
+ }
+
+ @Override
+ public T accept(StatementVisitor statementVisitor, S context) {
+ return statementVisitor.visit(this, context);
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java
index 861612fecd..51dd3493ab 100644
--- a/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java
+++ b/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java
@@ -93,6 +93,8 @@
import java.util.Locale;
import java.util.Set;
import java.util.function.Predicate;
+import net.sf.jsqlparser.statement.export.ExportDataStatement;
+import net.sf.jsqlparser.statement.load.LoadDataStatement;
/**
* Derives a {@link StatementFeatures} verdict from a statement tree.
@@ -521,6 +523,27 @@ public Void visit(Drop drop, S context) {
return null;
}
+ @Override
+ public Void visit(AssertStatement assertStatement, S context) {
+ analysis.claimTopLevel();
+ analysis.certain(StmtFeature.READS_DATA);
+ return null;
+ }
+
+ @Override
+ public Void visit(ExportDataStatement exportDataStatement, S context) {
+ analysis.claimTopLevel();
+ analysis.certain(StmtFeature.READS_DATA, StmtFeature.MODIFIES_DATA);
+ return null;
+ }
+
+ @Override
+ public Void visit(LoadDataStatement loadDataStatement, S context) {
+ analysis.claimTopLevel();
+ analysis.certain(StmtFeature.MODIFIES_DATA);
+ return null;
+ }
+
@Override
public Void visit(PurgeStatement purgeStatement, S context) {
analysis.claimTopLevel();
diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java
index 8ec9dc1bc0..ae2b286768 100644
--- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java
+++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java
@@ -68,6 +68,8 @@
import net.sf.jsqlparser.statement.update.ParenthesedUpdate;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.upsert.Upsert;
+import net.sf.jsqlparser.statement.export.ExportDataStatement;
+import net.sf.jsqlparser.statement.load.LoadDataStatement;
public interface StatementVisitor {
@@ -371,6 +373,24 @@ default void visit(RenameTableStatement renameTableStatement) {
this.visit(renameTableStatement, null);
}
+ T visit(AssertStatement assertStatement, S context);
+
+ default void visit(AssertStatement assertStatement) {
+ this.visit(assertStatement, null);
+ }
+
+ T visit(ExportDataStatement exportDataStatement, S context);
+
+ default void visit(ExportDataStatement exportDataStatement) {
+ this.visit(exportDataStatement, null);
+ }
+
+ T visit(LoadDataStatement loadDataStatement, S context);
+
+ default void visit(LoadDataStatement loadDataStatement) {
+ this.visit(loadDataStatement, null);
+ }
+
T visit(PurgeStatement purgeStatement, S context);
default void visit(PurgeStatement purgeStatement) {
diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java
index ab05725ad5..e3df12b1a4 100644
--- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java
+++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java
@@ -89,6 +89,8 @@
import java.util.List;
import net.sf.jsqlparser.util.TableDefinitionTraversal;
+import net.sf.jsqlparser.statement.export.ExportDataStatement;
+import net.sf.jsqlparser.statement.load.LoadDataStatement;
@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"})
public class StatementVisitorAdapter implements StatementVisitor {
@@ -618,6 +620,21 @@ public T visit(RenameTableStatement renameTableStatement, S context) {
return null;
}
+ @Override
+ public T visit(AssertStatement assertStatement, S context) {
+ return null;
+ }
+
+ @Override
+ public T visit(ExportDataStatement exportDataStatement, S context) {
+ return null;
+ }
+
+ @Override
+ public T visit(LoadDataStatement loadDataStatement, S context) {
+ return null;
+ }
+
@Override
public T visit(PurgeStatement purgeStatement, S context) {
return null;
diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java b/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java
index 3dbcb4600b..d248f7879d 100644
--- a/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java
+++ b/src/main/java/net/sf/jsqlparser/statement/create/table/CreateTable.java
@@ -36,6 +36,7 @@ public class CreateTable implements Statement {
private List tableElements;
private Select select;
private Table likeTable;
+ private Table cloneTable;
private ColDataType ofType;
private boolean selectParenthesis;
private boolean ifNotExists = false;
@@ -236,6 +237,22 @@ public void setLikeTable(Table likeTable, boolean parenthesis) {
this.selectParenthesis = parenthesis;
}
+ /**
+ * BigQuery's {@code CREATE [SNAPSHOT] TABLE target CLONE source [FOR SYSTEM_TIME AS OF ...]}.
+ */
+ public Table getCloneTable() {
+ return cloneTable;
+ }
+
+ public void setCloneTable(Table cloneTable) {
+ this.cloneTable = cloneTable;
+ }
+
+ public CreateTable withCloneTable(Table cloneTable) {
+ setCloneTable(cloneTable);
+ return this;
+ }
+
public boolean isIfNotExists() {
return ifNotExists;
}
@@ -330,6 +347,9 @@ private void appendCreateClause(StringBuilder b) {
if (partitionOf != null) {
b.append(" PARTITION OF ").append(partitionOf);
}
+ if (cloneTable != null) {
+ b.append(" CLONE ").append(cloneTable);
+ }
}
private void appendColumnDefinitions(StringBuilder b) {
diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/TablePartitioning.java b/src/main/java/net/sf/jsqlparser/statement/create/table/TablePartitioning.java
index 70b127dbba..1c96a39ea1 100644
--- a/src/main/java/net/sf/jsqlparser/statement/create/table/TablePartitioning.java
+++ b/src/main/java/net/sf/jsqlparser/statement/create/table/TablePartitioning.java
@@ -27,7 +27,12 @@
public class TablePartitioning implements Serializable {
public enum Type {
- HASH, KEY, RANGE, LIST
+ HASH, KEY, RANGE, LIST,
+ /**
+ * BigQuery partitions by a bare expression, without a partitioning method keyword:
+ * {@code PARTITION BY DATE(ts)}.
+ */
+ EXPRESSION
}
private Type type;
@@ -285,6 +290,10 @@ public void appendTo(StringBuilder builder, Consumer expressionPrint
}
private void appendMethod(StringBuilder builder, Consumer expressionPrinter) {
+ if (type == Type.EXPRESSION) {
+ expressionPrinter.accept(expression);
+ return;
+ }
if (linear) {
builder.append("LINEAR ");
}
diff --git a/src/main/java/net/sf/jsqlparser/statement/export/ExportDataStatement.java b/src/main/java/net/sf/jsqlparser/statement/export/ExportDataStatement.java
new file mode 100644
index 0000000000..e9571a397a
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/statement/export/ExportDataStatement.java
@@ -0,0 +1,90 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2026 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement.export;
+
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.statement.Statement;
+import net.sf.jsqlparser.statement.StatementVisitor;
+import net.sf.jsqlparser.statement.select.Select;
+
+/**
+ * BigQuery's {@code EXPORT DATA [WITH CONNECTION connection] OPTIONS (option_list) AS query}.
+ *
+ * @see Export
+ * statements
+ */
+public class ExportDataStatement implements Statement {
+ private String connectionName;
+ private ExpressionList options;
+ private Select select;
+
+ public String getConnectionName() {
+ return connectionName;
+ }
+
+ public void setConnectionName(String connectionName) {
+ this.connectionName = connectionName;
+ }
+
+ public ExportDataStatement withConnectionName(String connectionName) {
+ setConnectionName(connectionName);
+ return this;
+ }
+
+ public ExpressionList getOptions() {
+ return options;
+ }
+
+ public void setOptions(ExpressionList options) {
+ this.options = options;
+ }
+
+ public ExportDataStatement withOptions(ExpressionList options) {
+ setOptions(options);
+ return this;
+ }
+
+ public Select getSelect() {
+ return select;
+ }
+
+ public void setSelect(Select select) {
+ this.select = select;
+ }
+
+ public ExportDataStatement withSelect(Select select) {
+ setSelect(select);
+ return this;
+ }
+
+ public StringBuilder appendTo(StringBuilder builder) {
+ builder.append("EXPORT DATA");
+ if (connectionName != null) {
+ builder.append(" WITH CONNECTION ").append(connectionName);
+ }
+ if (options != null) {
+ builder.append(" OPTIONS (").append(options).append(")");
+ }
+ builder.append(" AS ").append(select);
+ return builder;
+ }
+
+ @Override
+ public String toString() {
+ return appendTo(new StringBuilder()).toString();
+ }
+
+ @Override
+ public T accept(StatementVisitor statementVisitor, S context) {
+ return statementVisitor.visit(this, context);
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/statement/load/LoadDataStatement.java b/src/main/java/net/sf/jsqlparser/statement/load/LoadDataStatement.java
new file mode 100644
index 0000000000..3e04159f1e
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/statement/load/LoadDataStatement.java
@@ -0,0 +1,156 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2026 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement.load;
+
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.Statement;
+import net.sf.jsqlparser.statement.StatementVisitor;
+import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+
+import java.util.List;
+
+/**
+ * BigQuery's {@code LOAD DATA {INTO | OVERWRITE} table [(column_definitions)] [OPTIONS
+ * (option_list)] FROM FILES (file_options) [WITH PARTITION COLUMNS] [WITH CONNECTION connection]}.
+ *
+ * @see Load
+ * statements
+ */
+public class LoadDataStatement implements Statement {
+ private boolean overwrite;
+ private Table table;
+ private List columnDefinitions;
+ private ExpressionList> options;
+ private ExpressionList> fileOptions;
+ private boolean withPartitionColumns;
+ private String connectionName;
+
+ public boolean isOverwrite() {
+ return overwrite;
+ }
+
+ public void setOverwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+ }
+
+ public LoadDataStatement withOverwrite(boolean overwrite) {
+ setOverwrite(overwrite);
+ return this;
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public void setTable(Table table) {
+ this.table = table;
+ }
+
+ public LoadDataStatement withTable(Table table) {
+ setTable(table);
+ return this;
+ }
+
+ public List getColumnDefinitions() {
+ return columnDefinitions;
+ }
+
+ public void setColumnDefinitions(List columnDefinitions) {
+ this.columnDefinitions = columnDefinitions;
+ }
+
+ public LoadDataStatement withColumnDefinitions(List columnDefinitions) {
+ setColumnDefinitions(columnDefinitions);
+ return this;
+ }
+
+ public ExpressionList> getOptions() {
+ return options;
+ }
+
+ public void setOptions(ExpressionList> options) {
+ this.options = options;
+ }
+
+ public LoadDataStatement withOptions(ExpressionList> options) {
+ setOptions(options);
+ return this;
+ }
+
+ public ExpressionList> getFileOptions() {
+ return fileOptions;
+ }
+
+ public void setFileOptions(ExpressionList> fileOptions) {
+ this.fileOptions = fileOptions;
+ }
+
+ public LoadDataStatement withFileOptions(ExpressionList> fileOptions) {
+ setFileOptions(fileOptions);
+ return this;
+ }
+
+ public boolean isWithPartitionColumns() {
+ return withPartitionColumns;
+ }
+
+ public void setWithPartitionColumns(boolean withPartitionColumns) {
+ this.withPartitionColumns = withPartitionColumns;
+ }
+
+ public LoadDataStatement withWithPartitionColumns(boolean withPartitionColumns) {
+ setWithPartitionColumns(withPartitionColumns);
+ return this;
+ }
+
+ public String getConnectionName() {
+ return connectionName;
+ }
+
+ public void setConnectionName(String connectionName) {
+ this.connectionName = connectionName;
+ }
+
+ public LoadDataStatement withConnectionName(String connectionName) {
+ setConnectionName(connectionName);
+ return this;
+ }
+
+ public StringBuilder appendTo(StringBuilder builder) {
+ builder.append("LOAD DATA ").append(overwrite ? "OVERWRITE " : "INTO ").append(table);
+ if (columnDefinitions != null && !columnDefinitions.isEmpty()) {
+ builder.append(" ").append(PlainSelect.getStringList(columnDefinitions, true, true));
+ }
+ if (options != null) {
+ builder.append(" OPTIONS (").append(options).append(")");
+ }
+ builder.append(" FROM FILES (").append(fileOptions).append(")");
+ if (withPartitionColumns) {
+ builder.append(" WITH PARTITION COLUMNS");
+ }
+ if (connectionName != null) {
+ builder.append(" WITH CONNECTION ").append(connectionName);
+ }
+ return builder;
+ }
+
+ @Override
+ public String toString() {
+ return appendTo(new StringBuilder()).toString();
+ }
+
+ @Override
+ public T accept(StatementVisitor statementVisitor, S context) {
+ return statementVisitor.visit(this, context);
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/statement/select/TableFunction.java b/src/main/java/net/sf/jsqlparser/statement/select/TableFunction.java
index 51e4057ec8..359ab7e770 100644
--- a/src/main/java/net/sf/jsqlparser/statement/select/TableFunction.java
+++ b/src/main/java/net/sf/jsqlparser/statement/select/TableFunction.java
@@ -24,6 +24,8 @@ public class TableFunction extends Function implements FromItem {
private Function function;
private ParenthesedExpressionList rowsFromFunctions;
private String withClause = null;
+ private boolean withOffset = false;
+ private Alias offsetAlias = null;
public TableFunction(Function function) {
this.function = function;
@@ -121,6 +123,37 @@ public TableFunction setPrefix(String prefix) {
return this;
}
+ /**
+ * BigQuery's {@code UNNEST(array) [AS alias] WITH OFFSET [AS offsetAlias]}, where the
+ * {@code WITH OFFSET} clause follows the table alias and carries an alias of its own.
+ */
+ public boolean isWithOffset() {
+ return withOffset;
+ }
+
+ public void setWithOffset(boolean withOffset) {
+ this.withOffset = withOffset;
+ }
+
+ public TableFunction withWithOffset(boolean withOffset) {
+ setWithOffset(withOffset);
+ return this;
+ }
+
+ public Alias getOffsetAlias() {
+ return offsetAlias;
+ }
+
+ public void setOffsetAlias(Alias offsetAlias) {
+ this.offsetAlias = offsetAlias;
+ this.withOffset = true;
+ }
+
+ public TableFunction withOffsetAlias(Alias offsetAlias) {
+ setOffsetAlias(offsetAlias);
+ return this;
+ }
+
public String getWithClause() {
return withClause;
}
@@ -211,6 +244,13 @@ public StringBuilder appendTo(StringBuilder builder) {
if (alias != null) {
builder.append(alias);
}
+
+ if (withOffset) {
+ builder.append(" WITH OFFSET");
+ if (offsetAlias != null) {
+ builder.append(offsetAlias);
+ }
+ }
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 3bf8df2e0b..658dfea877 100644
--- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
+++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
@@ -209,6 +209,9 @@
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;
import net.sf.jsqlparser.statement.upsert.Upsert;
+import net.sf.jsqlparser.statement.AssertStatement;
+import net.sf.jsqlparser.statement.export.ExportDataStatement;
+import net.sf.jsqlparser.statement.load.LoadDataStatement;
/**
@@ -2464,6 +2467,39 @@ public void visit(RenameTableStatement renameTableStatement) {
StatementVisitor.super.visit(renameTableStatement);
}
+ @Override
+ public Void visit(AssertStatement assertStatement, S context) {
+ assertStatement.getExpression().accept(this, context);
+ return null;
+ }
+
+ @Override
+ public void visit(AssertStatement assertStatement) {
+ StatementVisitor.super.visit(assertStatement);
+ }
+
+ @Override
+ public Void visit(ExportDataStatement exportDataStatement, S context) {
+ exportDataStatement.getSelect().accept((SelectVisitor>) this, context);
+ return null;
+ }
+
+ @Override
+ public void visit(ExportDataStatement exportDataStatement) {
+ StatementVisitor.super.visit(exportDataStatement);
+ }
+
+ @Override
+ public Void visit(LoadDataStatement loadDataStatement, S context) {
+ loadDataStatement.getTable().accept(this, context);
+ return null;
+ }
+
+ @Override
+ public void visit(LoadDataStatement loadDataStatement) {
+ StatementVisitor.super.visit(loadDataStatement);
+ }
+
@Override
public Void visit(PurgeStatement purgeStatement, S context) {
if (purgeStatement.getPurgeObjectType() == PurgeObjectType.TABLE) {
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java
index c45bac2882..faed455c30 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/CreateTableDeParser.java
@@ -62,6 +62,9 @@ public void deParse(CreateTable createTable) {
builder.append(" PARTITION OF ")
.append(createTable.getPartitionOf().getFullyQualifiedName());
}
+ if (createTable.getCloneTable() != null) {
+ builder.append(" CLONE ").append(createTable.getCloneTable());
+ }
if (createTable.getColumns() != null && !createTable.getColumns().isEmpty()) {
builder.append(" (");
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 69cb9770cb..c8f4e3902e 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java
@@ -97,6 +97,9 @@
import net.sf.jsqlparser.statement.update.ParenthesedUpdate;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.upsert.Upsert;
+import net.sf.jsqlparser.statement.AssertStatement;
+import net.sf.jsqlparser.statement.export.ExportDataStatement;
+import net.sf.jsqlparser.statement.load.LoadDataStatement;
public class StatementDeParser extends AbstractDeParser
implements StatementVisitor {
@@ -552,6 +555,24 @@ public StringBuilder visit(RenameTableStatement renameTableStatement, S cont
return builder;
}
+ @Override
+ public StringBuilder visit(AssertStatement assertStatement, S context) {
+ assertStatement.appendTo(builder);
+ return builder;
+ }
+
+ @Override
+ public StringBuilder visit(ExportDataStatement exportDataStatement, S context) {
+ exportDataStatement.appendTo(builder);
+ return builder;
+ }
+
+ @Override
+ public StringBuilder visit(LoadDataStatement loadDataStatement, S context) {
+ loadDataStatement.appendTo(builder);
+ return builder;
+ }
+
@Override
public StringBuilder visit(PurgeStatement purgeStatement, S context) {
purgeStatement.appendTo(builder);
diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java
index 6764ef740d..1ed3a2ec92 100644
--- a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java
+++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java
@@ -99,6 +99,9 @@
import net.sf.jsqlparser.statement.upsert.Upsert;
import net.sf.jsqlparser.util.validation.ValidationCapability;
import net.sf.jsqlparser.util.validation.metadata.NamedObject;
+import net.sf.jsqlparser.statement.AssertStatement;
+import net.sf.jsqlparser.statement.export.ExportDataStatement;
+import net.sf.jsqlparser.statement.load.LoadDataStatement;
/**
* @author gitmotte
@@ -477,6 +480,24 @@ public Void visit(RenameTableStatement renameTableStatement, S context) {
return null;
}
+ @Override
+ public Void visit(AssertStatement assertStatement, S context) {
+ // TODO: not yet implemented
+ return null;
+ }
+
+ @Override
+ public Void visit(ExportDataStatement exportDataStatement, S context) {
+ // TODO: not yet implemented
+ return null;
+ }
+
+ @Override
+ public Void visit(LoadDataStatement loadDataStatement, S context) {
+ // TODO: not yet implemented
+ return null;
+ }
+
@Override
public Void visit(PurgeStatement purgeStatement, S context) {
// TODO: not yet implemented
@@ -693,6 +714,18 @@ public void visit(RenameTableStatement renameTableStatement) {
visit(renameTableStatement, null);
}
+ public void visit(AssertStatement assertStatement) {
+ visit(assertStatement, null);
+ }
+
+ public void visit(ExportDataStatement exportDataStatement) {
+ visit(exportDataStatement, null);
+ }
+
+ public void visit(LoadDataStatement loadDataStatement) {
+ visit(loadDataStatement, null);
+ }
+
public void visit(PurgeStatement purgeStatement) {
visit(purgeStatement, null);
}
diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
index 4ab8d15051..0ddb836c58 100644
--- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
+++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
@@ -72,6 +72,7 @@ import net.sf.jsqlparser.statement.create.view.*;
import net.sf.jsqlparser.statement.delete.*;
import net.sf.jsqlparser.statement.drop.*;
import net.sf.jsqlparser.statement.insert.*;
+import net.sf.jsqlparser.statement.load.*;
import net.sf.jsqlparser.statement.execute.*;
import net.sf.jsqlparser.statement.piped.*;
import net.sf.jsqlparser.statement.select.*;
@@ -1933,6 +1934,7 @@ String NonReservedWord() :
| tk=
| tk=
| tk=
+ | tk=
| tk=
| tk=
| tk=
@@ -1973,6 +1975,7 @@ String NonReservedWord() :
| tk=
| tk=
| tk=
+ | tk=
| tk=
| tk=
| tk=
@@ -2100,6 +2103,7 @@ String NonReservedWord() :
| tk=
| tk=
| tk=
+ | tk=
| tk=
| tk=
| tk=
@@ -2912,6 +2916,12 @@ Statement SingleStatement() :
|
LOOKAHEAD({ isKeywordAhead("REVOKE") }) stm = Revoke(false)
|
+ LOOKAHEAD({ getToken(1).kind == K_ASSERT }) stm = AssertStatement()
+ |
+ LOOKAHEAD({ getToken(1).kind == K_EXPORT && getToken(2).kind == K_DATA }) stm = ExportDataStatement()
+ |
+ LOOKAHEAD({ getToken(1).kind == K_LOAD && getToken(2).kind == K_DATA }) stm = LoadDataStatement()
+ |
stm = PurgeStatement()
|
stm = SessionStatement()
@@ -4313,6 +4323,90 @@ RenameTableStatement RenameTableStatement(): {
}
}
+/**
+ * BigQuery's LOAD DATA {INTO | OVERWRITE} table [(column_definitions)] [OPTIONS (option_list)]
+ * FROM FILES (file_options) [WITH PARTITION COLUMNS] [WITH CONNECTION connection].
+ */
+LoadDataStatement LoadDataStatement() #LoadDataStatement:
+{
+ LoadDataStatement loadDataStatement = new LoadDataStatement();
+ List columnDefinitions = new ArrayList();
+ ColumnDefinition columnDefinition;
+ ExpressionList options;
+ ObjectNames connectionName;
+ Table table;
+}
+{
+
+ ( | { loadDataStatement.setOverwrite(true); } )
+ table=Table() { loadDataStatement.setTable(table); }
+ [
+ LOOKAHEAD(2) "("
+ columnDefinition=ColumnDefinition() { columnDefinitions.add(columnDefinition); }
+ ( "," columnDefinition=ColumnDefinition() { columnDefinitions.add(columnDefinition); } )*
+ ")" { loadDataStatement.setColumnDefinitions(columnDefinitions); }
+ ]
+ [
+ LOOKAHEAD({ isKeywordAhead("OPTIONS") })
+ "(" options=ExpressionList() ")" { loadDataStatement.setOptions(options); }
+ ]
+
+ LOOKAHEAD({ isKeywordAhead("FILES") })
+ "(" options=ExpressionList() ")" { loadDataStatement.setFileOptions(options); }
+ [
+ LOOKAHEAD(2)
+ { loadDataStatement.setWithPartitionColumns(true); }
+ ]
+ [
+ LOOKAHEAD({ getToken(1).kind == K_WITH && "CONNECTION".equalsIgnoreCase(getToken(2).image) })
+ connectionName=RelObjectNames()
+ { loadDataStatement.setConnectionName(String.join(".", connectionName.getNames())); }
+ ]
+ { return loadDataStatement; }
+}
+
+/**
+ * BigQuery's EXPORT DATA [WITH CONNECTION connection] OPTIONS (option_list) AS query.
+ */
+ExportDataStatement ExportDataStatement() #ExportDataStatement:
+{
+ ExportDataStatement exportDataStatement = new ExportDataStatement();
+ ExpressionList options;
+ ObjectNames connectionName;
+ Select select;
+}
+{
+
+ [
+ LOOKAHEAD({ getToken(1).kind == K_WITH && "CONNECTION".equalsIgnoreCase(getToken(2).image) })
+ connectionName=RelObjectNames()
+ { exportDataStatement.setConnectionName(String.join(".", connectionName.getNames())); }
+ ]
+ [
+ LOOKAHEAD({ isKeywordAhead("OPTIONS") })
+ "(" options=ExpressionList() ")"
+ { exportDataStatement.setOptions(options); }
+ ]
+ select=Select() { exportDataStatement.setSelect(select); }
+ { return exportDataStatement; }
+}
+
+/**
+ * BigQuery's ASSERT expression [AS description] debugging statement.
+ */
+AssertStatement AssertStatement() #AssertStatement:
+{
+ AssertStatement assertStatement = new AssertStatement();
+ Expression expression;
+ Token token;
+}
+{
+ expression=Expression() { assertStatement.setExpression(expression); }
+ [ token=
+ { assertStatement.setDescription(new StringValue(token.image)); } ]
+ { return assertStatement; }
+}
+
PurgeStatement PurgeStatement(): {
PurgeStatement purgeStatement = null;
Table table;
@@ -7642,6 +7736,13 @@ FromItem FromItem() #FromItem:
)
[ LOOKAHEAD({ isAliasAhead() }) alias=Alias() { fromItem.setAlias(alias); } ]
+ [
+ // BigQuery: UNNEST(array) AS alias WITH OFFSET AS offsetAlias
+ LOOKAHEAD(2, { fromItem instanceof TableFunction })
+ { ((TableFunction) fromItem).setWithOffset(true); }
+ [ LOOKAHEAD({ isAliasAhead() }) alias=Alias()
+ { ((TableFunction) fromItem).setOffsetAlias(alias); } ]
+ ]
[
LOOKAHEAD(2, {fromItem instanceof Table }) timeTravelStr = TimeTravelAfterAlias()
{ ((Table) fromItem).setTimeTravelStrAfterAlias(timeTravelStr); }
@@ -9459,7 +9560,7 @@ Expression ArrayExpression(Expression obj): {
Expression stopExpr = null;
} {
"["
- [LOOKAHEAD(3) idxExpr = SimpleExpression()]
+ [LOOKAHEAD(3) idxExpr = ArrayIndexExpression()]
[
(":" { startExpr=idxExpr; idxExpr=null; })
[stopExpr = SimpleExpression()]
@@ -9468,7 +9569,7 @@ Expression ArrayExpression(Expression obj): {
{ expr = new ArrayExpression(obj, idxExpr, startExpr, stopExpr); }
(
LOOKAHEAD(2) "["
- [LOOKAHEAD(3) idxExpr = SimpleExpression()]
+ [LOOKAHEAD(3) idxExpr = ArrayIndexExpression()]
[
(":" { startExpr=idxExpr; idxExpr=null; })
[stopExpr = SimpleExpression()]
@@ -9479,6 +9580,29 @@ Expression ArrayExpression(Expression obj): {
{ return expr; }
}
+/**
+ * Array subscript index. BigQuery addresses array elements positionally with
+ * OFFSET(n), ORDINAL(n), SAFE_OFFSET(n) and SAFE_ORDINAL(n). Only OFFSET needs
+ * an explicit rule here, the others are plain identifiers and parse as functions.
+ */
+Expression ArrayIndexExpression() :
+{
+ Token token;
+ Expression expr;
+}
+{
+ (
+ LOOKAHEAD(2) token = "(" expr = SimpleExpression() ")"
+ {
+ return new Function()
+ .withName(token.image)
+ .withParameters(new ExpressionList(expr));
+ }
+ |
+ expr = SimpleExpression() { return expr; }
+ )
+}
+
Expression NavigationStep(Expression base):
{
Expression step = null;
@@ -11489,6 +11613,7 @@ Function InternalFunction(boolean escaped):
Column attributeColumn = null;
Token attributeToken = null;
Limit limit;
+ List orderByList = null;
List keywordArgs = null;
}
{
@@ -11538,6 +11663,12 @@ Function InternalFunction(boolean escaped):
)
]
+ // BigQuery orders aggregate arguments after the null handling:
+ // ARRAY_AGG(expr [IGNORE NULLS] [ORDER BY key] [LIMIT n])
+ [
+ LOOKAHEAD(2) orderByList = OrderByElements() { retval.setOrderByElements(orderByList); }
+ ]
+
[
limit = PlainLimit() { retval.setLimit(limit); }
]
@@ -11621,7 +11752,8 @@ ExpressionList FunctionArgumentList(Function retval):
{ if ("XMLFOREST".equalsIgnoreCase(retval.getName())) { return XmlForestArguments(); } }
[ LOOKAHEAD(2) extraKeywordToken = { retval.setExtraKeyword(extraKeywordToken.image); } ]
expressionList=ExpressionList()
- [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ]
+ // explicit lookahead: InternalFunction accepts ORDER BY after IGNORE NULLS too
+ [ LOOKAHEAD(2) orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ]
// https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/LISTAGG.html
[
@@ -13425,6 +13557,12 @@ CreateTable CreateTable(boolean isUsingOrReplace):
[ LOOKAHEAD(2) { createTable.setIfNotExists(true); }]
table=Table()
+ [
+ // BigQuery: CREATE [SNAPSHOT] TABLE target CLONE source [FOR SYSTEM_TIME AS OF ...]
+ LOOKAHEAD(2) likeTable=Table()
+ [ LOOKAHEAD(2) tableColumn=TimeTravelAfterAlias() { likeTable.setTimeTravel(tableColumn); } ]
+ { createTable.setCloneTable(likeTable); likeTable = null; }
+ ]
[ ofType=ColDataType() { createTable.setOfType(ofType); } ]
[ LOOKAHEAD(2) partitionOfTable=Table()
{ createTable.setPartitionOf(partitionOfTable); } ]
@@ -14857,6 +14995,7 @@ TablePartitioning CreateTablePartitioning():
{
TablePartitioning partitioning = null;
TablePartitioning subPartitioning = null;
+ Expression expression = null;
ExpressionList expressionList = null;
ExpressionList columns = null;
List partitionDefinitions = null;
@@ -14867,9 +15006,11 @@ TablePartitioning CreateTablePartitioning():
{
(
- partitioning=HashOrKeyPartitioning()
+ // explicit lookaheads: the method keywords win over the BigQuery
+ // bare-expression alternative below, which they share a prefix with
+ LOOKAHEAD(2) partitioning=HashOrKeyPartitioning()
|
- { partitioning = new TablePartitioning(TablePartitioning.Type.RANGE); }
+ LOOKAHEAD(2) { partitioning = new TablePartitioning(TablePartitioning.Type.RANGE); }
(
"(" expressionList=SimpleExpressionList() ")" {
if (expressionList.size() == 1) {
@@ -14886,7 +15027,7 @@ TablePartitioning CreateTablePartitioning():
}
)
|
- { partitioning = new TablePartitioning(TablePartitioning.Type.LIST); }
+ LOOKAHEAD(2) { partitioning = new TablePartitioning(TablePartitioning.Type.LIST); }
(
"(" expressionList=SimpleExpressionList() ")" {
if (expressionList.size() == 1) {
@@ -14902,6 +15043,17 @@ TablePartitioning CreateTablePartitioning():
partitioning.setColumns(columns);
}
)
+ |
+ // BigQuery partitions by a bare expression: PARTITION BY d, PARTITION BY DATE(ts),
+ // PARTITION BY RANGE_BUCKET(id, GENERATE_ARRAY(0, 100, 10))
+ LOOKAHEAD({ getToken(1).kind != K_HASH && getToken(1).kind != K_KEY
+ && getToken(1).kind != K_LINEAR && getToken(1).kind != K_RANGE
+ && getToken(1).kind != K_LIST })
+ expression=SimpleExpression()
+ {
+ partitioning = new TablePartitioning(TablePartitioning.Type.EXPRESSION);
+ partitioning.setExpression(expression);
+ }
)
[ LOOKAHEAD({ getToken(1).kind == K_PARTITIONS })
tk= { partitioning.setPartitions(Long.valueOf(tk.image)); } ]
diff --git a/src/test/java/net/sf/jsqlparser/statement/select/BigQueryTest.java b/src/test/java/net/sf/jsqlparser/statement/select/BigQueryTest.java
index 81a3217844..33a74b2cc0 100644
--- a/src/test/java/net/sf/jsqlparser/statement/select/BigQueryTest.java
+++ b/src/test/java/net/sf/jsqlparser/statement/select/BigQueryTest.java
@@ -10,6 +10,12 @@
package net.sf.jsqlparser.statement.select;
import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.ArrayExpression;
+import net.sf.jsqlparser.statement.AssertStatement;
+import net.sf.jsqlparser.statement.create.table.CreateTable;
+import net.sf.jsqlparser.statement.export.ExportDataStatement;
+import net.sf.jsqlparser.statement.load.LoadDataStatement;
+import net.sf.jsqlparser.statement.create.table.TablePartitioning;
import net.sf.jsqlparser.test.TestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
@@ -110,4 +116,233 @@ void testTimeSeriesFunction() throws JSQLParserException {
TableFunction function = select.getFromItem(TableFunction.class);
Assertions.assertEquals("TABLE", function.getFunction().getExtraKeyword());
}
+
+ @Test
+ void testArrayOffsetAccessor() throws JSQLParserException {
+ String sqlStr = "SELECT arr[OFFSET(0)] FROM t";
+ PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ ArrayExpression arrayExpression =
+ (ArrayExpression) select.getSelectItem(0).getExpression();
+
+ Assertions.assertEquals("OFFSET(0)", arrayExpression.getIndexExpression().toString());
+ }
+
+ @Test
+ void testArraySafeOffsetAndOrdinalAccessors() throws JSQLParserException {
+ String sqlStr = "SELECT arr[ORDINAL(1)], arr[SAFE_OFFSET(2)], arr[SAFE_ORDINAL(3)] FROM t";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testOffsetClauseStillParses() throws JSQLParserException {
+ String sqlStr = "SELECT a FROM t LIMIT 10 OFFSET 5";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testUnnestWithOffsetAfterAlias() throws JSQLParserException {
+ String sqlStr = "SELECT x, pos FROM UNNEST([1, 2, 3]) AS x WITH OFFSET AS pos";
+ PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ TableFunction unnest = (TableFunction) select.getFromItem();
+
+ Assertions.assertTrue(unnest.isWithOffset());
+ Assertions.assertEquals("x", unnest.getAlias().getName());
+ Assertions.assertEquals("pos", unnest.getOffsetAlias().getName());
+ }
+
+ @Test
+ void testUnnestWithOffsetWithoutOffsetAlias() throws JSQLParserException {
+ String sqlStr = "SELECT * FROM t, UNNEST(t.arr) AS c WITH OFFSET";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testUnnestWithOrdinalityAliasStillBindsToTableFunction() throws JSQLParserException {
+ String sqlStr = "SELECT * FROM UNNEST(ARRAY[1, 2, 3]) WITH ORDINALITY AS t (a, b)";
+ PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ TableFunction unnest = (TableFunction) select.getFromItem();
+
+ Assertions.assertFalse(unnest.isWithOffset());
+ Assertions.assertEquals("t", unnest.getAlias().getName());
+ }
+
+ @Test
+ void testAggregateFunctionIgnoreNullsBeforeOrderBy() throws JSQLParserException {
+ String sqlStr = "SELECT ARRAY_AGG(x IGNORE NULLS ORDER BY x) FROM t";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testAggregateFunctionIgnoreNullsOrderByLimit() throws JSQLParserException {
+ String sqlStr = "SELECT ARRAY_AGG(DISTINCT x IGNORE NULLS ORDER BY x DESC LIMIT 5) FROM t";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testAggregateFunctionOrderByWithoutNullHandling() throws JSQLParserException {
+ String sqlStr = "SELECT ARRAY_AGG(x ORDER BY x) FROM t";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testCreateTablePartitionByExpression() throws JSQLParserException {
+ String sqlStr = "CREATE TABLE ds.t (id INT64, ts TIMESTAMP) PARTITION BY DATE(ts)";
+ CreateTable createTable =
+ (CreateTable) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertEquals(TablePartitioning.Type.EXPRESSION,
+ createTable.getPartitioning().getType());
+ Assertions.assertEquals("DATE(ts)",
+ createTable.getPartitioning().getExpression().toString());
+ }
+
+ @Test
+ void testCreateTablePartitionByColumnWithOptions() throws JSQLParserException {
+ String sqlStr =
+ "CREATE TABLE ds.t (id INT64, d DATE) PARTITION BY d OPTIONS (description = 'x')";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testCreateTableAsSelectPartitionByAndClusterBy() throws JSQLParserException {
+ String sqlStr = "CREATE TABLE ds.t PARTITION BY d CLUSTER BY id "
+ + "AS SELECT 1 AS id, CURRENT_DATE() AS d";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testCreateTablePartitionByHashStillParses() throws JSQLParserException {
+ String sqlStr = "CREATE TABLE t (id INT) PARTITION BY HASH (id)";
+ CreateTable createTable =
+ (CreateTable) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertEquals(TablePartitioning.Type.HASH,
+ createTable.getPartitioning().getType());
+ }
+
+ @Test
+ void testAssertStatementWithDescription() throws JSQLParserException {
+ String sqlStr = "ASSERT (SELECT COUNT(*) FROM t) > 0 AS 'table is empty'";
+ AssertStatement assertStatement =
+ (AssertStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertEquals("'table is empty'", assertStatement.getDescription().toString());
+ }
+
+ @Test
+ void testAssertStatementWithoutDescription() throws JSQLParserException {
+ String sqlStr = "ASSERT EXISTS(SELECT 1 FROM t)";
+ AssertStatement assertStatement =
+ (AssertStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertNull(assertStatement.getDescription());
+ }
+
+ @Test
+ void testAssertRemainsUsableAsIdentifier() throws JSQLParserException {
+ String sqlStr = "SELECT assert FROM t";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testExportData() throws JSQLParserException {
+ String sqlStr = "EXPORT DATA OPTIONS (uri = 'gs://bucket/*.csv', format = 'CSV', "
+ + "overwrite = true) AS SELECT * FROM t";
+ ExportDataStatement exportData =
+ (ExportDataStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertEquals(3, exportData.getOptions().size());
+ Assertions.assertNull(exportData.getConnectionName());
+ }
+
+ @Test
+ void testExportDataWithConnection() throws JSQLParserException {
+ String sqlStr = "EXPORT DATA WITH CONNECTION myproject.us.myconnection "
+ + "OPTIONS (uri = 'gs://bucket/*.csv') AS SELECT a FROM t";
+ ExportDataStatement exportData =
+ (ExportDataStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertEquals("myproject.us.myconnection", exportData.getConnectionName());
+ }
+
+ @Test
+ void testExportDataTablesNamesFinder() throws JSQLParserException {
+ String sqlStr = "EXPORT DATA OPTIONS (uri = 'gs://bucket/*.csv') AS SELECT * FROM ds.t";
+
+ Assertions.assertEquals(java.util.Collections.singletonList("ds.t"),
+ new net.sf.jsqlparser.util.TablesNamesFinder()
+ .getTableList(net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(sqlStr)));
+ }
+
+ @Test
+ void testLoadDataIntoFromFiles() throws JSQLParserException {
+ String sqlStr = "LOAD DATA INTO mydataset.mytable "
+ + "FROM FILES (format = 'AVRO', uris = ['gs://bucket/*.avro'])";
+ LoadDataStatement loadData =
+ (LoadDataStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertFalse(loadData.isOverwrite());
+ Assertions.assertEquals("mydataset.mytable", loadData.getTable().getFullyQualifiedName());
+ Assertions.assertEquals(2, loadData.getFileOptions().size());
+ }
+
+ @Test
+ void testLoadDataOverwriteWithColumnDefinitions() throws JSQLParserException {
+ String sqlStr = "LOAD DATA OVERWRITE ds.t (id INT64, name STRING) "
+ + "FROM FILES (format = 'CSV', uris = ['gs://b/f.csv'])";
+ LoadDataStatement loadData =
+ (LoadDataStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertTrue(loadData.isOverwrite());
+ Assertions.assertEquals(2, loadData.getColumnDefinitions().size());
+ }
+
+ @Test
+ void testLoadDataWithOptionsAndConnection() throws JSQLParserException {
+ String sqlStr = "LOAD DATA INTO ds.t OPTIONS (description = 'x') "
+ + "FROM FILES (format = 'PARQUET') WITH PARTITION COLUMNS "
+ + "WITH CONNECTION myproject.us.conn";
+ LoadDataStatement loadData =
+ (LoadDataStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertEquals("myproject.us.conn", loadData.getConnectionName());
+ Assertions.assertTrue(loadData.isWithPartitionColumns());
+ }
+
+ @Test
+ void testLoadDataTablesNamesFinder() throws JSQLParserException {
+ String sqlStr = "LOAD DATA INTO ds.t FROM FILES (format = 'CSV')";
+
+ Assertions.assertEquals(java.util.Collections.singletonList("ds.t"),
+ new net.sf.jsqlparser.util.TablesNamesFinder()
+ .getTableList(net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(sqlStr)));
+ }
+
+ @Test
+ void testCreateSnapshotTableClone() throws JSQLParserException {
+ String sqlStr = "CREATE SNAPSHOT TABLE ds.snap CLONE ds.t";
+ CreateTable createTable =
+ (CreateTable) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+
+ Assertions.assertEquals("ds.t", createTable.getCloneTable().getFullyQualifiedName());
+ }
+
+ @Test
+ void testCreateSnapshotTableCloneForSystemTimeAsOf() throws JSQLParserException {
+ String sqlStr = "CREATE SNAPSHOT TABLE IF NOT EXISTS ds.snap CLONE ds.t "
+ + "FOR SYSTEM_TIME AS OF CURRENT_TIMESTAMP()";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testCreateTableCloneWithOptions() throws JSQLParserException {
+ String sqlStr = "CREATE TABLE ds.copy CLONE ds.t OPTIONS (description = 'x')";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
+
+ @Test
+ void testCloneRemainsUsableAsIdentifier() throws JSQLParserException {
+ String sqlStr = "SELECT clone FROM t";
+ TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
+ }
}