Skip to content

Commit 6521440

Browse files
authored
Merge pull request #5893 from sysown/test/port-pr5088-parsersql-cases
test(parsersql): port SET parser cases from PR 5088
2 parents 495553a + 32286db commit 6521440

2 files changed

Lines changed: 323 additions & 8 deletions

File tree

lib/Query_Processor_ParserSQL.cpp

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ static std::string strip_quotes(const std::string& s) {
7777
static std::string strip_scope_prefix(std::string var_name) {
7878
if (var_name.size() > 2 && var_name[0] == '@' && var_name[1] == '@') {
7979
var_name = var_name.substr(2);
80-
for (const char* prefix : {"session.", "local.", "global."}) {
81-
size_t plen = strlen(prefix); // NOSONAR: prefix is a string literal, strlen is evaluated at compile-time
80+
for (const char* prefix : {"session.", "local.", "global.", "persist.", "persist_only."}) {
81+
size_t plen = strlen(prefix); // NOSONAR: prefix is a string literal, strlen is evaluated at compile-time
8282
if (var_name.size() > plen &&
8383
strncasecmp(var_name.c_str(), prefix, plen) == 0) {
8484
var_name = var_name.substr(plen);
@@ -93,7 +93,7 @@ static std::string strip_scope_prefix(std::string var_name) {
9393
* Normalises a SET variable name for consistent lookup.
9494
*
9595
* Steps:
96-
* 1. Strip keyword scope prefix (SESSION/GLOBAL/LOCAL).
96+
* 1. Strip keyword scope prefix (SESSION/GLOBAL/LOCAL/PERSIST/PERSIST_ONLY).
9797
* 2. Strip @@-style scope prefix (@@session. → "").
9898
* 3. Lowercase the result.
9999
* 4. Resolve legacy aliases: "transaction_isolation" → "tx_isolation",
@@ -103,7 +103,7 @@ static std::string strip_scope_prefix(std::string var_name) {
103103
* wrote the SET statement, matching the behaviour of the regex-based parser.
104104
*/
105105
static std::string normalize_set_var_name(std::string var_name) {
106-
for (const char* prefix : {"SESSION ", "GLOBAL ", "LOCAL "}) {
106+
for (const char* prefix : {"SESSION ", "GLOBAL ", "LOCAL ", "PERSIST ", "PERSIST_ONLY "}) {
107107
size_t plen = strlen(prefix); // NOSONAR: prefix is a string literal, strlen is evaluated at compile-time
108108
if (var_name.size() > plen &&
109109
strncasecmp(var_name.c_str(), prefix, plen) == 0) {
@@ -150,6 +150,102 @@ static void skip_quoted_char(const char*& p, const char* end) {
150150
}
151151
}
152152

153+
static bool is_sql_space(char c) {
154+
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
155+
}
156+
157+
static std::string trim_copy(const char* start, const char* end) {
158+
while (start < end && is_sql_space(*start)) start++;
159+
while (end > start && is_sql_space(*(end - 1))) end--;
160+
return std::string(start, end);
161+
}
162+
163+
static bool is_single_sql_token_value(const std::string& s) {
164+
if (s.size() < 2) return false;
165+
char q = s.front();
166+
if (q != '\'' && q != '"' && q != '`') return false;
167+
const char* p = s.data() + 1;
168+
const char* end = s.data() + s.size();
169+
while (p < end) {
170+
if (*p == '\\' && p + 1 < end) {
171+
p += 2;
172+
continue;
173+
}
174+
if (*p == q) {
175+
p++;
176+
while (p < end && is_sql_space(*p)) p++;
177+
return p == end;
178+
}
179+
p++;
180+
}
181+
return false;
182+
}
183+
184+
static std::string strip_single_sql_token_quotes(const std::string& s) {
185+
if (!is_single_sql_token_value(s)) return s;
186+
size_t start = 1;
187+
size_t end = s.size() - 1;
188+
while (end > start && is_sql_space(s[end - 1])) end--;
189+
return s.substr(start, end - start);
190+
}
191+
192+
static bool is_assignment_operator_at(const char* p, const char* end) {
193+
if (p >= end) return false;
194+
if (*p == '=') return true;
195+
return *p == ':' && p + 1 < end && *(p + 1) == '=';
196+
}
197+
198+
static const char* skip_assignment_operator(const char* p, const char* end) {
199+
if (p < end && *p == ':' && p + 1 < end && *(p + 1) == '=') return p + 2;
200+
if (p < end && *p == '=') return p + 1;
201+
return p;
202+
}
203+
204+
static std::string extract_mysql_assignment_value(
205+
const char*& scan, const char* query, int query_len)
206+
{
207+
const char* qstart = query;
208+
const char* qend = query + query_len;
209+
if (!scan || scan < qstart || scan > qend) scan = qstart;
210+
211+
const char* p = scan;
212+
int depth = 0;
213+
while (p < qend) {
214+
if (*p == '\'' || *p == '"' || *p == '`') {
215+
skip_quoted_char(p, qend);
216+
} else if (*p == '(' || *p == '[') {
217+
depth++;
218+
} else if ((*p == ')' || *p == ']') && depth > 0) {
219+
depth--;
220+
} else if (depth == 0 && is_assignment_operator_at(p, qend)) {
221+
break;
222+
}
223+
p++;
224+
}
225+
if (p >= qend) return "";
226+
227+
const char* value_start = skip_assignment_operator(p, qend);
228+
while (value_start < qend && is_sql_space(*value_start)) value_start++;
229+
230+
p = value_start;
231+
depth = 0;
232+
while (p < qend) {
233+
if (*p == '\'' || *p == '"' || *p == '`') {
234+
skip_quoted_char(p, qend);
235+
} else if (*p == '(' || *p == '[') {
236+
depth++;
237+
} else if ((*p == ')' || *p == ']') && depth > 0) {
238+
depth--;
239+
} else if (depth == 0 && (*p == ',' || *p == ';')) {
240+
break;
241+
}
242+
p++;
243+
}
244+
245+
scan = (p < qend && *p == ',') ? p + 1 : p;
246+
return trim_copy(value_start, p);
247+
}
248+
153249
static std::string extract_paren_expr(const char* query, int query_len,
154250
const char* after_var) {
155251
if (!after_var || after_var >= query + query_len) return "";
@@ -528,7 +624,7 @@ static std::string resolve_var_value(
528624

529625
static std::string finalize_var_value(std::string val) {
530626
if (val == "''" || val == "\"\"") return "";
531-
return strip_quotes(val);
627+
return strip_single_sql_token_quotes(val);
532628
}
533629

534630
template <Dialect D>
@@ -569,6 +665,8 @@ static std::map<std::string, std::vector<std::string>> walk_set_stmt(
569665
std::map<std::string, std::vector<std::string>> result;
570666
if (!set_stmt) return result;
571667

668+
const char* mysql_assignment_scan = query;
669+
572670
for (const AstNode* child = set_stmt->first_child;
573671
child; child = child->next_sibling)
574672
{
@@ -604,11 +702,17 @@ static std::map<std::string, std::vector<std::string>> walk_set_stmt(
604702
std::vector<std::string> vals;
605703
for (const AstNode* rhs = target->next_sibling;
606704
rhs; rhs = rhs->next_sibling) {
607-
std::string raw = resolve_var_value<D>(
608-
target, rhs, query, query_len, arena);
609705
if constexpr (D == Dialect::PostgreSQL) {
706+
std::string raw = resolve_var_value<D>(
707+
target, rhs, query, query_len, arena);
610708
vals.push_back(std::move(raw));
611709
} else {
710+
std::string raw = extract_mysql_assignment_value(
711+
mysql_assignment_scan, query, query_len);
712+
if (raw.empty()) {
713+
raw = resolve_var_value<D>(
714+
target, rhs, query, query_len, arena);
715+
}
612716
vals.push_back(finalize_var_value(std::move(raw)));
613717
}
614718
}

0 commit comments

Comments
 (0)