@@ -77,8 +77,8 @@ static std::string strip_quotes(const std::string& s) {
7777static 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 */
105105static 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+
153249static 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
529625static 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
534630template <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