Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,8 @@ public enum Feature {
* @see SetStatement
*/
set,
/** SQL Server SET option [, option] ON | OFF. */
sqlServerSetOptions,
/**
* @see ResetStatement
*/
Expand Down
122 changes: 104 additions & 18 deletions src/main/java/net/sf/jsqlparser/statement/SetStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,84 @@

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.statement.select.PlainSelect;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

public final class SetStatement implements Statement {

private final List<NameExpr> values = new ArrayList<>();
private String effectParameter;
private OnOffOptions onOffOptions;

/** SQL Server options that share the SET option [, option] ON | OFF syntax. */
public enum OnOffOption {
QUOTED_IDENTIFIER, CONCAT_NULL_YIELDS_NULL, CURSOR_CLOSE_ON_COMMIT, ARITHABORT, ARITHIGNORE, FMTONLY, NOCOUNT, NOEXEC, NUMERIC_ROUNDABORT, PARSEONLY, ANSI_DEFAULTS, ANSI_NULL_DFLT_OFF, ANSI_NULL_DFLT_ON, ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, FORCEPLAN, SHOWPLAN_ALL, SHOWPLAN_TEXT, SHOWPLAN_XML, IMPLICIT_TRANSACTIONS, REMOTE_PROC_TRANSACTIONS, XACT_ABORT;

public static OnOffOption fromName(String name) {
for (OnOffOption option : values()) {
if (option.name().equalsIgnoreCase(name)) {
return option;
}
}
return null;
}
}

/** A group of options sharing one ON/OFF value; separate from assignment expressions. */
public static final class OnOffOptions implements Serializable {
private final List<OnOffOption> options;
private boolean on;

public OnOffOptions(Collection<OnOffOption> options, boolean on) {
this.options = new ArrayList<>(options);
if (this.options.isEmpty() || this.options.contains(null)) {
throw new IllegalArgumentException("At least one non-null SET option is required");
}
this.on = on;
}

public List<OnOffOption> getOptions() {
return options;
}

public boolean isOn() {
return on;
}

public void setOn(boolean on) {
this.on = on;
}

private StringBuilder appendTo(StringBuilder builder) {
if (options.isEmpty() || options.contains(null)) {
throw new IllegalStateException("Invalid SQL Server SET option group");
}
for (int i = 0; i < options.size(); i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(options.get(i));
}
return builder.append(on ? " ON" : " OFF");
}
}

public OnOffOptions getOnOffOptions() {
return onOffOptions;
}

/** Selects SQL Server option syntax, clearing any existing assignments and scope. */
public void setOnOffOptions(OnOffOptions onOffOptions) {
Objects.requireNonNull(onOffOptions, "onOffOptions");
clear();
this.onOffOptions = onOffOptions;
}

public SetStatement() {
// empty constructor
Expand All @@ -33,6 +99,7 @@ public SetStatement(Object name, ExpressionList<?> value) {
}

public void add(Object name, ExpressionList<?> value, boolean useEqual) {
onOffOptions = null;
values.add(new NameExpr(name, value, useEqual));
}

Expand Down Expand Up @@ -103,35 +170,53 @@ public void setExpressions(int idx, ExpressionList<?> expressions) {
values.get(idx).expressions = expressions;
}

private String toString(NameExpr ne) {
return ne.name + (ne.useEqual ? " = " : " ")
+ PlainSelect.getStringList(ne.expressions, true, false);
/** Shares statement punctuation with deparsers while allowing expression visitors. */
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionRenderer) {
builder.append("SET ");
if (onOffOptions != null) {
if (!values.isEmpty() || effectParameter != null) {
throw new IllegalStateException(
"SET options cannot be combined with assignments or scope");
}
return onOffOptions.appendTo(builder);
}
if (effectParameter != null) {
builder.append(effectParameter).append(" ");
}
for (int i = 0; i < values.size(); i++) {
if (i > 0) {
builder.append(", ");
}
appendAssignment(builder, values.get(i), expressionRenderer);
}
return builder;
}

@Override
public String toString() {
StringBuilder b = new StringBuilder("SET ");
if (effectParameter != null) {
b.append(effectParameter).append(" ");
}
boolean addComma = false;
for (NameExpr ne : values) {
if (addComma) {
b.append(", ");
} else {
addComma = true;
private static void appendAssignment(StringBuilder builder, NameExpr value,
Consumer<Expression> expressionRenderer) {
builder.append(value.name).append(value.useEqual ? " = " : " ");
if (value.expressions != null) {
for (int i = 0; i < value.expressions.size(); i++) {
if (i > 0) {
builder.append(", ");
}
expressionRenderer.accept((Expression) value.expressions.get(i));
}
b.append(toString(ne));
}
}

return b.toString();
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
return appendTo(builder, builder::append).toString();
}

public List<NameExpr> getKeyValuePairs() {
return values;
}

public void addKeyValuePairs(Collection<NameExpr> keyValuePairs) {
onOffOptions = null;
values.addAll(keyValuePairs);
}

Expand All @@ -140,6 +225,7 @@ public void addKeyValuePairs(NameExpr... keyValuePairs) {
}

public void clear() {
onOffOptions = null;
values.clear();
effectParameter = null;
}
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1733,7 +1733,9 @@ public void visit(Execute execute) {

@Override
public <S> Void visit(SetStatement setStatement, S context) {
throwUnsupported(setStatement);
if (setStatement.getOnOffOptions() == null) {
throwUnsupported(setStatement);
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@
*/
package net.sf.jsqlparser.util.deparser;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.statement.SetStatement;

import java.util.List;

public class SetStatementDeParser extends AbstractDeParser<SetStatement> {

Expand All @@ -27,28 +25,7 @@ public SetStatementDeParser(ExpressionVisitor<StringBuilder> expressionVisitor,

@Override
public void deParse(SetStatement set) {
builder.append("SET ");
if (set.getEffectParameter() != null) {
builder.append(set.getEffectParameter()).append(" ");
}
for (int i = 0; i < set.getCount(); i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(set.getName(i));
if (set.isUseEqual(i)) {
builder.append(" =");
}
builder.append(" ");
List<Expression> expressions = set.getExpressions(i);
for (int j = 0; j < expressions.size(); j++) {
if (j > 0) {
builder.append(", ");
}
expressions.get(j).accept(expressionVisitor, null);
}
}

set.appendTo(builder, expression -> expression.accept(expressionVisitor, null));
}

public ExpressionVisitor<StringBuilder> getExpressionVisitor() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public enum SqlServerVersion implements Version {
Feature.executeExec, Feature.executeExecute,

// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/set-local-variable-transact-sql?view=sql-server-ver15
Feature.set,
Feature.set, Feature.sqlServerSetOptions,

// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-table-transact-sql?view=sql-server-ver15
Feature.alterTable, // https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-sequence-transact-sql?view=sql-server-ver15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public class SetStatementValidator extends AbstractValidator<SetStatement> {
public void validate(SetStatement set) {
for (ValidationCapability c : getCapabilities()) {
validateFeature(c, Feature.set);
if (set.getOnOffOptions() != null) {
validateFeature(c, Feature.sqlServerSetOptions);
}
}
for (int i = 0; i < set.getCount(); i++) {
validateOptionalExpressions(set.getExpressions(i));
Expand Down
45 changes: 43 additions & 2 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -4068,7 +4068,22 @@ SessionStatement SessionStatement():
}

SetStatement Set(): {
String namePart;
SetStatement set;
}
{
<K_SET>
(
LOOKAHEAD({ Dialect.SQLSERVER.name().equals(getAsString(Feature.dialect))
&& getToken(1).kind == S_IDENTIFIER
&& SetStatement.OnOffOption.fromName(getToken(1).image) != null
&& !"=".equals(getToken(2).image) && !".".equals(getToken(2).image) })
set=SqlServerSetOnOffOptions()
| set=SetAssignments()
)
{ return set; }
}

SetStatement SetAssignments(): {
Object name;
ExpressionList expList;
boolean useEqual = false;
Expand All @@ -4078,7 +4093,6 @@ SetStatement Set(): {
String effectParameter = null;
}
{
<K_SET>
[LOOKAHEAD(3) (tk = <K_LOCAL> | tk = <K_SESSION>) {effectParameter = tk.image; } ]
(
LOOKAHEAD(2)
Expand Down Expand Up @@ -4126,6 +4140,33 @@ SetStatement Set(): {
{ return set; }
}

SetStatement SqlServerSetOnOffOptions(): {
SetStatement set = new SetStatement();
List<SetStatement.OnOffOption> options = new ArrayList<SetStatement.OnOffOption>();
SetStatement.OnOffOption option;
Token name;
boolean on;
}
{
name=<S_IDENTIFIER>
{
option = accessEnum(SetStatement.OnOffOption.class, name.image);
options.add(option);
}
(
"," name=<S_IDENTIFIER>
{
option = accessEnum(SetStatement.OnOffOption.class, name.image);
options.add(option);
}
)*
( <K_ON> { on = true; } | <K_OFF> { on = false; } )
{
set.setOnOffOptions(new SetStatement.OnOffOptions(options, on));
return set;
}
}

ResetStatement Reset(): {
String name;
ResetStatement reset;
Expand Down
6 changes: 6 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,12 @@ operators such as ``js#>>'{a}'`` and ``js#>'{a}'`` work without surrounding
spaces. Quote identifiers containing ``#``, for example ``"js#"``. Other
dialects retain their existing identifier and hash-comment rules.

With ``Dialect.SQLSERVER``, ``SET NOCOUNT ON`` and grouped boolean options such as
``SET QUOTED_IDENTIFIER, ANSI_NULLS OFF`` use ``SetStatement.getOnOffOptions()``.
The ordered ``OnOffOption`` list and shared ``isOn()`` value are editable;
``setOnOffOptions()`` replaces generic assignments and their scope. Both SQL
renderers share statement punctuation while generic assignments retain expression
visitor support. Parsing a SET directive records it without changing lexer settings.
``Dialect.SQLSERVER`` enables ``INSERT BULK table (name type, ...) WITH (...)``.
``InsertBulk`` exposes the target table, existing ``ColumnDefinition`` models,
and ordered typed options, including ``ROWS_PER_BATCH`` and ``ORDER`` keys.
Expand Down
Loading
Loading