|
| 1 | +use sqlparser::ast::{ |
| 2 | + BinaryOperator, Expr as SQLExpr, FunctionArg, FunctionArgExpr, FunctionArguments, Ident, |
| 3 | + ObjectName, ObjectNamePart, UnaryOperator, Value, ValueWithSpan, |
| 4 | +}; |
| 5 | + |
| 6 | +fn ident_to_string(ident: &Ident) -> String { |
| 7 | + ident.value.clone() |
| 8 | +} |
| 9 | + |
| 10 | +fn object_name_to_string(name: &ObjectName) -> String { |
| 11 | + name.0 |
| 12 | + .iter() |
| 13 | + .map(|part| match part { |
| 14 | + ObjectNamePart::Identifier(ident) => ident_to_string(ident), |
| 15 | + ObjectNamePart::Function(func) => func.name.value.clone(), |
| 16 | + }) |
| 17 | + .collect::<Vec<_>>() |
| 18 | + .join(".") |
| 19 | +} |
| 20 | + |
| 21 | +fn value_to_string(v: &Value) -> String { |
| 22 | + match v { |
| 23 | + Value::Number(n, _) => n.clone(), |
| 24 | + Value::Boolean(b) => b.to_string(), |
| 25 | + Value::Null => "null".to_string(), |
| 26 | + Value::SingleQuotedString(s) | Value::DoubleQuotedString(s) => format!("\"{s}\""), |
| 27 | + other => format!("{other}"), |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +fn value_with_span_to_string(v: &ValueWithSpan) -> String { |
| 32 | + value_to_string(&v.value) |
| 33 | +} |
| 34 | + |
| 35 | +fn binary_op_to_string(op: &BinaryOperator) -> String { |
| 36 | + match op { |
| 37 | + BinaryOperator::Plus => "+".to_string(), |
| 38 | + BinaryOperator::Minus => "-".to_string(), |
| 39 | + BinaryOperator::Multiply => "*".to_string(), |
| 40 | + BinaryOperator::Divide => "/".to_string(), |
| 41 | + BinaryOperator::Modulo => "%".to_string(), |
| 42 | + BinaryOperator::StringConcat => "||".to_string(), |
| 43 | + other => format!("{other}"), |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +fn unary_op_to_string(op: &UnaryOperator) -> String { |
| 48 | + match op { |
| 49 | + UnaryOperator::Plus => "+".to_string(), |
| 50 | + UnaryOperator::Minus => "-".to_string(), |
| 51 | + UnaryOperator::Not => "not".to_string(), |
| 52 | + other => format!("{other}"), |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +fn expr_to_string(e: &SQLExpr) -> String { |
| 57 | + match e { |
| 58 | + SQLExpr::Identifier(ident) => ident_to_string(ident), |
| 59 | + SQLExpr::CompoundIdentifier(idents) => idents |
| 60 | + .iter() |
| 61 | + .map(ident_to_string) |
| 62 | + .collect::<Vec<_>>() |
| 63 | + .join("."), |
| 64 | + SQLExpr::Value(v) => value_with_span_to_string(v), |
| 65 | + |
| 66 | + SQLExpr::UnaryOp { op, expr } => { |
| 67 | + let op = unary_op_to_string(op); |
| 68 | + format!("({op} {})", expr_to_string(expr)) |
| 69 | + } |
| 70 | + SQLExpr::BinaryOp { left, op, right } => { |
| 71 | + let op = binary_op_to_string(op); |
| 72 | + format!("({} {op} {})", expr_to_string(left), expr_to_string(right)) |
| 73 | + } |
| 74 | + |
| 75 | + SQLExpr::Nested(inner) => expr_to_string(inner), |
| 76 | + SQLExpr::Cast { |
| 77 | + expr, data_type, .. |
| 78 | + } => { |
| 79 | + format!("cast({} as {data_type})", expr_to_string(expr)) |
| 80 | + } |
| 81 | + |
| 82 | + SQLExpr::Function(func) => { |
| 83 | + let fn_name = object_name_to_string(&func.name).to_lowercase(); |
| 84 | + |
| 85 | + // Special-case COUNT(*) to preserve the star. |
| 86 | + if fn_name == "count" |
| 87 | + && matches!( |
| 88 | + &func.args, |
| 89 | + FunctionArguments::List(args) |
| 90 | + if args.args.len() == 1 |
| 91 | + && matches!(&args.args[0], FunctionArg::Unnamed(FunctionArgExpr::Wildcard)) |
| 92 | + ) |
| 93 | + { |
| 94 | + return "count(*)".to_string(); |
| 95 | + } |
| 96 | + |
| 97 | + let args: Vec<String> = match &func.args { |
| 98 | + FunctionArguments::None => vec![], |
| 99 | + FunctionArguments::Subquery(_) => vec!["<subquery>".to_string()], |
| 100 | + FunctionArguments::List(args) => args |
| 101 | + .args |
| 102 | + .iter() |
| 103 | + .map(|arg| match arg { |
| 104 | + FunctionArg::Named { arg, .. } | FunctionArg::ExprNamed { arg, .. } => { |
| 105 | + match arg { |
| 106 | + FunctionArgExpr::Expr(e) => expr_to_string(e), |
| 107 | + FunctionArgExpr::QualifiedWildcard(obj) => { |
| 108 | + format!("{}.*", object_name_to_string(obj)) |
| 109 | + } |
| 110 | + FunctionArgExpr::Wildcard => "*".to_string(), |
| 111 | + } |
| 112 | + } |
| 113 | + FunctionArg::Unnamed(arg) => match arg { |
| 114 | + FunctionArgExpr::Expr(e) => expr_to_string(e), |
| 115 | + FunctionArgExpr::QualifiedWildcard(obj) => { |
| 116 | + format!("{}.*", object_name_to_string(obj)) |
| 117 | + } |
| 118 | + FunctionArgExpr::Wildcard => "*".to_string(), |
| 119 | + }, |
| 120 | + }) |
| 121 | + .collect(), |
| 122 | + }; |
| 123 | + |
| 124 | + format!("{fn_name}({})", args.join(", ")) |
| 125 | + } |
| 126 | + |
| 127 | + // If we haven't normalized a specific expression yet, fall back to sqlparser's Display |
| 128 | + // to avoid changing the set of supported queries. |
| 129 | + other => format!("{other}"), |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +/// Returns a stable, user-facing name for an unnamed SQL projection expression. |
| 134 | +/// |
| 135 | +/// This is intended to be shared across SQL (AST -> name) and, in the future, |
| 136 | +/// DataFrame (DSL Expr -> name) so that default column names follow consistent rules. |
| 137 | +pub fn normalized_sql_expr_name(expr: &SQLExpr) -> String { |
| 138 | + expr_to_string(expr) |
| 139 | +} |
0 commit comments