diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java index 19e1ad471..5ca90ffc6 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -134,6 +134,7 @@ public class ParserKeywordsUtils { {"SELECT", RESTRICTED_ALIAS}, {"SEMI", RESTRICTED_JSQLPARSER}, {"SET", RESTRICTED_JSQLPARSER}, + {"SETTINGS", RESTRICTED_JSQLPARSER}, {"SOME", RESTRICTED_JSQLPARSER}, {"START", RESTRICTED_JSQLPARSER}, {"STATEMENT", RESTRICTED_JSQLPARSER}, 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 87bae64eb..2e1057987 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java @@ -9,13 +9,7 @@ */ package net.sf.jsqlparser.statement.select; -import net.sf.jsqlparser.expression.Alias; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.OracleHierarchicalExpression; -import net.sf.jsqlparser.expression.OracleHint; -import net.sf.jsqlparser.expression.PreferringClause; -import net.sf.jsqlparser.expression.WindowDefinition; -import net.sf.jsqlparser.schema.Table; +import static java.util.stream.Collectors.joining; import java.util.ArrayList; import java.util.Arrays; @@ -24,8 +18,14 @@ import java.util.Iterator; import java.util.List; import java.util.Optional; - -import static java.util.stream.Collectors.joining; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHierarchicalExpression; +import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.PreferringClause; +import net.sf.jsqlparser.expression.WindowDefinition; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.update.UpdateSet; @SuppressWarnings({"PMD.CyclomaticComplexity"}) public class PlainSelect extends Select { @@ -65,6 +65,7 @@ public class PlainSelect extends Select { private boolean isUsingOnly = false; private boolean useWithNoLog = false; private Table intoTempTable = null; + private List settings = null; public PlainSelect() {} @@ -323,6 +324,19 @@ public PlainSelect withIntoTempTable(Table intoTempTable) { return this; } + public List getSettings() { + return settings; + } + + public void setSettings(List settings) { + this.settings = settings; + } + + public PlainSelect withSettings(List settings) { + this.setSettings(settings); + return this; + } + @Override public T accept(SelectVisitor selectVisitor, S context) { return selectVisitor.visit(this, context); @@ -632,6 +646,11 @@ public String toString() { StringBuilder builder = new StringBuilder(); super.appendTo(builder); + if (settings != null && !settings.isEmpty()) { + builder.append(" SETTINGS "); + UpdateSet.appendUpdateSetsTo(builder, settings); + } + if (optimizeFor != null) { builder.append(optimizeFor); } @@ -779,6 +798,18 @@ public PlainSelect addJoins(Collection joins) { return this.withJoins(collection); } + public PlainSelect addSettings(UpdateSet... settings) { + List collection = Optional.ofNullable(getSettings()).orElseGet(ArrayList::new); + Collections.addAll(collection, settings); + return this.withSettings(collection); + } + + public PlainSelect addSettings(Collection settings) { + List collection = Optional.ofNullable(getSettings()).orElseGet(ArrayList::new); + collection.addAll(settings); + return this.withSettings(collection); + } + public E getFromItem(Class type) { return type.cast(getFromItem()); } 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 300f9d1c8..bb606a347 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -369,6 +369,10 @@ public StringBuilder visit(PlainSelect plainSelect, S context) { builder.append(" SKIP LOCKED"); } } + if (plainSelect.getSettings() != null && !plainSelect.getSettings().isEmpty()) { + builder.append(" SETTINGS "); + deparseUpdateSets(plainSelect.getSettings(), builder, expressionVisitor); + } if (plainSelect.getOptimizeFor() != null) { deparseOptimizeFor(plainSelect.getOptimizeFor()); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 662fd0300..391b67461 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -603,6 +603,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -4292,6 +4293,7 @@ PlainSelect PlainSelect() #PlainSelect: String windowName = null; WindowDefinition winDef; Table intoTempTable = null; + List settings = null; Distinct distinct; } { @@ -4408,6 +4410,7 @@ PlainSelect PlainSelect() #PlainSelect: [ LOOKAHEAD(2) ( { plainSelect.setNoWait(true); } | { plainSelect.setSkipLocked(true); }) ] ] + [ LOOKAHEAD(2) settings = UpdateSets() { plainSelect.setSettings(settings); } ] [ LOOKAHEAD() optimize = OptimizeFor() { plainSelect.setOptimizeFor(optimize); } ] [ LOOKAHEAD(3) intoTempTable = Table() { plainSelect.setIntoTempTable(intoTempTable);} ] [ LOOKAHEAD(3) { plainSelect.setUseWithNoLog(true); } ] diff --git a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java index 648c65835..de68cc32a 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java @@ -9,6 +9,8 @@ */ package net.sf.jsqlparser.statement.select; +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; + import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.parser.CCJSqlParserUtil; @@ -16,8 +18,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; -import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; - public class ClickHouseTest { @Test @@ -132,4 +132,21 @@ public void testParameterizedAggregateFunctionIssue2125() throws JSQLParserExcep Assertions.assertEquals(1, function.getParameters().size()); Assertions.assertEquals(1, function.getChainedParameters().size()); } + + @Test + public void testSettingsClauseIssue2362() throws JSQLParserException { + String sql = "SELECT *\nFROM events\nSETTINGS max_threads = 1"; + PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sql, true); + Assertions.assertNotNull(select.getSettings()); + Assertions.assertEquals(1, select.getSettings().size()); + Assertions.assertEquals("max_threads = 1", select.getSettings().get(0).toString()); + } + + @Test + public void testMultipleSettingsClauseIssue2362() throws JSQLParserException { + String sql = "SELECT * FROM events SETTINGS max_threads = 1, max_rows_to_read = 1000"; + PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sql, true); + Assertions.assertNotNull(select.getSettings()); + Assertions.assertEquals(2, select.getSettings().size()); + } }