From 1c6d0f0b7e1dc26ff25b81c1279a4a4054dc5929 Mon Sep 17 00:00:00 2001 From: minleejae Date: Fri, 11 Sep 2026 05:51:06 +0000 Subject: [PATCH] Support SQL Server INSERT BULK declarations --- .../sf/jsqlparser/parser/feature/Feature.java | 2 +- .../statement/StatementFeatureVisitor.java | 8 + .../statement/StatementVisitor.java | 9 + .../statement/StatementVisitorAdapter.java | 8 + .../statement/insert/InsertBulk.java | 162 ++++++++++++++++++ .../sf/jsqlparser/util/TablesNamesFinder.java | 8 + .../util/deparser/StatementDeParser.java | 8 + .../validation/feature/SqlServerVersion.java | 1 + .../validator/InsertBulkValidator.java | 31 ++++ .../validator/StatementValidator.java | 7 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 75 ++++++++ src/site/sphinx/usage.rst | 7 + .../statement/insert/InsertBulkTest.java | 151 ++++++++++++++++ 13 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/sf/jsqlparser/statement/insert/InsertBulk.java create mode 100644 src/main/java/net/sf/jsqlparser/util/validation/validator/InsertBulkValidator.java create mode 100644 src/test/java/net/sf/jsqlparser/statement/insert/InsertBulkTest.java diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java index 9d21315559..f13f1b07c9 100644 --- a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java +++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java @@ -457,7 +457,7 @@ public enum Feature { */ oracleBlock, - execute, executeExec, executeCall, executeExecute, doStatement, + execute, executeExec, executeCall, executeExecute, doStatement, insertBulk, /** * SQL "EXECUTE" statement is allowed diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java index 7f13f5efcb..7ee627c3ed 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java @@ -61,6 +61,7 @@ import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.imprt.Import; import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.insert.InsertBulk; import net.sf.jsqlparser.statement.lock.LockStatement; import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; @@ -392,6 +393,13 @@ public Void visit(Insert insert, S context) { return super.visit(insert, context); } + @Override + public Void visit(InsertBulk statement, S context) { + analysis.claimTopLevel(); + analysis.certain(StmtFeature.MODIFIES_DATA); + return super.visit(statement, context); + } + @Override public Void visit(Update update, S context) { boolean topLevel = 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 2001533ae4..efc42f5ba8 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java @@ -56,6 +56,7 @@ import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.imprt.Import; import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.insert.InsertBulk; import net.sf.jsqlparser.statement.insert.ParenthesedInsert; import net.sf.jsqlparser.statement.lock.LockStatement; import net.sf.jsqlparser.statement.merge.Merge; @@ -70,6 +71,14 @@ public interface StatementVisitor { + default T visit(InsertBulk statement, S context) { + return null; + } + + default void visit(InsertBulk statement) { + this.visit(statement, null); + } + T visit(Analyze analyze, S context); default void visit(Analyze analyze) { diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java index e633adebb7..d39ccb8b2b 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java @@ -62,6 +62,7 @@ import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.imprt.Import; import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.insert.InsertBulk; import net.sf.jsqlparser.statement.insert.InsertConflictAction; import net.sf.jsqlparser.statement.insert.ParenthesedInsert; import net.sf.jsqlparser.statement.lock.LockStatement; @@ -314,6 +315,13 @@ private T visitReturningClause(ReturningClause returningClause, S context) { return null; } + @Override + public T visit(InsertBulk statement, S context) { + fromItemVisitor.visitFromItem(statement.getTable(), context); + statement.visitExpressions(expression -> expression.accept(expressionVisitor, context)); + return null; + } + @Override public T visit(ParenthesedInsert insert, S context) { insert.getInsert().accept(this, context); diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/InsertBulk.java b/src/main/java/net/sf/jsqlparser/statement/insert/InsertBulk.java new file mode 100644 index 0000000000..8464a4fa99 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/insert/InsertBulk.java @@ -0,0 +1,162 @@ +/*- + * #%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.insert; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import net.sf.jsqlparser.expression.Expression; +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.OrderByElement; + +/** SQL Server's SQL declaration preceding a separate bulk-load data stream. */ +public class InsertBulk implements Statement { + private Table table; + private List columns = new ArrayList<>(); + private List