From c9e929e2e6d4324dece4bf3f11dd89627af67583 Mon Sep 17 00:00:00 2001 From: Sam Jewell <2903904+samjewell@users.noreply.github.com> Date: Thu, 27 Mar 2025 11:32:48 +0000 Subject: [PATCH] SQL Expressions: Allow more MySQL AST node-types (#102973) * SQL Expressions: Add Null-literal node * Retain some order in the code - put NullVal with BoolVal * Add support for `IN` keyword * Add GROUP_CONCAT keyword * Add COLLATE keyword From Claude: The test case demonstrates a simple use of COLLATE with a string literal, but in real MySQL queries, COLLATE is often used in more complex scenarios like: 1. String comparisons: `SELECT * FROM table WHERE name COLLATE utf8mb4_bin = 'John'` 2. Sorting: `SELECT * FROM table ORDER BY name COLLATE utf8mb4_unicode_ci` 3. JOINs: `SELECT * FROM table1 JOIN table2 ON table1.name COLLATE utf8mb4_bin = table2.name` The COLLATE clause is particularly useful when you need case-sensitive comparisons (utf8mb4_bin) or specific language-aware sorting rules. --- pkg/expr/sql/parser_allow.go | 14 +++++++++++++- pkg/expr/sql/parser_allow_test.go | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pkg/expr/sql/parser_allow.go b/pkg/expr/sql/parser_allow.go index 21573b179e1..f19f1bc4e63 100644 --- a/pkg/expr/sql/parser_allow.go +++ b/pkg/expr/sql/parser_allow.go @@ -60,7 +60,7 @@ func allowedNode(node sqlparser.SQLNode) (b bool) { case *sqlparser.BinaryExpr, *sqlparser.UnaryExpr: return - case sqlparser.BoolVal: + case sqlparser.BoolVal, *sqlparser.NullVal: return case *sqlparser.CaseExpr, *sqlparser.When: @@ -81,6 +81,15 @@ func allowedNode(node sqlparser.SQLNode) (b bool) { case *sqlparser.ConvertExpr, *sqlparser.ConvertType: return + case *sqlparser.CollateExpr: + return + + case sqlparser.Exprs: + return + + case *sqlparser.GroupConcatExpr: + return + case sqlparser.GroupBy: return @@ -132,6 +141,9 @@ func allowedNode(node sqlparser.SQLNode) (b bool) { case *sqlparser.TrimExpr: return + case sqlparser.ValTuple: + return + case *sqlparser.With: return diff --git a/pkg/expr/sql/parser_allow_test.go b/pkg/expr/sql/parser_allow_test.go index 31b777edac2..36aca7b0c27 100644 --- a/pkg/expr/sql/parser_allow_test.go +++ b/pkg/expr/sql/parser_allow_test.go @@ -42,6 +42,26 @@ func TestAllowQuery(t *testing.T) { q: `SELECT * FROM a_table WHERE a_column IS NOT NULL`, err: nil, }, + { + name: "null literal", + q: `SELECT 1 as id, NULL as null_col`, + err: nil, + }, + { + name: "val tuple in read query", + q: `SELECT 1 WHERE 1 IN (1, 2, 3)`, + err: nil, + }, + { + name: "group concat in read query", + q: `SELECT 1 as id, GROUP_CONCAT('will_', 'concatenate') as concat_val`, + err: nil, + }, + { + name: "collate in read query", + q: `SELECT 'some text' COLLATE utf8mb4_bin`, + err: nil, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) {