gianm commented on code in PR #17894:
URL: https://github.com/apache/druid/pull/17894#discussion_r2047759330


##########
docs/querying/sql.md:
##########
@@ -391,6 +391,26 @@ like `100` (denoting an integer), `100.0` (denoting a 
floating point value), or
 timestamps can be written like `TIMESTAMP '2000-01-01 00:00:00'`. Literal 
intervals, used for time arithmetic, can be
 written like `INTERVAL '1' HOUR`, `INTERVAL '1 02:03' DAY TO MINUTE`, 
`INTERVAL '1-2' YEAR TO MONTH`, and so on.
 
+## SET statements
+
+Druid SQL over HTTP supports including 0 or more `SET` statements separated by 
`;` preceding the statement to execute
+in order to assign [SQL query context parameter 
values](../querying/sql-query-context.md).
+
+The syntax of a `SET` statement is
+
+```sql
+SET identifier = literal

Review Comment:
   IMO, the example here should have a trailing `;` -- it's not technically 
part of the statement, but it is in practice always required (since `SET` can 
only appear before some other statement).



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidPlanner.java:
##########
@@ -283,6 +292,49 @@ public void close()
     planner.close();
   }
 
+  private SqlNode processStatementList(SqlNode root, String sql)
+  {
+    if (root instanceof SqlNodeList) {
+      final SqlNodeList nodeList = (SqlNodeList) root;
+      if (!allowSetStatementsToBuildContext && nodeList.size() > 1) {
+        throw InvalidSqlInput.exception("Multiple statements detected in SQL 
string[%s], but only a single statement is supported",

Review Comment:
   Echoing the entire string back in the error message probably isn't good, 
since they can be quite long and all the bits after the statement ("but only a 
single statement is supported") may be missed by the user. How about using a 
message like: "SQL query string must contain a single statement only".



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidPlanner.java:
##########
@@ -283,6 +292,49 @@ public void close()
     planner.close();
   }
 
+  private SqlNode processStatementList(SqlNode root, String sql)
+  {
+    if (root instanceof SqlNodeList) {
+      final SqlNodeList nodeList = (SqlNodeList) root;
+      if (!allowSetStatementsToBuildContext && nodeList.size() > 1) {
+        throw InvalidSqlInput.exception("Multiple statements detected in SQL 
string[%s], but only a single statement is supported",
+                                        sql
+        );
+      }
+      final Map<String, Object> contextMap = new LinkedHashMap<>();
+      boolean isMissingDruidStatementNode = true;
+      // convert 0 or more SET statements into a Map of stuff to add to the 
query context
+      for (int i = 0; i < nodeList.size(); i++) {
+        SqlNode sqlNode = nodeList.get(i);
+        if (sqlNode instanceof SqlSetOption) {
+          final SqlSetOption sqlSetOption = (SqlSetOption) sqlNode;
+          if (!(sqlSetOption.getValue() instanceof SqlLiteral)) {
+            throw InvalidSqlInput.exception("Invalid sql SET statement[%s], 
value must be a literal", sqlSetOption);

Review Comment:
   What does the interpolated `sqlSetOption` end up looking like? I'm just 
wondering if the error messages are going to be "nice".
   
   SQL should be capitalized, btw.
   
   I also wonder if it might be nicer to interpolate just the name of the 
parameter.



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidPlanner.java:
##########
@@ -283,6 +292,49 @@ public void close()
     planner.close();
   }
 
+  private SqlNode processStatementList(SqlNode root, String sql)
+  {
+    if (root instanceof SqlNodeList) {
+      final SqlNodeList nodeList = (SqlNodeList) root;
+      if (!allowSetStatementsToBuildContext && nodeList.size() > 1) {
+        throw InvalidSqlInput.exception("Multiple statements detected in SQL 
string[%s], but only a single statement is supported",
+                                        sql
+        );
+      }
+      final Map<String, Object> contextMap = new LinkedHashMap<>();
+      boolean isMissingDruidStatementNode = true;
+      // convert 0 or more SET statements into a Map of stuff to add to the 
query context
+      for (int i = 0; i < nodeList.size(); i++) {
+        SqlNode sqlNode = nodeList.get(i);
+        if (sqlNode instanceof SqlSetOption) {
+          final SqlSetOption sqlSetOption = (SqlSetOption) sqlNode;
+          if (!(sqlSetOption.getValue() instanceof SqlLiteral)) {
+            throw InvalidSqlInput.exception("Invalid sql SET statement[%s], 
value must be a literal", sqlSetOption);
+          }
+          final SqlLiteral value = (SqlLiteral) sqlSetOption.getValue();
+          contextMap.put(sqlSetOption.getName().getSimple(), 
SqlResults.coerce(plannerContext.getJsonMapper(), 
SqlResults.Context.fromPlannerContext(plannerContext), value.getValue(), 
value.getTypeName(), "set"));
+        } else if (i < nodeList.size() - 1) {
+          // only SET statements can appear before the last statement
+          throw InvalidSqlInput.exception("Invalid sql statement list[%s] - 
only SET statements are permitted before the final statement",
+                                          sql
+          );
+        } else {
+          // last SqlNode
+          root = sqlNode;
+          isMissingDruidStatementNode = false;
+        }
+      }
+      if (isMissingDruidStatementNode) {
+        throw InvalidSqlInput.exception(
+            "Invalid sql statement list[%s] - statement list must end with a 
statement that is not a SET",

Review Comment:
   Don't echo the entire `sql` in the error message for reasons discussed in 
other comments. SQL should be capitalized.



##########
docs/querying/sql-query-context.md:
##########
@@ -60,19 +60,31 @@ For more information, see [Overriding default query context 
values](../configura
 
 ## Set the query context
 
-You can configure query context parameters in the `context` object of the 
[JSON API](../api-reference/sql-api.md) or as a [JDBC connection properties 
object](../api-reference/sql-jdbc.md).
+You can configure query context parameters in the `context` object or as part 
of the `query` string with [`SET`

Review Comment:
   This has become kind of a run-on. Perhaps splitting it up into multiple 
sentences or a bulleted list would be good.



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidPlanner.java:
##########
@@ -114,14 +120,16 @@ public AuthResult(
       final FrameworkConfig frameworkConfig,
       final PlannerContext plannerContext,
       final SqlEngine engine,
-      final PlannerHook hook
+      final PlannerHook hook,
+      boolean allowSetStatementsToBuildContext

Review Comment:
   `final` please. It's good to be consistent and either use `final` on all 
fields or none of them.



##########
sql/src/test/java/org/apache/druid/sql/avatica/DruidAvaticaHandlerTest.java:
##########
@@ -339,7 +339,7 @@ public void tearDown() throws Exception
   public void testSelectCount() throws SQLException
   {
     try (Statement stmt = client.createStatement()) {
-      final ResultSet resultSet = stmt.executeQuery("SELECT COUNT(*) AS cnt 
FROM druid.foo");
+      final ResultSet resultSet = stmt.executeQuery("SELECT COUNT(*) AS cnt 
FROM druid.foo;");

Review Comment:
   nice



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidPlanner.java:
##########
@@ -283,6 +292,49 @@ public void close()
     planner.close();
   }
 
+  private SqlNode processStatementList(SqlNode root, String sql)
+  {
+    if (root instanceof SqlNodeList) {
+      final SqlNodeList nodeList = (SqlNodeList) root;
+      if (!allowSetStatementsToBuildContext && nodeList.size() > 1) {
+        throw InvalidSqlInput.exception("Multiple statements detected in SQL 
string[%s], but only a single statement is supported",
+                                        sql
+        );
+      }
+      final Map<String, Object> contextMap = new LinkedHashMap<>();
+      boolean isMissingDruidStatementNode = true;
+      // convert 0 or more SET statements into a Map of stuff to add to the 
query context
+      for (int i = 0; i < nodeList.size(); i++) {
+        SqlNode sqlNode = nodeList.get(i);
+        if (sqlNode instanceof SqlSetOption) {
+          final SqlSetOption sqlSetOption = (SqlSetOption) sqlNode;
+          if (!(sqlSetOption.getValue() instanceof SqlLiteral)) {
+            throw InvalidSqlInput.exception("Invalid sql SET statement[%s], 
value must be a literal", sqlSetOption);
+          }
+          final SqlLiteral value = (SqlLiteral) sqlSetOption.getValue();
+          contextMap.put(sqlSetOption.getName().getSimple(), 
SqlResults.coerce(plannerContext.getJsonMapper(), 
SqlResults.Context.fromPlannerContext(plannerContext), value.getValue(), 
value.getTypeName(), "set"));
+        } else if (i < nodeList.size() - 1) {
+          // only SET statements can appear before the last statement
+          throw InvalidSqlInput.exception("Invalid sql statement list[%s] - 
only SET statements are permitted before the final statement",

Review Comment:
   Don't echo the entire `sql` in the error message for reasons discussed in 
other comments. SQL should be capitalized.



##########
docs/querying/sql.md:
##########
@@ -391,6 +391,26 @@ like `100` (denoting an integer), `100.0` (denoting a 
floating point value), or
 timestamps can be written like `TIMESTAMP '2000-01-01 00:00:00'`. Literal 
intervals, used for time arithmetic, can be
 written like `INTERVAL '1' HOUR`, `INTERVAL '1 02:03' DAY TO MINUTE`, 
`INTERVAL '1-2' YEAR TO MONTH`, and so on.
 
+## SET statements
+
+Druid SQL over HTTP supports including 0 or more `SET` statements separated by 
`;` preceding the statement to execute
+in order to assign [SQL query context parameter 
values](../querying/sql-query-context.md).
+
+The syntax of a `SET` statement is

Review Comment:
   should have trailing `:`



##########
sql/src/main/java/org/apache/druid/sql/DirectStatement.java:
##########
@@ -245,6 +240,17 @@ public ResultSet plan()
     }
   }
 
+  protected DruidPlanner getPlanner()

Review Comment:
   nit: `createPlanner` is a better name; `getPlanner` suggests it's getting a 
single thing (but this method creates a new planner each time).



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidPlanner.java:
##########
@@ -283,6 +292,49 @@ public void close()
     planner.close();
   }
 
+  private SqlNode processStatementList(SqlNode root, String sql)

Review Comment:
   This method could use some javadoc about what is meant by "process" -- what 
is it looking for and what does it return?



##########
docs/querying/sql.md:
##########
@@ -391,6 +391,26 @@ like `100` (denoting an integer), `100.0` (denoting a 
floating point value), or
 timestamps can be written like `TIMESTAMP '2000-01-01 00:00:00'`. Literal 
intervals, used for time arithmetic, can be
 written like `INTERVAL '1' HOUR`, `INTERVAL '1 02:03' DAY TO MINUTE`, 
`INTERVAL '1-2' YEAR TO MONTH`, and so on.
 
+## SET statements
+
+Druid SQL over HTTP supports including 0 or more `SET` statements separated by 
`;` preceding the statement to execute
+in order to assign [SQL query context parameter 
values](../querying/sql-query-context.md).

Review Comment:
   This should clarify that `SET` statements only affect the 
immediately-following non-`SET` statement, and must be issued in the same HTTP 
call as the non-`SET` statement. Otherwise people may assume that `SET` affects 
all future statements they issue (kind of nonsensical since there are no 
sessions in our HTTP API, but people may assume it anyway).



##########
extensions-contrib/spectator-histogram/src/main/java/org/apache/druid/spectator/histogram/NullableOffsetsHeader.java:
##########
@@ -308,7 +308,7 @@ int getOffsetIndex(int index)
     offsetIndex += byteCardinality;
 
     // After getting the cumulative cardinality upto the 64 bit boundary 
immediately
-    // preceeding the 64 bits that contains our index, we need to accumulate 
the
+    // preceding the 64 bits that contains our index, we need to accumulate the

Review Comment:
   ideally these comment fixes would be in their own, different commit



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to