relay_event_normalization/normalize/span/description/sql/
parser.rs

1//! Logic for parsing SQL queries and manipulating the resulting Abstract Syntax Tree.
2use std::borrow::Cow;
3use std::ops::ControlFlow;
4
5use itertools::Itertools;
6use sqlparser::ast::{
7    AlterTableOperation, Assignment, BinaryOperator, CloseCursor, ColumnDef, CopySource, Declare,
8    Expr, FunctionArg, Ident, LockTable, ObjectName, Query, Select, SelectItem, SetExpr,
9    ShowStatementFilter, Statement, TableAlias, TableConstraint, TableFactor, UnaryOperator, Value,
10    VisitMut, VisitorMut,
11};
12use sqlparser::dialect::{Dialect, GenericDialect};
13
14use crate::span::TABLE_NAME_REGEX;
15
16/// Keeps track of the maximum depth of an SQL expression that the [`NormalizeVisitor`] encounters.
17///
18/// This is used to prevent the serialization of the AST from crashing.
19/// Note that the expression depth does not fully cover the depth of complex SQL statements,
20/// because not everything in SQL is an expression.
21const MAX_EXPRESSION_DEPTH: usize = 64;
22
23/// Derive the SQL dialect from `db_system` (the value obtained from `span.data.system`)
24/// and try to parse the query into an AST.
25pub fn parse_query(
26    db_system: Option<&str>,
27    query: &str,
28) -> Result<Vec<Statement>, sqlparser::parser::ParserError> {
29    relay_log::with_scope(
30        |scope| {
31            scope.set_tag("db_system", db_system.unwrap_or_default());
32            scope.set_extra("query", query.into());
33        },
34        || match std::panic::catch_unwind(|| parse_query_inner(db_system, query)) {
35            Ok(res) => res,
36            Err(_) => Err(sqlparser::parser::ParserError::ParserError(
37                "panicked".to_string(),
38            )),
39        },
40    )
41}
42
43fn parse_query_inner(
44    db_system: Option<&str>,
45    query: &str,
46) -> Result<Vec<Statement>, sqlparser::parser::ParserError> {
47    // See https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md#notes-and-well-known-identifiers-for-dbsystem
48    //     https://docs.rs/sqlparser/latest/sqlparser/dialect/fn.dialect_from_str.html
49    let dialect = db_system
50        .and_then(sqlparser::dialect::dialect_from_str)
51        .unwrap_or_else(|| Box::new(GenericDialect {}));
52    let dialect = DialectWithParameters(dialect);
53
54    sqlparser::parser::Parser::parse_sql(&dialect, query)
55}
56
57/// Tries to parse a series of SQL queries into an AST and normalize it.
58pub fn normalize_parsed_queries(
59    db_system: Option<&str>,
60    string: &str,
61) -> Result<(String, Vec<Statement>), ()> {
62    let mut parsed = parse_query(db_system, string).map_err(|_| ())?;
63    parsed.visit(&mut NormalizeVisitor);
64    parsed.visit(&mut MaxDepthVisitor::new());
65
66    let concatenated = parsed
67        .iter()
68        .map(|statement| statement.to_string())
69        .join("; ");
70
71    // Insert placeholders that the SQL serializer cannot provide.
72    let replaced = concatenated.replace("___UPDATE_LHS___ = NULL", "..");
73
74    Ok((replaced, parsed))
75}
76
77/// A visitor that normalizes the SQL AST in-place.
78///
79/// Used for span description normalization.
80struct NormalizeVisitor;
81
82impl NormalizeVisitor {
83    /// Placeholder for string and numerical literals.
84    fn placeholder() -> Value {
85        Value::Number("%s".into(), false)
86    }
87
88    /// Placeholder for a list of items that has been scrubbed.
89    fn ellipsis() -> Ident {
90        Ident::new("..")
91    }
92
93    /// Check if a selected item can be erased into `..`.
94    /// We only collapse simple values (e.g. NULL) and columns (`col1` or `col1 AS foo`).
95    fn is_collapsible(item: &SelectItem) -> bool {
96        match item {
97            SelectItem::UnnamedExpr(expr) | SelectItem::ExprWithAlias { expr, .. } => {
98                matches!(
99                    expr,
100                    Expr::Value(_) | Expr::Identifier(_) | Expr::CompoundIdentifier(_)
101                )
102            }
103            _ => false,
104        }
105    }
106
107    /// Helper function to collapse a sequence of selected items into `..`.
108    fn collapse_items(collapse: &mut Vec<SelectItem>, output: &mut Vec<SelectItem>) {
109        match collapse.len() {
110            0 => {}
111            1 => {
112                output.append(collapse);
113            }
114            _ => {
115                output.push(SelectItem::UnnamedExpr(Expr::Identifier(Self::ellipsis())));
116            }
117        }
118    }
119
120    /// Normalizes `SELECT ...` queries.
121    fn transform_query(&mut self, query: &mut Query) {
122        if let SetExpr::Select(select) = &mut *query.body {
123            self.transform_select(&mut *select);
124        }
125    }
126
127    fn transform_select(&mut self, select: &mut Select) {
128        // Track collapsible selected items (e.g. `SELECT col1, col2`).
129        let mut collapse = vec![];
130
131        // Iterate over selected item.
132        for item in std::mem::take(&mut select.projection) {
133            // Normalize aliases.
134            let item = match item {
135                // Remove alias.
136                SelectItem::ExprWithAlias { expr, .. } => SelectItem::UnnamedExpr(expr),
137                // Strip prefix, e.g. `"mytable".*`.
138                SelectItem::QualifiedWildcard(_, options) => SelectItem::Wildcard(options),
139                _ => item,
140            };
141            if Self::is_collapsible(&item) {
142                collapse.push(item);
143            } else {
144                Self::collapse_items(&mut collapse, &mut select.projection);
145                collapse.clear();
146                select.projection.push(item);
147            }
148        }
149        Self::collapse_items(&mut collapse, &mut select.projection);
150    }
151
152    fn simplify_table_alias(alias: &mut Option<TableAlias>) {
153        if let Some(TableAlias { name, columns }) = alias {
154            Self::scrub_name(name);
155            for column in columns {
156                Self::scrub_name(column);
157            }
158        }
159    }
160
161    fn simplify_compound_identifier(parts: &mut Vec<Ident>) {
162        if let Some(mut last) = parts.pop() {
163            Self::scrub_name(&mut last);
164            *parts = vec![last];
165        }
166    }
167
168    fn scrub_name(name: &mut Ident) {
169        name.quote_style = None;
170        if let Cow::Owned(s) = TABLE_NAME_REGEX.replace_all(&name.value, "{%s}") {
171            name.value = s
172        };
173    }
174
175    fn erase_name(name: &mut Ident) {
176        name.quote_style = None;
177        name.value = "%s".into()
178    }
179
180    fn scrub_statement_filter(filter: &mut Option<ShowStatementFilter>) {
181        if let Some(s) = filter {
182            match s {
183                sqlparser::ast::ShowStatementFilter::Like(s)
184                | sqlparser::ast::ShowStatementFilter::ILike(s) => "%s".clone_into(s),
185                sqlparser::ast::ShowStatementFilter::Where(_) => {}
186            }
187        }
188    }
189}
190
191impl VisitorMut for NormalizeVisitor {
192    type Break = ();
193
194    fn pre_visit_relation(&mut self, relation: &mut ObjectName) -> ControlFlow<Self::Break> {
195        Self::simplify_compound_identifier(&mut relation.0);
196        ControlFlow::Continue(())
197    }
198
199    fn pre_visit_table_factor(
200        &mut self,
201        table_factor: &mut TableFactor,
202    ) -> ControlFlow<Self::Break> {
203        match table_factor {
204            TableFactor::Table { alias, .. } => {
205                Self::simplify_table_alias(alias);
206            }
207            TableFactor::Derived {
208                subquery, alias, ..
209            } => {
210                self.transform_query(subquery);
211                Self::simplify_table_alias(alias);
212            }
213            TableFactor::TableFunction { alias, .. } => {
214                Self::simplify_table_alias(alias);
215            }
216            TableFactor::UNNEST {
217                alias,
218                with_offset_alias,
219                ..
220            } => {
221                Self::simplify_table_alias(alias);
222                if let Some(ident) = with_offset_alias {
223                    Self::scrub_name(ident);
224                }
225            }
226            TableFactor::NestedJoin { alias, .. } => {
227                Self::simplify_table_alias(alias);
228            }
229            TableFactor::Pivot {
230                value_column,
231                alias,
232                ..
233            } => {
234                Self::simplify_compound_identifier(value_column);
235                Self::simplify_table_alias(alias);
236            }
237            TableFactor::Function {
238                name, args, alias, ..
239            } => {
240                Self::simplify_compound_identifier(&mut name.0);
241                for arg in args {
242                    if let FunctionArg::Named { name, .. } = arg {
243                        Self::scrub_name(name);
244                    }
245                }
246                Self::simplify_table_alias(alias);
247            }
248            TableFactor::JsonTable { columns, alias, .. } => {
249                for column in columns {
250                    Self::scrub_name(&mut column.name);
251                }
252                Self::simplify_table_alias(alias);
253            }
254            TableFactor::Unpivot {
255                value,
256                name,
257                columns,
258                alias,
259                ..
260            } => {
261                Self::scrub_name(value);
262                Self::scrub_name(name);
263                Self::simplify_compound_identifier(columns);
264                Self::simplify_table_alias(alias);
265            }
266        }
267        ControlFlow::Continue(())
268    }
269
270    fn pre_visit_expr(&mut self, expr: &mut Expr) -> ControlFlow<Self::Break> {
271        match expr {
272            // Simple values like numbers and strings are replaced by a placeholder:
273            Expr::Value(x) => *x = Self::placeholder(),
274            // `IN (val1, val2, val3)` is replaced by `IN (%s)`.
275            Expr::InList { list, .. } => *list = vec![Expr::Value(Self::placeholder())],
276            // `"table"."col"` is replaced by `col`.
277            Expr::CompoundIdentifier(parts) => {
278                Self::simplify_compound_identifier(parts);
279            }
280            // `"col"` is replaced by `col`.
281            Expr::Identifier(ident) => {
282                Self::scrub_name(ident);
283            }
284            // Recurse into subqueries.
285            Expr::Subquery(query) => self.transform_query(query),
286            // Remove negative sign, e.g. `-100` becomes `%s`.
287            Expr::UnaryOp {
288                op: UnaryOperator::Minus,
289                expr: inner,
290            } => {
291                if let Expr::Value(_) = **inner {
292                    *expr = Expr::Value(Self::placeholder())
293                }
294            }
295            // Simplify `CASE WHEN..` expressions.
296            Expr::Case {
297                operand,
298                conditions,
299                results,
300                else_result,
301            } => {
302                operand.take();
303                *conditions = vec![Expr::Identifier(Self::ellipsis())];
304                *results = vec![Expr::Identifier(Self::ellipsis())];
305                else_result.take();
306            }
307            _ => {}
308        }
309        ControlFlow::Continue(())
310    }
311
312    fn post_visit_expr(&mut self, expr: &mut Expr) -> ControlFlow<Self::Break> {
313        // Casts are omitted for simplification. Because we replace the entire expression,
314        // the replacement has to occur *after* visiting its children.
315        match expr {
316            Expr::Cast { expr: inner, .. } => {
317                *expr = take_expr(inner);
318            }
319            Expr::BinaryOp {
320                left,
321                op: op @ (BinaryOperator::Or | BinaryOperator::And),
322                right,
323            } => {
324                remove_redundant_parentheses(op, left);
325                remove_redundant_parentheses(op, right);
326                if left == right {
327                    //     /\
328                    //    /  \
329                    //   /\   B
330                    //  /  \
331                    // A    A
332                    *expr = take_expr(left);
333                } else {
334                    //     /\
335                    //    /  \
336                    //   /\   B
337                    //  /  \
338                    // A    B
339                    if let Expr::BinaryOp {
340                        left: left_left,
341                        op: left_op,
342                        right: left_right,
343                    } = left.as_mut()
344                    {
345                        if left_op == op && left_right == right {
346                            *left = Box::new(take_expr(left_left));
347                        }
348                    }
349                }
350            }
351            Expr::Nested(inner) if matches!(inner.as_ref(), &Expr::Nested(_)) => {
352                // Remove multiple levels of parentheses.
353                // These can occur because of the binary op reduction above.
354                *expr = take_expr(inner);
355            }
356            _ => (),
357        }
358
359        ControlFlow::Continue(())
360    }
361
362    fn post_visit_statement(&mut self, statement: &mut Statement) -> ControlFlow<Self::Break> {
363        match statement {
364            Statement::Query(query) => {
365                self.transform_query(query);
366            }
367            // `INSERT INTO col1, col2 VALUES (val1, val2)` becomes `INSERT INTO .. VALUES (%s)`.
368            Statement::Insert {
369                columns, source, ..
370            } => {
371                *columns = vec![Self::ellipsis()];
372                if let Some(source) = source.as_mut() {
373                    if let SetExpr::Values(v) = &mut *source.body {
374                        v.rows = vec![vec![Expr::Value(Self::placeholder())]]
375                    }
376                }
377            }
378            // Simple lists of col = value assignments are collapsed to `..`.
379            Statement::Update { assignments, .. } => {
380                if assignments.len() > 1
381                    && assignments
382                        .iter()
383                        .all(|a| matches!(a.value, Expr::Value(_)))
384                {
385                    *assignments = vec![Assignment {
386                        id: vec![Ident::new("___UPDATE_LHS___")],
387                        value: Expr::Value(Value::Null),
388                    }]
389                } else {
390                    for assignment in assignments.iter_mut() {
391                        Self::simplify_compound_identifier(&mut assignment.id);
392                    }
393                }
394            }
395            // `SAVEPOINT foo` becomes `SAVEPOINT %s`.
396            Statement::Savepoint { name } => Self::erase_name(name),
397            Statement::ReleaseSavepoint { name } => Self::erase_name(name),
398            Statement::Declare { stmts } => {
399                for Declare {
400                    names, for_query, ..
401                } in stmts
402                {
403                    for name in names {
404                        Self::erase_name(name);
405                    }
406                    if let Some(for_query) = for_query {
407                        self.transform_query(for_query.as_mut());
408                    }
409                }
410            }
411            Statement::Fetch { name, into, .. } => {
412                Self::erase_name(name);
413                if let Some(into) = into {
414                    into.0 = vec![Ident {
415                        value: "%s".into(),
416                        quote_style: None,
417                    }];
418                }
419            }
420            Statement::Close { cursor } => match cursor {
421                CloseCursor::All => {}
422                CloseCursor::Specific { name } => Self::erase_name(name),
423            },
424            Statement::AlterTable {
425                name, operations, ..
426            } => {
427                Self::simplify_compound_identifier(&mut name.0);
428                for operation in operations {
429                    match operation {
430                        AlterTableOperation::AddConstraint(c) => match c {
431                            TableConstraint::Unique { name, columns, .. } => {
432                                if let Some(name) = name {
433                                    Self::scrub_name(name);
434                                }
435                                for column in columns {
436                                    Self::scrub_name(column);
437                                }
438                            }
439                            TableConstraint::ForeignKey {
440                                name,
441                                columns,
442                                referred_columns,
443                                ..
444                            } => {
445                                if let Some(name) = name {
446                                    Self::scrub_name(name);
447                                }
448                                for column in columns {
449                                    Self::scrub_name(column);
450                                }
451                                for column in referred_columns {
452                                    Self::scrub_name(column);
453                                }
454                            }
455                            TableConstraint::Check { name, .. } => {
456                                if let Some(name) = name {
457                                    Self::scrub_name(name);
458                                }
459                            }
460                            TableConstraint::Index { name, columns, .. } => {
461                                if let Some(name) = name {
462                                    Self::scrub_name(name);
463                                }
464                                for column in columns {
465                                    Self::scrub_name(column);
466                                }
467                            }
468                            TableConstraint::FulltextOrSpatial {
469                                opt_index_name,
470                                columns,
471                                ..
472                            } => {
473                                if let Some(name) = opt_index_name {
474                                    Self::scrub_name(name);
475                                }
476                                for column in columns {
477                                    Self::scrub_name(column);
478                                }
479                            }
480                        },
481                        AlterTableOperation::AddColumn { column_def, .. } => {
482                            let ColumnDef { name, .. } = column_def;
483                            Self::scrub_name(name);
484                        }
485                        AlterTableOperation::DropConstraint { name, .. } => Self::scrub_name(name),
486                        AlterTableOperation::DropColumn { column_name, .. } => {
487                            Self::scrub_name(column_name)
488                        }
489                        AlterTableOperation::RenameColumn {
490                            old_column_name,
491                            new_column_name,
492                        } => {
493                            Self::scrub_name(old_column_name);
494                            Self::scrub_name(new_column_name);
495                        }
496                        AlterTableOperation::ChangeColumn {
497                            old_name, new_name, ..
498                        } => {
499                            Self::scrub_name(old_name);
500                            Self::scrub_name(new_name);
501                        }
502                        AlterTableOperation::RenameConstraint { old_name, new_name } => {
503                            Self::scrub_name(old_name);
504                            Self::scrub_name(new_name);
505                        }
506                        AlterTableOperation::AlterColumn { column_name, .. } => {
507                            Self::scrub_name(column_name);
508                        }
509                        _ => {}
510                    }
511                }
512            }
513            Statement::Analyze { columns, .. } => {
514                Self::simplify_compound_identifier(columns);
515            }
516            Statement::Truncate { .. } => {}
517            Statement::Msck { .. } => {}
518            Statement::Directory { path, .. } => {
519                "%s".clone_into(path);
520            }
521            Statement::Call(_) => {}
522            Statement::Copy { source, values, .. } => {
523                if let CopySource::Table { columns, .. } = source {
524                    Self::simplify_compound_identifier(columns);
525                }
526                *values = vec![Some("..".into())];
527            }
528            Statement::CopyIntoSnowflake {
529                from_stage_alias,
530                files,
531                pattern,
532                validation_mode,
533                ..
534            } => {
535                if let Some(from_stage_alias) = from_stage_alias {
536                    Self::scrub_name(from_stage_alias);
537                }
538                *files = None;
539                *pattern = None;
540                *validation_mode = None;
541            }
542            Statement::Delete { .. } => {}
543            Statement::CreateView {
544                columns,
545                cluster_by,
546                ..
547            } => {
548                for column in columns {
549                    Self::scrub_name(&mut column.name);
550                }
551                Self::simplify_compound_identifier(cluster_by);
552            }
553            Statement::CreateTable { .. } => {}
554            Statement::CreateVirtualTable {
555                module_name,
556                module_args,
557                ..
558            } => {
559                Self::scrub_name(module_name);
560                Self::simplify_compound_identifier(module_args);
561            }
562            Statement::CreateIndex {
563                name,
564                using,
565                include,
566                ..
567            } => {
568                // `table_name` is visited by `visit_relation`, but `name` is not.
569                if let Some(name) = name {
570                    Self::simplify_compound_identifier(&mut name.0);
571                }
572                if let Some(using) = using {
573                    Self::scrub_name(using);
574                }
575                Self::simplify_compound_identifier(include);
576            }
577            Statement::CreateRole { .. } => {}
578            Statement::AlterIndex { name, operation } => {
579                // Names here are not visited by `visit_relation`.
580                Self::simplify_compound_identifier(&mut name.0);
581                match operation {
582                    sqlparser::ast::AlterIndexOperation::RenameIndex { index_name } => {
583                        Self::simplify_compound_identifier(&mut index_name.0);
584                    }
585                }
586            }
587            Statement::AlterView { .. } => {}
588            Statement::AlterRole { name, .. } => {
589                Self::scrub_name(name);
590            }
591            Statement::AttachDatabase { schema_name, .. } => Self::scrub_name(schema_name),
592            Statement::Drop { .. } => {}
593            Statement::DropFunction { .. } => {}
594            Statement::CreateExtension { name, .. } => Self::scrub_name(name),
595            Statement::Flush { channel, .. } => *channel = None,
596            Statement::Discard { .. } => {}
597            Statement::SetRole { role_name, .. } => {
598                if let Some(role_name) = role_name {
599                    Self::scrub_name(role_name);
600                }
601            }
602            Statement::SetVariable { .. } => {}
603            Statement::SetTimeZone { .. } => {}
604            Statement::SetNames {
605                charset_name,
606                collation_name,
607            } => {
608                *charset_name = "%s".into();
609                *collation_name = None;
610            }
611            Statement::SetNamesDefault {} => {}
612            Statement::ShowFunctions { filter } => Self::scrub_statement_filter(filter),
613            Statement::ShowVariable { variable } => Self::simplify_compound_identifier(variable),
614            Statement::ShowVariables { filter, .. } => Self::scrub_statement_filter(filter),
615            Statement::ShowCreate { .. } => {}
616            Statement::ShowColumns { filter, .. } => Self::scrub_statement_filter(filter),
617            Statement::ShowTables {
618                db_name, filter, ..
619            } => {
620                if let Some(db_name) = db_name {
621                    Self::scrub_name(db_name);
622                }
623                Self::scrub_statement_filter(filter);
624            }
625            Statement::ShowCollation { filter } => Self::scrub_statement_filter(filter),
626            Statement::Use { db_name } => Self::scrub_name(db_name),
627            Statement::StartTransaction { .. } => {}
628            Statement::SetTransaction { .. } => {}
629            Statement::Comment { comment, .. } => *comment = None,
630            Statement::Commit { .. } => {}
631            Statement::Rollback { savepoint, .. } => {
632                if let Some(savepoint) = savepoint {
633                    Self::erase_name(savepoint);
634                }
635            }
636            Statement::CreateSchema { .. } => {}
637            Statement::CreateDatabase {
638                location,
639                managed_location,
640                ..
641            } => {
642                *location = None;
643                *managed_location = None;
644            }
645            Statement::CreateFunction { .. } => {}
646            Statement::CreateProcedure { .. } => {}
647            Statement::CreateMacro { .. } => {}
648            Statement::CreateStage { comment, .. } => *comment = None,
649            Statement::Assert { .. } => {}
650            Statement::Grant {
651                grantees,
652                granted_by,
653                ..
654            } => {
655                Self::simplify_compound_identifier(grantees);
656                *granted_by = None;
657            }
658            Statement::Revoke {
659                grantees,
660                granted_by,
661                ..
662            } => {
663                Self::simplify_compound_identifier(grantees);
664                *granted_by = None;
665            }
666            Statement::Deallocate { name, .. } => {
667                Self::scrub_name(name);
668            }
669            Statement::Execute { name, .. } => Self::scrub_name(name),
670            Statement::Prepare { name, .. } => Self::scrub_name(name),
671            Statement::Kill { id, .. } => *id = 0,
672            Statement::ExplainTable { .. } => {}
673            Statement::Explain { .. } => {}
674            Statement::Merge { .. } => {}
675            Statement::Cache { .. } => {}
676            Statement::UNCache { .. } => {}
677            Statement::CreateSequence { .. } => {}
678            Statement::CreateType { .. } => {}
679            Statement::Pragma { .. } => {}
680            Statement::LockTables { tables } => {
681                for table in tables {
682                    let LockTable {
683                        table,
684                        alias,
685                        lock_type: _,
686                    } = table;
687                    Self::scrub_name(table);
688                    if let Some(alias) = alias {
689                        Self::scrub_name(alias);
690                    }
691                }
692            }
693            Statement::UnlockTables => {}
694            Statement::Install { extension_name } | Statement::Load { extension_name } => {
695                Self::scrub_name(extension_name)
696            }
697            Statement::ShowStatus {
698                filter: _,
699                global: _,
700                session: _,
701            } => {}
702            Statement::Unload {
703                query: _,
704                to,
705                with: _,
706            } => {
707                Self::scrub_name(to);
708            }
709        }
710
711        ControlFlow::Continue(())
712    }
713}
714
715/// Get ownership of an `Expr` and leave a NULL value in its place.
716///
717/// We cannot use [`std::mem::take`], because [`Expr`] does not implement [`Default`].
718fn take_expr(expr: &mut Expr) -> Expr {
719    let mut swapped = Expr::Value(Value::Null);
720    std::mem::swap(&mut swapped, expr);
721    swapped
722}
723
724/// Removes parentheses for equal operators, e.g. `(a OR b) OR c`.
725///
726/// Only use this function on operations which have the
727/// [associative property](https://en.wikipedia.org/wiki/Associative_property).
728fn remove_redundant_parentheses(outer_op: &BinaryOperator, expr: &mut Expr) {
729    if let Expr::Nested(inner) = expr {
730        if let Expr::BinaryOp { op, .. } = inner.as_ref() {
731            if op == outer_op {
732                *expr = take_expr(inner.as_mut());
733            }
734        }
735    }
736}
737
738/// Limits the maximum expression depth of an SQL statement.
739///
740/// This prevents stack overflows when serializing the query back to a string.
741struct MaxDepthVisitor {
742    /// The current depth of an expression.
743    current_expr_depth: usize,
744}
745
746impl MaxDepthVisitor {
747    pub fn new() -> Self {
748        Self {
749            current_expr_depth: 0,
750        }
751    }
752}
753
754impl VisitorMut for MaxDepthVisitor {
755    type Break = ();
756
757    fn pre_visit_expr(&mut self, expr: &mut Expr) -> ControlFlow<Self::Break> {
758        if self.current_expr_depth > MAX_EXPRESSION_DEPTH {
759            *expr = Expr::Value(Value::Placeholder("..".to_owned()));
760            return ControlFlow::Continue(());
761        }
762        self.current_expr_depth += 1;
763        ControlFlow::Continue(())
764    }
765
766    fn post_visit_expr(&mut self, _expr: &mut Expr) -> ControlFlow<Self::Break> {
767        self.current_expr_depth = self.current_expr_depth.saturating_sub(1);
768        ControlFlow::Continue(())
769    }
770}
771
772/// An extension of an SQL dialect that accepts `?`, `%s`, `:c0` as valid input.
773#[derive(Debug)]
774struct DialectWithParameters(Box<dyn Dialect>);
775
776impl DialectWithParameters {
777    const PARAMETERS: &'static str = "?%:";
778}
779
780impl Dialect for DialectWithParameters {
781    fn dialect(&self) -> std::any::TypeId {
782        self.0.dialect()
783    }
784
785    fn is_identifier_start(&self, ch: char) -> bool {
786        Self::PARAMETERS.contains(ch) || self.0.is_identifier_start(ch)
787    }
788
789    fn is_identifier_part(&self, ch: char) -> bool {
790        self.0.is_identifier_part(ch)
791    }
792
793    fn is_delimited_identifier_start(&self, ch: char) -> bool {
794        self.0.is_delimited_identifier_start(ch)
795    }
796
797    fn is_proper_identifier_inside_quotes(
798        &self,
799        chars: std::iter::Peekable<std::str::Chars<'_>>,
800    ) -> bool {
801        self.0.is_proper_identifier_inside_quotes(chars)
802    }
803
804    fn supports_filter_during_aggregation(&self) -> bool {
805        self.0.supports_filter_during_aggregation()
806    }
807
808    fn supports_within_after_array_aggregation(&self) -> bool {
809        self.0.supports_within_after_array_aggregation()
810    }
811
812    fn supports_group_by_expr(&self) -> bool {
813        self.0.supports_group_by_expr()
814    }
815
816    fn supports_substring_from_for_expr(&self) -> bool {
817        self.0.supports_substring_from_for_expr()
818    }
819
820    fn parse_prefix(
821        &self,
822        parser: &mut sqlparser::parser::Parser,
823    ) -> Option<Result<Expr, sqlparser::parser::ParserError>> {
824        self.0.parse_prefix(parser)
825    }
826
827    fn parse_infix(
828        &self,
829        parser: &mut sqlparser::parser::Parser,
830        expr: &Expr,
831        precedence: u8,
832    ) -> Option<Result<Expr, sqlparser::parser::ParserError>> {
833        self.0.parse_infix(parser, expr, precedence)
834    }
835
836    fn get_next_precedence(
837        &self,
838        parser: &sqlparser::parser::Parser,
839    ) -> Option<Result<u8, sqlparser::parser::ParserError>> {
840        self.0.get_next_precedence(parser)
841    }
842
843    fn parse_statement(
844        &self,
845        parser: &mut sqlparser::parser::Parser,
846    ) -> Option<Result<Statement, sqlparser::parser::ParserError>> {
847        self.0.parse_statement(parser)
848    }
849}
850
851#[cfg(test)]
852mod tests {
853    use super::*;
854
855    #[test]
856    fn parse_deep_expression() {
857        let query = "SELECT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1";
858        assert_eq!(
859            normalize_parsed_queries(None, query).unwrap().0,
860            "SELECT .. + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s + %s"
861        );
862    }
863
864    #[test]
865    fn parse_dont_panic() {
866        assert!(parse_query_inner(None, "REPLACE g;'341234c").is_err());
867    }
868}