1use crate::ast::{
71 Ast, BinaryOp, CaseArm, CreateViewRef, Distinct, Expr, ExprRef, JoinKind, LiteralKind, Nulls,
72 Order, OrderItem, Quantifier, QueryBody, QueryRef, SelectRef, SetOp, Slice, Source, SourceRef,
73 StrRef, Target, UnaryOp, WindowBound, WindowExclude, WindowRef, WindowUnit,
74};
75use crate::matcher::NONE;
76use crate::tokenize::quoted;
77
78#[must_use]
84pub fn create_view(ast: &Ast, index: CreateViewRef) -> String {
85 let written = ast.create_view(index);
86 let name = ast.name(written.name).last().unwrap_or_default();
87 let temporary = if written.temporary { "TEMP " } else { "" };
88 let mut out = format!("CREATE {temporary}VIEW {}", quoted(name));
89 if !written.columns.is_empty() {
90 out += &format!(" ({})", names(ast, written.columns));
93 }
94 out + &format!(" AS {};", query(ast, written.query))
95}
96
97#[must_use]
99pub fn query(ast: &Ast, index: QueryRef) -> String {
100 let held = ast.query(index);
101 let mut out = with(ast, held.ctes);
102 out += &match held.body {
103 QueryBody::Select(select) => selection(ast, select),
104 QueryBody::SetOp { op, quantifier, by_name, left, right } => {
105 setop(ast, op, quantifier, by_name, left, right)
106 }
107 QueryBody::Values(rows) => format!("SELECT * FROM ({}) AS valueslist", values(ast, rows)),
110 QueryBody::Describe(inner) => format!("DESCRIBE ({})", query(ast, inner)),
111 QueryBody::Show { name, .. } => format!("SHOW {}", ast.name_text(name)),
112 };
113 if held.order_by_all {
114 out += " ORDER BY COLUMNS(*)";
116 } else if !held.order_by.is_empty() {
117 let items: Vec<String> =
118 ast.order_list(held.order_by).iter().map(|item| order(ast, item)).collect();
119 out += &format!(" ORDER BY {}", items.join(", "));
120 }
121 if held.limit != NONE {
122 if held.limit_percent {
125 out += &format!(" LIMIT ({}) %", expr(ast, held.limit));
126 } else {
127 out += &format!(" LIMIT {}", expr(ast, held.limit));
128 }
129 }
130 if held.offset != NONE {
131 out += &format!(" OFFSET {}", expr(ast, held.offset));
132 }
133 out
134}
135
136fn with(ast: &Ast, ctes: Slice) -> String {
142 if ctes.is_empty() {
143 return String::new();
144 }
145 let written: Vec<String> = ast
146 .cte_list(ctes)
147 .iter()
148 .map(|&index| {
149 let held = ast.cte(index);
150 let columns = if held.columns.is_empty() {
151 String::new()
152 } else {
153 format!(" ({})", names(ast, held.columns))
154 };
155 format!(
156 "{}{columns} AS MATERIALIZED ({})",
157 quoted(ast.string(held.name)),
158 query(ast, held.query)
159 )
160 })
161 .collect();
162 format!("WITH {}", written.join(", "))
163}
164
165fn setop(
172 ast: &Ast,
173 op: SetOp,
174 quantifier: Quantifier,
175 by_name: bool,
176 left: QueryRef,
177 right: QueryRef,
178) -> String {
179 let word = match op {
180 SetOp::Union => "UNION",
181 SetOp::Except => "EXCEPT",
182 SetOp::Intersect => "INTERSECT",
183 };
184 let all = if matches!(quantifier, Quantifier::All) { " ALL" } else { "" };
186 let named = if by_name { " BY NAME" } else { "" };
187 format!("{}{word}{all}{named} {}", branch(ast, left, true), branch(ast, right, false))
188}
189
190fn branch(ast: &Ast, index: QueryRef, left: bool) -> String {
192 let text = query(ast, index);
193 if matches!(ast.query(index).body, QueryBody::SetOp { .. }) {
194 return text;
195 }
196 if left { format!("({text}) ") } else { format!("({text})") }
197}
198
199fn selection(ast: &Ast, index: SelectRef) -> String {
201 let held = ast.select(index);
202 let mut out = "SELECT".to_string();
203 match held.distinct {
204 Distinct::No => {}
205 Distinct::Yes => out += " DISTINCT",
206 Distinct::On(list) => out += &format!(" DISTINCT ON ({})", exprs(ast, list)),
207 }
208 let targets: Vec<String> =
209 ast.target_list(held.targets).iter().map(|target| aliased(ast, target)).collect();
210 out += &format!(" {}", targets.join(", "));
211 if !held.from.is_empty() {
212 let sources: Vec<String> =
214 ast.source_list(held.from).iter().map(|&index| source(ast, index)).collect();
215 out += &format!(" FROM {}", sources.join(" , "));
216 }
217 if held.filter != NONE {
218 out += &format!(" WHERE {}", expr(ast, held.filter));
219 }
220 if held.group_by_all {
221 out += " GROUP BY ALL";
222 } else if !held.group_by.is_empty() {
223 out += &format!(" GROUP BY {}", exprs(ast, held.group_by));
224 }
225 if held.having != NONE {
226 out += &format!(" HAVING {}", expr(ast, held.having));
227 }
228 out
229}
230
231fn aliased(ast: &Ast, target: &Target) -> String {
233 let written = expr(ast, target.expr);
234 if target.alias == NONE {
235 return written;
236 }
237 format!("{written} AS {}", quoted(ast.string(target.alias)))
238}
239
240fn order(ast: &Ast, item: &OrderItem) -> String {
242 let mut out = expr(ast, item.expr);
243 match item.order {
244 Order::Unstated => {}
245 Order::Ascending => out += " ASC",
246 Order::Descending => out += " DESC",
247 }
248 match item.nulls {
249 Nulls::Unstated => {}
250 Nulls::First => out += " NULLS FIRST",
251 Nulls::Last => out += " NULLS LAST",
252 }
253 out
254}
255
256fn source(ast: &Ast, index: SourceRef) -> String {
258 match ast.source(index) {
259 Source::Table { name, alias, columns } => label(ast, parts(ast, name), alias, columns),
260 Source::Cte { cte, alias, columns } => {
263 label(ast, quoted(ast.string(ast.cte(cte).name)), alias, columns)
264 }
265 Source::Subquery { query: inner, alias, columns } => {
266 label(ast, format!("({})", query(ast, inner)), alias, columns)
267 }
268 Source::Function { name, args, alias, columns, .. } => {
269 let written: Vec<String> =
270 ast.target_list(args).iter().map(|arg| argument(ast, arg)).collect();
271 let call = format!("{}({})", parts(ast, name), written.join(", "));
272 label(ast, call, alias, columns)
273 }
274 Source::Values { rows, alias, columns } => {
277 let inner = format!("(SELECT * FROM ({}) AS valueslist)", values(ast, rows));
278 label(ast, inner, alias, columns)
279 }
280 Source::Join { left, right, kind, natural, on, using } => {
281 let word = match kind {
282 JoinKind::Inner => "INNER",
283 JoinKind::Left => "LEFT",
284 JoinKind::Right => "RIGHT",
285 JoinKind::Full => "FULL",
287 JoinKind::Semi => "SEMI",
288 JoinKind::Anti => "ANTI",
289 JoinKind::Cross => "CROSS",
290 JoinKind::Positional => "POSITIONAL",
291 };
292 let natural = if natural { "NATURAL " } else { "" };
293 let mut out =
294 format!("({} {natural}{word} JOIN {}", source(ast, left), source(ast, right));
295 if on != NONE {
296 out += &format!(" ON ({})", expr(ast, on));
299 }
300 if !using.is_empty() {
301 out += &format!(" USING ({})", names(ast, using));
302 }
303 out + ")"
304 }
305 }
306}
307
308fn argument(ast: &Ast, arg: &Target) -> String {
314 if arg.alias == NONE {
315 return expr(ast, arg.expr);
316 }
317 format!("({} = {})", quoted(ast.string(arg.alias)), expr(ast, arg.expr))
318}
319
320fn label(ast: &Ast, written: String, alias: StrRef, columns: Slice) -> String {
322 let mut out = written;
323 if alias != NONE {
324 out += &format!(" AS {}", quoted(ast.string(alias)));
325 }
326 if !columns.is_empty() {
327 out += &format!("({})", names(ast, columns));
328 }
329 out
330}
331
332fn values(ast: &Ast, rows: Slice) -> String {
334 let written: Vec<String> =
335 ast.rows(rows).iter().map(|&row| format!("({})", exprs(ast, row))).collect();
336 format!("VALUES {}", written.join(", "))
337}
338
339pub fn expression(ast: &Ast, index: ExprRef) -> String {
345 expr(ast, index)
346}
347
348fn expr(ast: &Ast, index: ExprRef) -> String {
350 match ast.expr(index) {
351 Expr::Star { qualifier, replacements } => star(ast, qualifier, replacements),
352 Expr::Column { name } => parts(ast, name),
353 Expr::Literal { kind, text } => literal(ast, kind, text),
354 Expr::Unary { op, operand } => unary(ast, op, operand),
355 Expr::Binary { op, left, right } => binary(ast, op, left, right),
356 Expr::Function { name, args, distinct, filter } => call(ast, name, args, distinct, filter),
357 Expr::Window { name, args, distinct, filter, ignore_nulls, spec } => {
358 window(ast, name, args, distinct, filter, ignore_nulls, spec)
359 }
360 Expr::Cast { operand, ty, try_cast } => {
361 let word = if try_cast { "TRY_CAST" } else { "CAST" };
362 format!("{word}({} AS {})", expr(ast, operand), typename(ast.string(ty)))
363 }
364 Expr::Case { operand, arms, otherwise } => case(ast, operand, arms, otherwise),
365 Expr::Between { operand, low, high, negated } => {
366 let written = format!(
367 "({} BETWEEN {} AND {})",
368 expr(ast, operand),
369 expr(ast, low),
370 expr(ast, high)
371 );
372 if negated { format!("(NOT {written})") } else { written }
373 }
374 Expr::In { operand, list, negated } => {
375 let written = format!("({} IN ({}))", expr(ast, operand), exprs(ast, list));
376 if negated { format!("(NOT {written})") } else { written }
377 }
378 Expr::InSubquery { operand, query: inner, negated } => {
379 let any = format!("({} = ANY({}))", expr(ast, operand), query(ast, inner));
380 if negated { format!("(NOT {any})") } else { any }
381 }
382 Expr::QuantifiedSubquery { operand, op, query: inner, all } => {
383 let (op, negate) = if all { (negated_comparison(op), true) } else { (op, false) };
384 let word = comparison_word(op);
385 let any = format!("({} {word} ANY({}))", expr(ast, operand), query(ast, inner));
386 if negate { format!("(NOT {any})") } else { any }
387 }
388 Expr::Parameter { name } => format!("${}", ast.string(name)),
389 Expr::List { items } => format!("list_value({})", exprs(ast, items)),
391 Expr::Row { items } => format!("\"row\"({})", exprs(ast, items)),
394 Expr::Subquery { query: inner } => format!("({})", query(ast, inner)),
395 Expr::Exists { query: inner, negated } => {
396 let exists = format!("EXISTS({})", query(ast, inner));
397 if negated { format!("(NOT {exists})") } else { exists }
398 }
399 }
400}
401
402fn comparison_word(op: BinaryOp) -> &'static str {
403 match op {
404 BinaryOp::Eq => "=",
405 BinaryOp::NotEq => "!=",
406 BinaryOp::Lt => "<",
407 BinaryOp::Gt => ">",
408 BinaryOp::LtEq => "<=",
409 BinaryOp::GtEq => ">=",
410 _ => unreachable!("the grammar permits only a comparison before ANY or ALL"),
411 }
412}
413
414fn negated_comparison(op: BinaryOp) -> BinaryOp {
415 match op {
416 BinaryOp::Eq => BinaryOp::NotEq,
417 BinaryOp::NotEq => BinaryOp::Eq,
418 BinaryOp::Lt => BinaryOp::GtEq,
419 BinaryOp::Gt => BinaryOp::LtEq,
420 BinaryOp::LtEq => BinaryOp::Gt,
421 BinaryOp::GtEq => BinaryOp::Lt,
422 _ => unreachable!("the grammar permits only a comparison before ANY or ALL"),
423 }
424}
425
426fn star(ast: &Ast, qualifier: Slice, replacements: Slice) -> String {
428 let mut out =
429 if qualifier.is_empty() { "*".to_string() } else { format!("{}.*", parts(ast, qualifier)) };
430 if !replacements.is_empty() {
431 let written: Vec<String> =
432 ast.target_list(replacements).iter().map(|target| aliased(ast, target)).collect();
433 out += &format!(" REPLACE ({})", written.join(", "));
434 }
435 out
436}
437
438fn literal(ast: &Ast, kind: LiteralKind, text: StrRef) -> String {
440 match kind {
441 LiteralKind::Null => "NULL".to_string(),
444 LiteralKind::True => "true".to_string(),
445 LiteralKind::False => "false".to_string(),
446 LiteralKind::Number => number(ast.string(text)),
447 LiteralKind::String => string(ast.string(text)),
448 LiteralKind::Blob => format!("{}::BLOB", string(ast.string(text))),
451 }
452}
453
454fn number(written: &str) -> String {
463 let text = written.replace('_', "");
464 if text.contains(['e', 'E']) {
465 return double(&text);
466 }
467 let Some((whole, fraction)) = text.split_once('.') else {
468 return leading(&text).to_string();
469 };
470 if fraction.is_empty() {
473 return leading(whole).to_string();
474 }
475 format!("{}.{fraction}", if whole.is_empty() { "" } else { leading(whole) })
476}
477
478fn leading(digits: &str) -> &str {
480 let trimmed = digits.trim_start_matches('0');
481 if trimmed.is_empty() { &digits[digits.len().saturating_sub(1)..] } else { trimmed }
482}
483
484fn double(text: &str) -> String {
492 let Ok(value) = text.parse::<f64>() else {
493 return text.to_string();
494 };
495 let shortest = format!("{value:e}");
498 let (mantissa, exponent) = shortest.split_once('e').unwrap_or((shortest.as_str(), "0"));
499 let exponent: i32 = exponent.parse().unwrap_or(0);
500 if (-4..=15).contains(&exponent) {
501 let plain = format!("{value}");
502 return if plain.contains('.') { plain } else { plain + ".0" };
503 }
504 let sign = if exponent < 0 { '-' } else { '+' };
505 format!("{mantissa}e{sign}{:02}", exponent.abs())
506}
507
508fn string(text: &str) -> String {
513 format!("'{}'", text.replace('\'', "''"))
514}
515
516fn unary(ast: &Ast, op: UnaryOp, operand: ExprRef) -> String {
518 if matches!(op, UnaryOp::Negate) {
522 if let Some(number) = negated(ast, operand) {
523 return number;
524 }
525 }
526 let written = expr(ast, operand);
527 match op {
528 UnaryOp::Not => format!("(NOT {written})"),
529 UnaryOp::Negate => format!("-({written})"),
530 UnaryOp::Plus => format!("+({written})"),
531 UnaryOp::BitNot => format!("~({written})"),
532 UnaryOp::Factorial => format!("factorial({written})"),
534 UnaryOp::IsNull => format!("({written} IS NULL)"),
535 UnaryOp::IsNotNull => format!("({written} IS NOT NULL)"),
536 UnaryOp::IsUnknown => format!("({written} IS NULL)"),
538 UnaryOp::IsNotUnknown => format!("({written} IS NOT NULL)"),
539 UnaryOp::IsTrue => distinct(&written, "true", true),
543 UnaryOp::IsNotTrue => distinct(&written, "true", false),
544 UnaryOp::IsFalse => distinct(&written, "false", true),
545 UnaryOp::IsNotFalse => distinct(&written, "false", false),
546 }
547}
548
549fn distinct(operand: &str, against: &str, same: bool) -> String {
551 let word = if same { "IS NOT DISTINCT FROM" } else { "IS DISTINCT FROM" };
552 format!("(CAST({operand} AS BOOLEAN) {word} {against})")
553}
554
555fn negated(ast: &Ast, index: ExprRef) -> Option<String> {
560 match ast.expr(index) {
561 Expr::Literal { kind: LiteralKind::Number, text } => {
562 Some(format!("-{}", number(ast.string(text))))
563 }
564 Expr::Unary { op: UnaryOp::Negate, operand } => {
565 let inner = negated(ast, operand)?;
566 Some(inner.strip_prefix('-').unwrap_or(&inner).to_string())
567 }
568 _ => None,
569 }
570}
571
572fn binary(ast: &Ast, op: BinaryOp, left: ExprRef, right: ExprRef) -> String {
574 let (left, right) = (expr(ast, left), expr(ast, right));
575 match op {
577 BinaryOp::SimilarTo => return format!("regexp_full_match({left}, {right})"),
578 BinaryOp::NotSimilarTo => return format!("(NOT regexp_full_match({left}, {right}))"),
579 BinaryOp::AtTimeZone => return format!("timezone({right}, {left})"),
581 BinaryOp::Collate => return format!("{left} COLLATE {right}"),
583 _ => {}
584 }
585 let word = match op {
586 BinaryOp::Or => "OR",
587 BinaryOp::And => "AND",
588 BinaryOp::Eq => "=",
589 BinaryOp::NotEq => "!=",
590 BinaryOp::Lt => "<",
591 BinaryOp::Gt => ">",
592 BinaryOp::LtEq => "<=",
593 BinaryOp::GtEq => ">=",
594 BinaryOp::IsDistinctFrom => "IS DISTINCT FROM",
595 BinaryOp::IsNotDistinctFrom => "IS NOT DISTINCT FROM",
596 BinaryOp::Add => "+",
597 BinaryOp::Subtract => "-",
598 BinaryOp::Multiply => "*",
599 BinaryOp::Divide => "/",
600 BinaryOp::IntegerDivide => "//",
601 BinaryOp::Modulo => "%",
602 BinaryOp::Power => "**",
605 BinaryOp::BitAnd => "&",
606 BinaryOp::BitOr => "|",
607 BinaryOp::ShiftLeft => "<<",
608 BinaryOp::ShiftRight => ">>",
609 BinaryOp::Concat => "||",
610 BinaryOp::Like => "~~",
613 BinaryOp::NotLike => "!~~",
614 BinaryOp::ILike => "~~*",
615 BinaryOp::NotILike => "!~~*",
616 BinaryOp::Glob => "~~~",
617 BinaryOp::Regex => "~",
618 BinaryOp::NotRegex => "!~",
619 BinaryOp::RegexInsensitive => "~*",
620 BinaryOp::NotRegexInsensitive => "!~*",
621 BinaryOp::Arrow => "->",
622 BinaryOp::LongArrow => "->>",
623 BinaryOp::Contains => "@>",
624 BinaryOp::ContainedBy => "<@",
625 BinaryOp::Overlaps => "&&",
626 BinaryOp::StartsWith => "^@",
627 BinaryOp::InetContainedByOrEq => "<<=",
628 BinaryOp::InetContainsOrEq => ">>=",
629 BinaryOp::Named(name) => ast.string(name),
630 BinaryOp::SimilarTo | BinaryOp::NotSimilarTo | BinaryOp::AtTimeZone | BinaryOp::Collate => {
631 unreachable!("the four that return above")
632 }
633 };
634 format!("({left} {word} {right})")
635}
636
637fn call(ast: &Ast, name: Slice, args: Slice, distinct: bool, filter: ExprRef) -> String {
639 let written = parts(ast, name);
640 let list = ast.expr_list(args);
641 if written.eq_ignore_ascii_case("count") {
645 let starred = list.len() == 1
646 && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
647 if qualifier.is_empty() && replacements.is_empty());
648 if starred || list.is_empty() {
649 return format!("count_star(){}", filtered(ast, filter));
650 }
651 }
652 let word = if distinct { "DISTINCT " } else { "" };
653 format!(
654 "{}({word}{}){}",
655 operator(ast, name, &written),
656 exprs(ast, args),
657 filtered(ast, filter)
658 )
659}
660
661fn filtered(ast: &Ast, filter: ExprRef) -> String {
666 if filter == NONE { String::new() } else { format!(" FILTER (WHERE {})", expr(ast, filter)) }
667}
668
669fn window(
676 ast: &Ast,
677 name: Slice,
678 args: Slice,
679 distinct: bool,
680 filter: ExprRef,
681 ignore_nulls: bool,
682 spec: WindowRef,
683) -> String {
684 let word = if distinct { "DISTINCT " } else { "" };
685 let nulls = if ignore_nulls { " IGNORE NULLS" } else { "" };
687 let written = parts(ast, name);
688 let list = ast.expr_list(args);
692 let bare = list.len() == 1
693 && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
694 if qualifier.is_empty() && replacements.is_empty());
695 let inner = if bare { String::new() } else { exprs(ast, args) };
696 let call =
697 format!("{}({word}{inner}{nulls}){}", operator(ast, name, &written), filtered(ast, filter));
698 let held = ast.window(spec);
699 let mut inside: Vec<String> = Vec::new();
700 if !held.partition.is_empty() {
701 inside.push(format!("PARTITION BY {}", exprs(ast, held.partition)));
702 }
703 if !held.order.is_empty() {
704 let items: Vec<String> =
705 ast.order_list(held.order).iter().map(|item| order(ast, item)).collect();
706 inside.push(format!("ORDER BY {}", items.join(", ")));
707 }
708 if !held.frame_is_default() {
709 let unit = match held.unit {
710 WindowUnit::Rows => "ROWS",
711 WindowUnit::Range => "RANGE",
712 WindowUnit::Groups => "GROUPS",
713 };
714 let mut frame =
715 format!("{unit} BETWEEN {} AND {}", bound(ast, held.start), bound(ast, held.end));
716 frame += match held.exclude {
717 WindowExclude::NoOthers => "",
718 WindowExclude::CurrentRow => " EXCLUDE CURRENT ROW",
719 WindowExclude::Group => " EXCLUDE GROUP",
720 WindowExclude::Ties => " EXCLUDE TIES",
721 };
722 inside.push(frame);
723 }
724 format!("{call} OVER ({})", inside.join(" "))
725}
726
727fn bound(ast: &Ast, end: WindowBound) -> String {
729 match end {
730 WindowBound::UnboundedPreceding => "UNBOUNDED PRECEDING".to_string(),
731 WindowBound::Preceding(offset) => format!("{} PRECEDING", expr(ast, offset)),
732 WindowBound::CurrentRow => "CURRENT ROW".to_string(),
733 WindowBound::Following(offset) => format!("{} FOLLOWING", expr(ast, offset)),
734 WindowBound::UnboundedFollowing => "UNBOUNDED FOLLOWING".to_string(),
735 }
736}
737
738fn operator(ast: &Ast, name: Slice, written: &str) -> String {
745 let one = ast.name(name).next().unwrap_or_default();
746 let alone = ast.name(name).count() == 1;
747 if alone && (one.eq_ignore_ascii_case("coalesce") || one.eq_ignore_ascii_case("ifnull")) {
748 return "COALESCE".to_string();
749 }
750 written.to_string()
751}
752
753fn case(ast: &Ast, operand: ExprRef, arms: Slice, otherwise: ExprRef) -> String {
759 let mut out = "CASE ".to_string();
760 for arm in ast.arm_list(arms) {
761 let when = when(ast, operand, arm);
762 out += &format!(" WHEN ({when}) THEN ({})", expr(ast, arm.then));
763 }
764 let last = if otherwise == NONE { "NULL".to_string() } else { expr(ast, otherwise) };
765 out + &format!(" ELSE {last} END")
766}
767
768fn when(ast: &Ast, operand: ExprRef, arm: &CaseArm) -> String {
771 if operand == NONE {
772 return expr(ast, arm.when);
773 }
774 format!("({} = {})", expr(ast, operand), expr(ast, arm.when))
775}
776
777fn typename(text: &str) -> String {
791 let text = text.trim();
792 if let Some(open) = suffix(text) {
795 return typename(&text[..open]) + &text[open..];
796 }
797 let (base, arguments) = arguments(text);
798 let Some(name) = standard(base) else {
799 let base = unquote(base);
800 return match arguments {
801 Some(arguments) => format!("{}({arguments})", catalogued(&base)),
802 None => catalogued(&base),
803 };
804 };
805 match (name, arguments) {
806 ("STRUCT" | "UNION", Some(inside)) => {
809 let written: Vec<String> = pieces(inside).iter().map(|piece| field(piece)).collect();
810 format!("{name}({})", written.join(", "))
811 }
812 ("MAP", Some(inside)) => {
813 let written: Vec<String> = pieces(inside).iter().map(|piece| typename(piece)).collect();
814 format!("{name}({})", written.join(", "))
815 }
816 ("DECIMAL" | "VARCHAR", Some(inside)) => {
819 format!("{name}({})", pieces(inside).join(", "))
820 }
821 _ => name.to_string(),
824 }
825}
826
827fn catalogued(base: &str) -> String {
840 if base.eq_ignore_ascii_case("json") { quoted(base) } else { base.to_string() }
841}
842
843fn unquote(base: &str) -> String {
845 match base.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')) {
846 Some(inside) => inside.replace("\"\"", "\""),
847 None => base.to_string(),
848 }
849}
850
851fn suffix(text: &str) -> Option<usize> {
853 let rest = text.strip_suffix(']')?;
854 let open = rest.rfind('[')?;
855 rest[open + 1..].bytes().all(|byte| byte.is_ascii_digit()).then_some(open)
856}
857
858fn arguments(text: &str) -> (&str, Option<&str>) {
860 let Some(rest) = text.strip_suffix(')') else {
861 return (text, None);
862 };
863 let mut depth = 0usize;
864 for (at, byte) in rest.bytes().enumerate() {
865 match byte {
866 b'(' if depth == 0 => depth = 1,
867 b'(' => depth += 1,
868 b')' => depth -= 1,
869 _ => continue,
870 }
871 if depth == 1 && byte == b'(' {
872 return (rest[..at].trim(), Some(rest[at + 1..].trim()));
873 }
874 }
875 (text, None)
876}
877
878fn pieces(inside: &str) -> Vec<&str> {
880 let mut found = Vec::new();
881 let (mut depth, mut quoted, mut start) = (0usize, false, 0usize);
882 for (at, byte) in inside.bytes().enumerate() {
883 match byte {
884 b'"' => quoted = !quoted,
885 b'(' | b'[' if !quoted => depth += 1,
886 b')' | b']' if !quoted => depth = depth.saturating_sub(1),
887 b',' if !quoted && depth == 0 => {
888 found.push(inside[start..at].trim());
889 start = at + 1;
890 }
891 _ => {}
892 }
893 }
894 found.push(inside[start..].trim());
895 found
896}
897
898fn field(piece: &str) -> String {
900 let mut quoting = false;
901 for (at, byte) in piece.bytes().enumerate() {
902 match byte {
903 b'"' => quoting = !quoting,
904 byte if byte.is_ascii_whitespace() && !quoting => {
905 let name = piece[..at].trim();
906 let name =
907 if name.starts_with('"') { quoted(&unquote(name)) } else { name.to_string() };
908 return format!("{name} {}", typename(&piece[at + 1..]));
909 }
910 _ => {}
911 }
912 }
913 piece.to_string()
914}
915
916fn standard(base: &str) -> Option<&'static str> {
922 const NAMES: &[(&str, &str)] = &[
923 ("BOOLEAN", "BOOLEAN"),
924 ("INT", "INTEGER"),
925 ("INTEGER", "INTEGER"),
926 ("SMALLINT", "SMALLINT"),
927 ("BIGINT", "BIGINT"),
928 ("DEC", "DECIMAL"),
929 ("DECIMAL", "DECIMAL"),
930 ("NUMERIC", "DECIMAL"),
931 ("REAL", "FLOAT"),
932 ("FLOAT", "FLOAT"),
933 ("DOUBLE PRECISION", "DOUBLE"),
934 ("CHAR", "VARCHAR"),
935 ("CHARACTER", "VARCHAR"),
936 ("CHARACTER VARYING", "VARCHAR"),
937 ("NATIONAL CHARACTER", "VARCHAR"),
938 ("NATIONAL CHARACTER VARYING", "VARCHAR"),
939 ("VARCHAR", "VARCHAR"),
940 ("BIT", "BIT"),
941 ("DATE", "DATE"),
942 ("TIME", "TIME"),
943 ("TIME WITH TIME ZONE", "TIME WITH TIME ZONE"),
944 ("TIME WITHOUT TIME ZONE", "TIME"),
945 ("TIMESTAMP", "TIMESTAMP"),
946 ("TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH TIME ZONE"),
947 ("TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP"),
948 ("INTERVAL", "INTERVAL"),
949 ("STRUCT", "STRUCT"),
950 ("UNION", "UNION"),
951 ("MAP", "MAP"),
952 ];
953 let written: Vec<&str> = base.split_whitespace().collect();
954 let written = written.join(" ");
955 NAMES
956 .iter()
957 .find(|(spelling, _)| spelling.eq_ignore_ascii_case(&written))
958 .map(|(_, name)| *name)
959}
960
961fn exprs(ast: &Ast, list: Slice) -> String {
963 let written: Vec<String> = ast.expr_list(list).iter().map(|&item| expr(ast, item)).collect();
964 written.join(", ")
965}
966
967fn names(ast: &Ast, list: Slice) -> String {
969 ast.name(list).map(quoted).collect::<Vec<_>>().join(", ")
970}
971
972fn parts(ast: &Ast, list: Slice) -> String {
974 ast.name(list).map(quoted).collect::<Vec<_>>().join(".")
975}
976
977#[cfg(test)]
978mod tests {
979 use super::create_view;
980 use crate::ast::Statement;
981 use crate::transform::parse_ast;
982
983 fn whole(sql: &str) -> String {
985 let ast = parse_ast(sql).unwrap_or_else(|error| panic!("{sql} should parse: {error}"));
986 let Statement::CreateView(index) = ast.statements[0] else {
987 panic!("that was not a create view");
988 };
989 create_view(&ast, index)
990 }
991
992 fn body(query: &str) -> String {
994 let written = whole(&format!("CREATE VIEW v AS {query}"));
995 written
996 .strip_prefix("CREATE VIEW v AS ")
997 .and_then(|rest| rest.strip_suffix(';'))
998 .expect("the statement wrapper is there")
999 .to_string()
1000 }
1001
1002 #[test]
1003 fn a_statement_loses_its_qualification_and_its_or_replace() {
1004 assert_eq!(whole("CREATE VIEW main.v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1005 assert_eq!(whole("CREATE OR REPLACE VIEW v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1006 assert_eq!(whole("CREATE VIEW IF NOT EXISTS v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1007 assert_eq!(whole("CREATE TEMP VIEW v AS SELECT 1"), "CREATE TEMP VIEW v AS SELECT 1;");
1008 }
1009
1010 #[test]
1012 fn an_alias_list_is_written_with_a_space_in_front_of_it() {
1013 assert_eq!(
1014 whole(r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2"#),
1015 r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2;"#
1016 );
1017 }
1018
1019 #[test]
1020 fn comments_and_spacing_go_and_the_case_of_a_name_stays() {
1021 assert_eq!(
1022 whole("CREATE VIEW v AS SELECT X /* a note */ FROM T"),
1023 "CREATE VIEW v AS SELECT X FROM T;"
1024 );
1025 }
1026
1027 #[test]
1028 fn every_binary_operation_is_parenthesised_and_every_unary_one_parenthesises_its_operand() {
1029 assert_eq!(body("SELECT x + y * 2 - 1 FROM t"), "SELECT ((x + (y * 2)) - 1) FROM t");
1030 assert_eq!(
1031 body("SELECT x > 1 AND y < 2 OR b FROM t"),
1032 "SELECT (((x > 1) AND (y < 2)) OR b) FROM t"
1033 );
1034 assert_eq!(body("SELECT NOT b FROM t"), "SELECT (NOT b) FROM t");
1035 assert_eq!(body("SELECT ~x FROM t"), "SELECT ~(x) FROM t");
1036 assert_eq!(body("SELECT +x FROM t"), "SELECT +(x) FROM t");
1037 assert_eq!(body("SELECT -x FROM t"), "SELECT -(x) FROM t");
1038 }
1039
1040 #[test]
1043 fn a_minus_in_front_of_a_constant_folds_into_it() {
1044 assert_eq!(body("SELECT -1"), "SELECT -1");
1045 assert_eq!(body("SELECT - -3"), "SELECT 3");
1046 assert_eq!(body("SELECT +3"), "SELECT +(3)");
1047 }
1048
1049 #[test]
1050 fn the_null_tests_and_the_boolean_tests() {
1051 assert_eq!(body("SELECT x IS NULL FROM t"), "SELECT (x IS NULL) FROM t");
1052 assert_eq!(body("SELECT x ISNULL FROM t"), "SELECT (x IS NULL) FROM t");
1053 assert_eq!(body("SELECT x NOTNULL FROM t"), "SELECT (x IS NOT NULL) FROM t");
1054 assert_eq!(
1055 body("SELECT b IS TRUE FROM t"),
1056 "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM true) FROM t"
1057 );
1058 assert_eq!(
1059 body("SELECT b IS NOT TRUE FROM t"),
1060 "SELECT (CAST(b AS BOOLEAN) IS DISTINCT FROM true) FROM t"
1061 );
1062 assert_eq!(
1063 body("SELECT b IS FALSE FROM t"),
1064 "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM false) FROM t"
1065 );
1066 assert_eq!(body("SELECT b IS UNKNOWN FROM t"), "SELECT (b IS NULL) FROM t");
1067 assert_eq!(body("SELECT b IS NOT UNKNOWN FROM t"), "SELECT (b IS NOT NULL) FROM t");
1068 assert_eq!(
1069 body("SELECT x IS DISTINCT FROM y FROM t"),
1070 "SELECT (x IS DISTINCT FROM y) FROM t"
1071 );
1072 }
1073
1074 #[test]
1075 fn a_negated_between_or_in_is_a_not_around_the_plain_one() {
1076 assert_eq!(body("SELECT x BETWEEN 1 AND 10 FROM t"), "SELECT (x BETWEEN 1 AND 10) FROM t");
1077 assert_eq!(
1078 body("SELECT x NOT BETWEEN 1 AND 2 FROM t"),
1079 "SELECT (NOT (x BETWEEN 1 AND 2)) FROM t"
1080 );
1081 assert_eq!(body("SELECT x IN (1, 2, 3) FROM t"), "SELECT (x IN (1, 2, 3)) FROM t");
1082 assert_eq!(body("SELECT x NOT IN (1, 2) FROM t"), "SELECT (NOT (x IN (1, 2))) FROM t");
1083 assert_eq!(body("SELECT x IN (SELECT y FROM t)"), "SELECT (x = ANY(SELECT y FROM t))");
1084 assert_eq!(
1085 body("SELECT x NOT IN (SELECT y FROM t)"),
1086 "SELECT (NOT (x = ANY(SELECT y FROM t)))"
1087 );
1088 assert_eq!(body("SELECT x = ANY (SELECT y FROM t)"), "SELECT (x = ANY(SELECT y FROM t))");
1089 assert_eq!(
1090 body("SELECT x > ALL (SELECT y FROM t)"),
1091 "SELECT (NOT (x <= ANY(SELECT y FROM t)))"
1092 );
1093 }
1094
1095 #[test]
1098 fn the_pattern_operators_come_back_as_symbols() {
1099 assert_eq!(body("SELECT s LIKE 'a' FROM t"), "SELECT (s ~~ 'a') FROM t");
1100 assert_eq!(body("SELECT s NOT LIKE 'a' FROM t"), "SELECT (s !~~ 'a') FROM t");
1101 assert_eq!(body("SELECT s ILIKE 'a' FROM t"), "SELECT (s ~~* 'a') FROM t");
1102 assert_eq!(body("SELECT s NOT ILIKE 'a' FROM t"), "SELECT (s !~~* 'a') FROM t");
1103 assert_eq!(body("SELECT s GLOB 'a' FROM t"), "SELECT (s ~~~ 'a') FROM t");
1104 assert_eq!(body("SELECT s !~ 'a' FROM t"), "SELECT (s !~ 'a') FROM t");
1105 assert_eq!(
1106 body("SELECT s NOT SIMILAR TO 'a' FROM t"),
1107 "SELECT (NOT regexp_full_match(s, 'a')) FROM t"
1108 );
1109 }
1110
1111 #[test]
1112 fn collate_has_no_parentheses_and_the_rest_of_the_operators_keep_their_spelling() {
1113 assert_eq!(body("SELECT s COLLATE NOCASE FROM t"), "SELECT s COLLATE NOCASE FROM t");
1114 assert_eq!(body("SELECT x // y FROM t"), "SELECT (x // y) FROM t");
1115 assert_eq!(body("SELECT x || y FROM t"), "SELECT (x || y) FROM t");
1116 assert_eq!(body("SELECT x @> y FROM t"), "SELECT (x @> y) FROM t");
1117 assert_eq!(body("SELECT x <=> y FROM t"), "SELECT (x <=> y) FROM t");
1118 }
1119
1120 #[test]
1122 fn a_case_is_written_the_long_way_round() {
1123 assert_eq!(
1124 body("SELECT CASE WHEN x > 0 THEN 'a' WHEN x < 0 THEN 'b' ELSE 'c' END FROM t"),
1125 "SELECT CASE WHEN ((x > 0)) THEN ('a') WHEN ((x < 0)) THEN ('b') ELSE 'c' END FROM t"
1126 );
1127 assert_eq!(
1128 body("SELECT CASE x WHEN 1 THEN 'a' END FROM t"),
1129 "SELECT CASE WHEN ((x = 1)) THEN ('a') ELSE NULL END FROM t"
1130 );
1131 }
1132
1133 #[test]
1134 fn a_cast_writes_its_type_in_upper_case_with_a_space_after_the_comma() {
1135 assert_eq!(body("SELECT x::varchar FROM t"), "SELECT CAST(x AS VARCHAR) FROM t");
1136 assert_eq!(
1137 body("SELECT cast(x as decimal(4,1)) FROM t"),
1138 "SELECT CAST(x AS DECIMAL(4, 1)) FROM t"
1139 );
1140 assert_eq!(
1141 body("SELECT TRY_CAST(s AS INTEGER) FROM t"),
1142 "SELECT TRY_CAST(s AS INTEGER) FROM t"
1143 );
1144 }
1145
1146 #[test]
1148 fn a_standard_type_name_is_resolved_and_the_modifiers_it_was_written_with_survive() {
1149 let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
1150 assert_eq!(cast("int"), "SELECT CAST(x AS INTEGER)");
1151 assert_eq!(cast("numeric(5)"), "SELECT CAST(x AS DECIMAL(5))");
1152 assert_eq!(cast("decimal"), "SELECT CAST(x AS DECIMAL)");
1153 assert_eq!(cast("varchar(10)"), "SELECT CAST(x AS VARCHAR(10))");
1154 assert_eq!(cast("national character(2)"), "SELECT CAST(x AS VARCHAR(2))");
1155 assert_eq!(cast("float(10)"), "SELECT CAST(x AS FLOAT)");
1158 assert_eq!(cast("real"), "SELECT CAST(x AS FLOAT)");
1159 assert_eq!(cast("double precision"), "SELECT CAST(x AS DOUBLE)");
1160 assert_eq!(cast("time with time zone"), "SELECT CAST(x AS TIME WITH TIME ZONE)");
1161 assert_eq!(cast("int[]"), "SELECT CAST(x AS INTEGER[])");
1162 assert_eq!(cast("int[2][3]"), "SELECT CAST(x AS INTEGER[2][3])");
1163 assert_eq!(cast("map(int, varchar)"), "SELECT CAST(x AS MAP(INTEGER, VARCHAR))");
1164 assert_eq!(cast("union(a int)"), "SELECT CAST(x AS UNION(a INTEGER))");
1165 }
1166
1167 #[test]
1169 fn a_struct_field_keeps_its_name_and_its_type_goes_round_again() {
1170 assert_eq!(body("SELECT CAST(x AS struct(a bool))"), "SELECT CAST(x AS STRUCT(a bool))");
1171 assert_eq!(
1172 body("SELECT CAST(x AS struct(\"A b\" int))"),
1173 "SELECT CAST(x AS STRUCT(\"A b\" INTEGER))"
1174 );
1175 }
1176
1177 #[test]
1179 fn a_type_name_the_grammar_has_no_rule_for_keeps_the_case_it_was_written_in() {
1180 let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
1181 assert_eq!(cast("text"), "SELECT CAST(x AS text)");
1182 assert_eq!(cast("TEXT"), "SELECT CAST(x AS TEXT)");
1183 assert_eq!(cast("DOUBLE"), "SELECT CAST(x AS DOUBLE)");
1184 assert_eq!(cast("bool"), "SELECT CAST(x AS bool)");
1185 assert_eq!(cast("\"bool\""), "SELECT CAST(x AS bool)");
1186 assert_eq!(cast("int4[]"), "SELECT CAST(x AS int4[])");
1187 assert_eq!(cast("TIMESTAMPTZ"), "SELECT CAST(x AS TIMESTAMPTZ)");
1188 assert_eq!(cast("JSON"), "SELECT CAST(x AS \"JSON\")");
1190 assert_eq!(cast("json"), "SELECT CAST(x AS \"json\")");
1191 assert_eq!(cast("json[]"), "SELECT CAST(x AS \"json\"[])");
1192 assert_eq!(cast("struct(a json)"), "SELECT CAST(x AS STRUCT(a \"json\"))");
1193 }
1194
1195 #[test]
1196 fn a_star_count_is_a_function_of_its_own_and_a_list_is_a_call() {
1197 assert_eq!(body("SELECT count(*) FROM t"), "SELECT count_star() FROM t");
1198 assert_eq!(body("SELECT count() FROM t"), "SELECT count_star() FROM t");
1199 assert_eq!(body("SELECT count(DISTINCT x) FROM t"), "SELECT count(DISTINCT x) FROM t");
1200 assert_eq!(body("SELECT [1, 2, 3]"), "SELECT list_value(1, 2, 3)");
1201 assert_eq!(body("SELECT []"), "SELECT list_value()");
1202 }
1203
1204 #[test]
1207 fn a_function_name_is_quoted_when_it_is_a_keyword() {
1208 assert_eq!(body("SELECT nullif(x, 1) FROM t"), "SELECT \"nullif\"(x, 1) FROM t");
1209 assert_eq!(body("SELECT length(s) FROM t"), "SELECT length(s) FROM t");
1210 }
1211
1212 #[test]
1213 fn the_literals() {
1214 assert_eq!(body("SELECT NULL, TRUE, FALSE"), "SELECT NULL, true, false");
1215 assert_eq!(body("SELECT 1.50, .5, 1_000"), "SELECT 1.50, .5, 1000");
1216 assert_eq!(body("SELECT 'it''s'"), "SELECT 'it''s'");
1217 }
1218
1219 #[test]
1221 fn a_number_is_written_back_as_the_value_the_shape_of_it_made() {
1222 assert_eq!(body("SELECT 007, 1_000"), "SELECT 7, 1000");
1223 assert_eq!(body("SELECT 1.50, 00.5, 1., 0.0"), "SELECT 1.50, 0.5, 1, 0.0");
1224 assert_eq!(body("SELECT 1e3, 1.5e2, 1e-3, 5e-4"), "SELECT 1000.0, 150.0, 0.001, 0.0005");
1225 assert_eq!(body("SELECT 5e-5, 2.5e-5, 1e-10"), "SELECT 5e-05, 2.5e-05, 1e-10");
1226 assert_eq!(body("SELECT 1e15, 1e16, 1e100"), "SELECT 1000000000000000.0, 1e+16, 1e+100");
1227 }
1228
1229 #[test]
1231 fn an_extract_is_a_date_part_call_and_the_keyword_it_named_has_one_spelling() {
1232 assert_eq!(body("SELECT extract(year FROM d)"), "SELECT date_part('YEAR', d)");
1233 assert_eq!(body("SELECT extract(years FROM d)"), "SELECT date_part('YEAR', d)");
1234 assert_eq!(body("SELECT extract(seconds FROM d)"), "SELECT date_part('SECOND', d)");
1235 assert_eq!(
1237 body("SELECT extract(millisecond FROM d)"),
1238 "SELECT date_part('MILLISECONDS', d)"
1239 );
1240 assert_eq!(
1241 body("SELECT extract(microseconds FROM d)"),
1242 "SELECT date_part('MICROSECONDS', d)"
1243 );
1244 assert_eq!(body("SELECT extract(millennia FROM d)"), "SELECT date_part('MILLENNIUM', d)");
1245 assert_eq!(body("SELECT extract(epoch FROM d)"), "SELECT date_part('epoch', d)");
1247 assert_eq!(body("SELECT extract(dow FROM d)"), "SELECT date_part('dow', d)");
1248 }
1249
1250 #[test]
1252 fn coalesce_and_ifnull_are_one_operator_and_it_is_written_in_upper_case() {
1253 assert_eq!(body("SELECT coalesce(x, y)"), "SELECT COALESCE(x, y)");
1254 assert_eq!(body("SELECT IfNull(x, y)"), "SELECT COALESCE(x, y)");
1255 assert_eq!(body("SELECT coalesce(x)"), "SELECT COALESCE(x)");
1257 assert_eq!(body("SELECT nullif(x, y)"), "SELECT \"nullif\"(x, y)");
1259 assert_eq!(body("SELECT greatest(x, y)"), "SELECT greatest(x, y)");
1260 }
1261
1262 #[test]
1263 fn the_modifiers_hang_off_the_query_and_not_off_the_select() {
1264 assert_eq!(body("SELECT x FROM t LIMIT 5 OFFSET 2"), "SELECT x FROM t LIMIT 5 OFFSET 2");
1265 assert_eq!(body("SELECT x FROM t LIMIT 10 PERCENT"), "SELECT x FROM t LIMIT (10) %");
1266 assert_eq!(
1267 body("SELECT x FROM t ORDER BY x ASC, y NULLS LAST"),
1268 "SELECT x FROM t ORDER BY x ASC, y NULLS LAST"
1269 );
1270 assert_eq!(body("SELECT x FROM t ORDER BY ALL"), "SELECT x FROM t ORDER BY COLUMNS(*)");
1271 assert_eq!(body("SELECT x FROM t GROUP BY ALL"), "SELECT x FROM t GROUP BY ALL");
1272 assert_eq!(
1273 body("SELECT x FROM t GROUP BY x HAVING x > 0"),
1274 "SELECT x FROM t GROUP BY x HAVING (x > 0)"
1275 );
1276 assert_eq!(
1277 body("SELECT DISTINCT ON (x) x, y FROM t"),
1278 "SELECT DISTINCT ON (x) x, y FROM t"
1279 );
1280 }
1281
1282 #[test]
1285 fn a_chain_of_set_operations_loses_a_space_in_the_middle() {
1286 assert_eq!(
1287 body("SELECT x FROM t UNION ALL SELECT y FROM t"),
1288 "(SELECT x FROM t) UNION ALL (SELECT y FROM t)"
1289 );
1290 assert_eq!(
1291 body("SELECT x FROM t UNION SELECT y FROM t UNION SELECT 1"),
1292 "(SELECT x FROM t) UNION (SELECT y FROM t)UNION (SELECT 1)"
1293 );
1294 assert_eq!(
1295 body("SELECT x FROM t UNION DISTINCT SELECT y FROM t"),
1296 "(SELECT x FROM t) UNION (SELECT y FROM t)"
1297 );
1298 }
1299
1300 #[test]
1301 fn a_values_body_is_wrapped_in_a_select_that_names_it() {
1302 assert_eq!(
1303 body("VALUES (1, 'a'), (2, 'b')"),
1304 "SELECT * FROM (VALUES (1, 'a'), (2, 'b')) AS valueslist"
1305 );
1306 }
1307
1308 #[test]
1310 fn a_from_list_has_a_space_before_the_comma() {
1311 assert_eq!(body("SELECT 1 FROM t AS t1, t AS t2"), "SELECT 1 FROM t AS t1 , t AS t2");
1312 }
1313
1314 #[test]
1315 fn a_from_item_and_its_aliases() {
1316 assert_eq!(body("SELECT 1 FROM t AS r(n)"), "SELECT 1 FROM t AS r(n)");
1317 assert_eq!(body("SELECT 1 FROM main.t"), "SELECT 1 FROM main.t");
1318 assert_eq!(
1319 body("SELECT 1 FROM (SELECT x FROM t) AS sub"),
1320 "SELECT 1 FROM (SELECT x FROM t) AS sub"
1321 );
1322 assert_eq!(body("SELECT 1 FROM range(10)"), "SELECT 1 FROM \"range\"(10)");
1323 }
1324
1325 #[test]
1328 fn a_join_is_parenthesised_and_so_is_its_condition_twice() {
1329 assert_eq!(
1330 body("SELECT 1 FROM t AS a JOIN t AS b ON a.x = b.y"),
1331 "SELECT 1 FROM (t AS a INNER JOIN t AS b ON ((a.x = b.y)))"
1332 );
1333 assert_eq!(
1334 body("SELECT 1 FROM t LEFT JOIN t AS u USING (x)"),
1335 "SELECT 1 FROM (t LEFT JOIN t AS u USING (x))"
1336 );
1337 assert_eq!(
1338 body("SELECT 1 FROM t CROSS JOIN t AS u"),
1339 "SELECT 1 FROM (t CROSS JOIN t AS u)"
1340 );
1341 assert_eq!(
1342 body("SELECT 1 FROM t FULL OUTER JOIN t AS u ON t.x = u.x"),
1343 "SELECT 1 FROM (t FULL JOIN t AS u ON ((t.x = u.x)))"
1344 );
1345 assert_eq!(
1346 body("SELECT 1 FROM t NATURAL JOIN t AS u"),
1347 "SELECT 1 FROM (t NATURAL INNER JOIN t AS u)"
1348 );
1349 assert_eq!(
1350 body("SELECT 1 FROM t POSITIONAL JOIN t AS u"),
1351 "SELECT 1 FROM (t POSITIONAL JOIN t AS u)"
1352 );
1353 }
1354
1355 #[test]
1356 fn a_target_keeps_its_alias_and_a_star_keeps_its_replace_list() {
1357 assert_eq!(body("SELECT 1 + 2 AS \"quoted alias\""), "SELECT (1 + 2) AS \"quoted alias\"");
1358 assert_eq!(body("SELECT x AS \"select\" FROM t"), "SELECT x AS \"select\" FROM t");
1359 assert_eq!(body("SELECT t.* FROM t"), "SELECT t.* FROM t");
1360 assert_eq!(
1361 body("SELECT * REPLACE (x + 1 AS x) FROM t"),
1362 "SELECT * REPLACE ((x + 1) AS x) FROM t"
1363 );
1364 }
1365
1366 #[test]
1367 fn a_describe_gets_parentheses_round_what_it_describes() {
1368 assert_eq!(body("DESCRIBE SELECT 1"), "DESCRIBE (SELECT 1)");
1369 }
1370
1371 #[test]
1374 fn a_window_is_written_with_the_parts_that_were_written_in_it() {
1375 assert_eq!(
1376 body("SELECT row_number() OVER () AS n FROM t"),
1377 "SELECT row_number() OVER () AS n FROM t"
1378 );
1379 assert_eq!(
1380 body(
1381 "SELECT row_number() OVER (PARTITION BY a ORDER BY b DESC NULLS FIRST) AS n FROM t"
1382 ),
1383 "SELECT row_number() OVER (PARTITION BY a ORDER BY b DESC NULLS FIRST) AS n FROM t"
1384 );
1385 assert_eq!(
1386 body(
1387 "SELECT sum(i) OVER (PARTITION BY i, i+1 ORDER BY i ASC NULLS LAST, i DESC) AS n FROM t"
1388 ),
1389 "SELECT sum(i) OVER (PARTITION BY i, (i + 1) ORDER BY i ASC NULLS LAST, i DESC) AS n FROM t"
1390 );
1391 assert_eq!(
1392 body("SELECT sum(DISTINCT i) OVER (ORDER BY i) AS n FROM t"),
1393 "SELECT sum(DISTINCT i) OVER (ORDER BY i) AS n FROM t"
1394 );
1395 assert_eq!(
1396 body("SELECT first_value(i IGNORE NULLS) OVER (ORDER BY i) AS n FROM t"),
1397 "SELECT first_value(i IGNORE NULLS) OVER (ORDER BY i) AS n FROM t"
1398 );
1399 assert_eq!(
1400 body("SELECT first_value(i RESPECT NULLS) OVER (ORDER BY i) AS n FROM t"),
1401 "SELECT first_value(i) OVER (ORDER BY i) AS n FROM t"
1402 );
1403 assert_eq!(
1404 body("SELECT count(*) OVER () AS n FROM t"),
1405 "SELECT count() OVER () AS n FROM t"
1406 );
1407 assert_eq!(
1408 body("SELECT main.sum(i) OVER (ORDER BY i) AS n FROM t"),
1409 "SELECT main.sum(i) OVER (ORDER BY i) AS n FROM t"
1410 );
1411 }
1412
1413 #[test]
1416 fn a_filter_is_written_after_the_call_and_before_the_over() {
1417 assert_eq!(
1418 body("SELECT sum(x) FILTER (WHERE y > 1) FROM t"),
1419 "SELECT sum(x) FILTER (WHERE (y > 1)) FROM t"
1420 );
1421 assert_eq!(
1422 body("SELECT sum(x) FILTER (y > 1) FROM t"),
1423 "SELECT sum(x) FILTER (WHERE (y > 1)) FROM t"
1424 );
1425 assert_eq!(
1426 body("SELECT count(*) FILTER (WHERE b) FROM t"),
1427 "SELECT count_star() FILTER (WHERE b) FROM t"
1428 );
1429 assert_eq!(
1430 body("SELECT count() FILTER (WHERE b) FROM t"),
1431 "SELECT count_star() FILTER (WHERE b) FROM t"
1432 );
1433 assert_eq!(
1434 body("SELECT sum(x) FILTER (WHERE y > 1) OVER (ORDER BY x) FROM t"),
1435 "SELECT sum(x) FILTER (WHERE (y > 1)) OVER (ORDER BY x) FROM t"
1436 );
1437 assert_eq!(
1438 body("SELECT sum(DISTINCT x) FILTER (WHERE b) OVER () FROM t"),
1439 "SELECT sum(DISTINCT x) FILTER (WHERE b) OVER () FROM t"
1440 );
1441 assert_eq!(
1442 body("SELECT count(*) FILTER (WHERE b) OVER () FROM t"),
1443 "SELECT count() FILTER (WHERE b) OVER () FROM t"
1444 );
1445 }
1446
1447 #[test]
1449 fn a_frame_is_written_only_when_it_is_not_the_one_that_was_assumed() {
1450 assert_eq!(
1451 body(
1452 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1453 ),
1454 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1455 );
1456 assert_eq!(
1457 body("SELECT sum(i) OVER (ORDER BY i RANGE UNBOUNDED PRECEDING) AS n FROM t"),
1458 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1459 );
1460 assert_eq!(
1461 body(
1462 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS n FROM t"
1463 ),
1464 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1465 );
1466 assert_eq!(
1467 body("SELECT sum(i) OVER (ORDER BY i ROWS CURRENT ROW) AS n FROM t"),
1468 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW) AS n FROM t"
1469 );
1470 assert_eq!(
1471 body(
1472 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN (1+1) PRECEDING AND CURRENT ROW) AS n FROM t"
1473 ),
1474 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN (1 + 1) PRECEDING AND CURRENT ROW) AS n FROM t"
1475 );
1476 assert_eq!(
1477 body(
1478 "SELECT sum(i) OVER (ORDER BY i GROUPS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS n FROM t"
1479 ),
1480 "SELECT sum(i) OVER (ORDER BY i GROUPS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS n FROM t"
1481 );
1482 assert_eq!(
1483 body(
1484 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP) AS n FROM t"
1485 ),
1486 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP) AS n FROM t"
1487 );
1488 assert_eq!(
1489 body(
1490 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS n FROM t"
1491 ),
1492 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS n FROM t"
1493 );
1494 }
1495
1496 #[test]
1499 fn a_frame_that_covers_the_partition_is_written_as_a_row_count() {
1500 for unit in ["ROWS", "RANGE", "GROUPS"] {
1501 assert_eq!(
1502 body(&format!(
1503 "SELECT sum(i) OVER (ORDER BY i {unit} BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS n FROM t"
1504 )),
1505 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS n FROM t"
1506 );
1507 }
1508 assert_eq!(
1509 body(
1510 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) AS n FROM t"
1511 ),
1512 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) AS n FROM t"
1513 );
1514 }
1515
1516 #[test]
1519 fn a_named_window_is_written_out_where_it_was_used() {
1520 assert_eq!(
1521 body("SELECT sum(i) OVER w AS n FROM t WINDOW w AS (PARTITION BY i ORDER BY i)"),
1522 "SELECT sum(i) OVER (PARTITION BY i ORDER BY i) AS n FROM t"
1523 );
1524 assert_eq!(
1525 body("SELECT sum(i) OVER (w) AS n FROM t WINDOW w AS (ORDER BY i)"),
1526 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1527 );
1528 assert_eq!(
1529 body(
1530 "SELECT sum(i) OVER (w ROWS UNBOUNDED PRECEDING) AS n FROM t WINDOW w AS (PARTITION BY i)"
1531 ),
1532 "SELECT sum(i) OVER (PARTITION BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1533 );
1534 assert_eq!(
1535 body("SELECT sum(i) OVER (w PARTITION BY i) AS n FROM t WINDOW w AS (ORDER BY i)"),
1536 "SELECT sum(i) OVER (PARTITION BY i ORDER BY i) AS n FROM t"
1537 );
1538 assert_eq!(
1539 body("SELECT sum(i) OVER v AS n FROM t WINDOW w AS (ORDER BY i), v AS (w)"),
1540 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1541 );
1542 }
1543}