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, 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 held @ Expr::Window { .. } => window(ast, held),
358 Expr::Cast { operand, ty, try_cast } => {
359 let word = if try_cast { "TRY_CAST" } else { "CAST" };
360 format!("{word}({} AS {})", expr(ast, operand), typename(ast.string(ty)))
361 }
362 Expr::Case { operand, arms, otherwise } => case(ast, operand, arms, otherwise),
363 Expr::Between { operand, low, high, negated } => {
364 let written = format!(
365 "({} BETWEEN {} AND {})",
366 expr(ast, operand),
367 expr(ast, low),
368 expr(ast, high)
369 );
370 if negated { format!("(NOT {written})") } else { written }
371 }
372 Expr::In { operand, list, negated } => {
373 let written = format!("({} IN ({}))", expr(ast, operand), exprs(ast, list));
374 if negated { format!("(NOT {written})") } else { written }
375 }
376 Expr::InSubquery { operand, query: inner, negated } => {
377 let any = format!("({} = ANY({}))", expr(ast, operand), query(ast, inner));
378 if negated { format!("(NOT {any})") } else { any }
379 }
380 Expr::QuantifiedSubquery { operand, op, query: inner, all } => {
381 let (op, negate) = if all { (negated_comparison(op), true) } else { (op, false) };
382 let word = comparison_word(op);
383 let any = format!("({} {word} ANY({}))", expr(ast, operand), query(ast, inner));
384 if negate { format!("(NOT {any})") } else { any }
385 }
386 Expr::Parameter { name } => format!("${}", ast.string(name)),
387 Expr::List { items } => format!("list_value({})", exprs(ast, items)),
389 Expr::Row { items } => format!("\"row\"({})", exprs(ast, items)),
392 Expr::Lambda { params, body } => {
394 let params: Vec<String> = ast.name(params).map(quoted).collect();
395 format!("(lambda {}: {})", params.join(", "), expr(ast, body))
396 }
397 Expr::Subquery { query: inner } => format!("({})", query(ast, inner)),
398 Expr::Exists { query: inner, negated } => {
399 let exists = format!("EXISTS({})", query(ast, inner));
400 if negated { format!("(NOT {exists})") } else { exists }
401 }
402 }
403}
404
405fn comparison_word(op: BinaryOp) -> &'static str {
406 match op {
407 BinaryOp::Eq => "=",
408 BinaryOp::NotEq => "!=",
409 BinaryOp::Lt => "<",
410 BinaryOp::Gt => ">",
411 BinaryOp::LtEq => "<=",
412 BinaryOp::GtEq => ">=",
413 _ => unreachable!("the grammar permits only a comparison before ANY or ALL"),
414 }
415}
416
417fn negated_comparison(op: BinaryOp) -> BinaryOp {
418 match op {
419 BinaryOp::Eq => BinaryOp::NotEq,
420 BinaryOp::NotEq => BinaryOp::Eq,
421 BinaryOp::Lt => BinaryOp::GtEq,
422 BinaryOp::Gt => BinaryOp::LtEq,
423 BinaryOp::LtEq => BinaryOp::Gt,
424 BinaryOp::GtEq => BinaryOp::Lt,
425 _ => unreachable!("the grammar permits only a comparison before ANY or ALL"),
426 }
427}
428
429fn star(ast: &Ast, qualifier: Slice, replacements: Slice) -> String {
431 let mut out =
432 if qualifier.is_empty() { "*".to_string() } else { format!("{}.*", parts(ast, qualifier)) };
433 if !replacements.is_empty() {
434 let written: Vec<String> =
435 ast.target_list(replacements).iter().map(|target| aliased(ast, target)).collect();
436 out += &format!(" REPLACE ({})", written.join(", "));
437 }
438 out
439}
440
441fn literal(ast: &Ast, kind: LiteralKind, text: StrRef) -> String {
443 match kind {
444 LiteralKind::Null => "NULL".to_string(),
447 LiteralKind::True => "true".to_string(),
448 LiteralKind::False => "false".to_string(),
449 LiteralKind::Number => number(ast.string(text)),
450 LiteralKind::String => string(ast.string(text)),
451 LiteralKind::Blob => format!("{}::BLOB", string(ast.string(text))),
454 }
455}
456
457fn number(written: &str) -> String {
466 let text = written.replace('_', "");
467 if text.contains(['e', 'E']) {
468 return double(&text);
469 }
470 let Some((whole, fraction)) = text.split_once('.') else {
471 return leading(&text).to_string();
472 };
473 if fraction.is_empty() {
476 return leading(whole).to_string();
477 }
478 format!("{}.{fraction}", if whole.is_empty() { "" } else { leading(whole) })
479}
480
481fn leading(digits: &str) -> &str {
483 let trimmed = digits.trim_start_matches('0');
484 if trimmed.is_empty() { &digits[digits.len().saturating_sub(1)..] } else { trimmed }
485}
486
487fn double(text: &str) -> String {
495 let Ok(value) = text.parse::<f64>() else {
496 return text.to_string();
497 };
498 let shortest = format!("{value:e}");
501 let (mantissa, exponent) = shortest.split_once('e').unwrap_or((shortest.as_str(), "0"));
502 let exponent: i32 = exponent.parse().unwrap_or(0);
503 if (-4..=15).contains(&exponent) {
504 let plain = format!("{value}");
505 return if plain.contains('.') { plain } else { plain + ".0" };
506 }
507 let sign = if exponent < 0 { '-' } else { '+' };
508 format!("{mantissa}e{sign}{:02}", exponent.abs())
509}
510
511fn string(text: &str) -> String {
516 format!("'{}'", text.replace('\'', "''"))
517}
518
519fn unary(ast: &Ast, op: UnaryOp, operand: ExprRef) -> String {
521 if matches!(op, UnaryOp::Negate) {
525 if let Some(number) = negated(ast, operand) {
526 return number;
527 }
528 }
529 let written = expr(ast, operand);
530 match op {
531 UnaryOp::Not => format!("(NOT {written})"),
532 UnaryOp::Negate => format!("-({written})"),
533 UnaryOp::Plus => format!("+({written})"),
534 UnaryOp::BitNot => format!("~({written})"),
535 UnaryOp::Factorial => format!("factorial({written})"),
537 UnaryOp::IsNull => format!("({written} IS NULL)"),
538 UnaryOp::IsNotNull => format!("({written} IS NOT NULL)"),
539 UnaryOp::IsUnknown => format!("({written} IS NULL)"),
541 UnaryOp::IsNotUnknown => format!("({written} IS NOT NULL)"),
542 UnaryOp::IsTrue => distinct(&written, "true", true),
546 UnaryOp::IsNotTrue => distinct(&written, "true", false),
547 UnaryOp::IsFalse => distinct(&written, "false", true),
548 UnaryOp::IsNotFalse => distinct(&written, "false", false),
549 }
550}
551
552fn distinct(operand: &str, against: &str, same: bool) -> String {
554 let word = if same { "IS NOT DISTINCT FROM" } else { "IS DISTINCT FROM" };
555 format!("(CAST({operand} AS BOOLEAN) {word} {against})")
556}
557
558fn negated(ast: &Ast, index: ExprRef) -> Option<String> {
563 match ast.expr(index) {
564 Expr::Literal { kind: LiteralKind::Number, text } => {
565 Some(format!("-{}", number(ast.string(text))))
566 }
567 Expr::Unary { op: UnaryOp::Negate, operand } => {
568 let inner = negated(ast, operand)?;
569 Some(inner.strip_prefix('-').unwrap_or(&inner).to_string())
570 }
571 _ => None,
572 }
573}
574
575fn binary(ast: &Ast, op: BinaryOp, left: ExprRef, right: ExprRef) -> String {
577 let (left, right) = (expr(ast, left), expr(ast, right));
578 match op {
580 BinaryOp::SimilarTo => return format!("regexp_full_match({left}, {right})"),
581 BinaryOp::NotSimilarTo => return format!("(NOT regexp_full_match({left}, {right}))"),
582 BinaryOp::AtTimeZone => return format!("timezone({right}, {left})"),
584 BinaryOp::Collate => return format!("{left} COLLATE {right}"),
586 _ => {}
587 }
588 let word = match op {
589 BinaryOp::Or => "OR",
590 BinaryOp::And => "AND",
591 BinaryOp::Eq => "=",
592 BinaryOp::NotEq => "!=",
593 BinaryOp::Lt => "<",
594 BinaryOp::Gt => ">",
595 BinaryOp::LtEq => "<=",
596 BinaryOp::GtEq => ">=",
597 BinaryOp::IsDistinctFrom => "IS DISTINCT FROM",
598 BinaryOp::IsNotDistinctFrom => "IS NOT DISTINCT FROM",
599 BinaryOp::Add => "+",
600 BinaryOp::Subtract => "-",
601 BinaryOp::Multiply => "*",
602 BinaryOp::Divide => "/",
603 BinaryOp::IntegerDivide => "//",
604 BinaryOp::Modulo => "%",
605 BinaryOp::Power => "**",
608 BinaryOp::BitAnd => "&",
609 BinaryOp::BitOr => "|",
610 BinaryOp::ShiftLeft => "<<",
611 BinaryOp::ShiftRight => ">>",
612 BinaryOp::Concat => "||",
613 BinaryOp::Like => "~~",
616 BinaryOp::NotLike => "!~~",
617 BinaryOp::ILike => "~~*",
618 BinaryOp::NotILike => "!~~*",
619 BinaryOp::Glob => "~~~",
620 BinaryOp::Regex => "~",
621 BinaryOp::NotRegex => "!~",
622 BinaryOp::RegexInsensitive => "~*",
623 BinaryOp::NotRegexInsensitive => "!~*",
624 BinaryOp::Arrow => "->",
625 BinaryOp::LongArrow => "->>",
626 BinaryOp::Contains => "@>",
627 BinaryOp::ContainedBy => "<@",
628 BinaryOp::Overlaps => "&&",
629 BinaryOp::StartsWith => "^@",
630 BinaryOp::InetContainedByOrEq => "<<=",
631 BinaryOp::InetContainsOrEq => ">>=",
632 BinaryOp::Named(name) => ast.string(name),
633 BinaryOp::SimilarTo | BinaryOp::NotSimilarTo | BinaryOp::AtTimeZone | BinaryOp::Collate => {
634 unreachable!("the four that return above")
635 }
636 };
637 format!("({left} {word} {right})")
638}
639
640fn call(ast: &Ast, name: Slice, args: Slice, distinct: bool, filter: ExprRef) -> String {
642 let written = parts(ast, name);
643 let list = ast.expr_list(args);
644 if written.eq_ignore_ascii_case("count") {
648 let starred = list.len() == 1
649 && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
650 if qualifier.is_empty() && replacements.is_empty());
651 if starred || list.is_empty() {
652 return format!("count_star(){}", filtered(ast, filter));
653 }
654 }
655 let word = if distinct { "DISTINCT " } else { "" };
656 format!(
657 "{}({word}{}){}",
658 operator(ast, name, &written),
659 exprs(ast, args),
660 filtered(ast, filter)
661 )
662}
663
664fn filtered(ast: &Ast, filter: ExprRef) -> String {
669 if filter == NONE { String::new() } else { format!(" FILTER (WHERE {})", expr(ast, filter)) }
670}
671
672fn window(ast: &Ast, held: Expr) -> String {
682 let Expr::Window { name, args, distinct, filter, ignore_nulls, order: sorted, spec } = held
683 else {
684 return String::new();
685 };
686 let word = if distinct { "DISTINCT " } else { "" };
687 let sorted = if sorted.is_empty() {
690 String::new()
691 } else {
692 let items: Vec<String> =
693 ast.order_list(sorted).iter().map(|item| order(ast, item)).collect();
694 format!(" ORDER BY {}", items.join(", "))
695 };
696 let nulls = if ignore_nulls { " IGNORE NULLS" } else { "" };
698 let written = parts(ast, name);
699 let list = ast.expr_list(args);
703 let bare = list.len() == 1
704 && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
705 if qualifier.is_empty() && replacements.is_empty());
706 let inner = if bare { String::new() } else { exprs(ast, args) };
707 let call = format!(
708 "{}({word}{inner}{sorted}{nulls}){}",
709 operator(ast, name, &written),
710 filtered(ast, filter)
711 );
712 let held = ast.window(spec);
713 let mut inside: Vec<String> = Vec::new();
714 if !held.partition.is_empty() {
715 inside.push(format!("PARTITION BY {}", exprs(ast, held.partition)));
716 }
717 if !held.order.is_empty() {
718 let items: Vec<String> =
719 ast.order_list(held.order).iter().map(|item| order(ast, item)).collect();
720 inside.push(format!("ORDER BY {}", items.join(", ")));
721 }
722 if !held.frame_is_default() {
723 let unit = match held.unit {
724 WindowUnit::Rows => "ROWS",
725 WindowUnit::Range => "RANGE",
726 WindowUnit::Groups => "GROUPS",
727 };
728 let mut frame =
729 format!("{unit} BETWEEN {} AND {}", bound(ast, held.start), bound(ast, held.end));
730 frame += match held.exclude {
731 WindowExclude::NoOthers => "",
732 WindowExclude::CurrentRow => " EXCLUDE CURRENT ROW",
733 WindowExclude::Group => " EXCLUDE GROUP",
734 WindowExclude::Ties => " EXCLUDE TIES",
735 };
736 inside.push(frame);
737 }
738 format!("{call} OVER ({})", inside.join(" "))
739}
740
741fn bound(ast: &Ast, end: WindowBound) -> String {
743 match end {
744 WindowBound::UnboundedPreceding => "UNBOUNDED PRECEDING".to_string(),
745 WindowBound::Preceding(offset) => format!("{} PRECEDING", expr(ast, offset)),
746 WindowBound::CurrentRow => "CURRENT ROW".to_string(),
747 WindowBound::Following(offset) => format!("{} FOLLOWING", expr(ast, offset)),
748 WindowBound::UnboundedFollowing => "UNBOUNDED FOLLOWING".to_string(),
749 }
750}
751
752fn operator(ast: &Ast, name: Slice, written: &str) -> String {
759 let one = ast.name(name).next().unwrap_or_default();
760 let alone = ast.name(name).count() == 1;
761 if alone && (one.eq_ignore_ascii_case("coalesce") || one.eq_ignore_ascii_case("ifnull")) {
762 return "COALESCE".to_string();
763 }
764 written.to_string()
765}
766
767fn case(ast: &Ast, operand: ExprRef, arms: Slice, otherwise: ExprRef) -> String {
773 let mut out = "CASE ".to_string();
774 for arm in ast.arm_list(arms) {
775 let when = when(ast, operand, arm);
776 out += &format!(" WHEN ({when}) THEN ({})", expr(ast, arm.then));
777 }
778 let last = if otherwise == NONE { "NULL".to_string() } else { expr(ast, otherwise) };
779 out + &format!(" ELSE {last} END")
780}
781
782fn when(ast: &Ast, operand: ExprRef, arm: &CaseArm) -> String {
785 if operand == NONE {
786 return expr(ast, arm.when);
787 }
788 format!("({} = {})", expr(ast, operand), expr(ast, arm.when))
789}
790
791fn typename(text: &str) -> String {
805 let text = text.trim();
806 if let Some(open) = suffix(text) {
809 return typename(&text[..open]) + &text[open..];
810 }
811 let (base, arguments) = arguments(text);
812 let Some(name) = standard(base) else {
813 let base = unquote(base);
814 return match arguments {
815 Some(arguments) => format!("{}({arguments})", catalogued(&base)),
816 None => catalogued(&base),
817 };
818 };
819 match (name, arguments) {
820 ("STRUCT" | "UNION", Some(inside)) => {
823 let written: Vec<String> = pieces(inside).iter().map(|piece| field(piece)).collect();
824 format!("{name}({})", written.join(", "))
825 }
826 ("MAP", Some(inside)) => {
827 let written: Vec<String> = pieces(inside).iter().map(|piece| typename(piece)).collect();
828 format!("{name}({})", written.join(", "))
829 }
830 ("DECIMAL" | "VARCHAR", Some(inside)) => {
833 format!("{name}({})", pieces(inside).join(", "))
834 }
835 _ => name.to_string(),
838 }
839}
840
841fn catalogued(base: &str) -> String {
854 if base.eq_ignore_ascii_case("json") { quoted(base) } else { base.to_string() }
855}
856
857fn unquote(base: &str) -> String {
859 match base.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')) {
860 Some(inside) => inside.replace("\"\"", "\""),
861 None => base.to_string(),
862 }
863}
864
865fn suffix(text: &str) -> Option<usize> {
867 let rest = text.strip_suffix(']')?;
868 let open = rest.rfind('[')?;
869 rest[open + 1..].bytes().all(|byte| byte.is_ascii_digit()).then_some(open)
870}
871
872fn arguments(text: &str) -> (&str, Option<&str>) {
874 let Some(rest) = text.strip_suffix(')') else {
875 return (text, None);
876 };
877 let mut depth = 0usize;
878 for (at, byte) in rest.bytes().enumerate() {
879 match byte {
880 b'(' if depth == 0 => depth = 1,
881 b'(' => depth += 1,
882 b')' => depth -= 1,
883 _ => continue,
884 }
885 if depth == 1 && byte == b'(' {
886 return (rest[..at].trim(), Some(rest[at + 1..].trim()));
887 }
888 }
889 (text, None)
890}
891
892fn pieces(inside: &str) -> Vec<&str> {
894 let mut found = Vec::new();
895 let (mut depth, mut quoted, mut start) = (0usize, false, 0usize);
896 for (at, byte) in inside.bytes().enumerate() {
897 match byte {
898 b'"' => quoted = !quoted,
899 b'(' | b'[' if !quoted => depth += 1,
900 b')' | b']' if !quoted => depth = depth.saturating_sub(1),
901 b',' if !quoted && depth == 0 => {
902 found.push(inside[start..at].trim());
903 start = at + 1;
904 }
905 _ => {}
906 }
907 }
908 found.push(inside[start..].trim());
909 found
910}
911
912fn field(piece: &str) -> String {
914 let mut quoting = false;
915 for (at, byte) in piece.bytes().enumerate() {
916 match byte {
917 b'"' => quoting = !quoting,
918 byte if byte.is_ascii_whitespace() && !quoting => {
919 let name = piece[..at].trim();
920 let name =
921 if name.starts_with('"') { quoted(&unquote(name)) } else { name.to_string() };
922 return format!("{name} {}", typename(&piece[at + 1..]));
923 }
924 _ => {}
925 }
926 }
927 piece.to_string()
928}
929
930fn standard(base: &str) -> Option<&'static str> {
936 const NAMES: &[(&str, &str)] = &[
937 ("BOOLEAN", "BOOLEAN"),
938 ("INT", "INTEGER"),
939 ("INTEGER", "INTEGER"),
940 ("SMALLINT", "SMALLINT"),
941 ("BIGINT", "BIGINT"),
942 ("DEC", "DECIMAL"),
943 ("DECIMAL", "DECIMAL"),
944 ("NUMERIC", "DECIMAL"),
945 ("REAL", "FLOAT"),
946 ("FLOAT", "FLOAT"),
947 ("DOUBLE PRECISION", "DOUBLE"),
948 ("CHAR", "VARCHAR"),
949 ("CHARACTER", "VARCHAR"),
950 ("CHARACTER VARYING", "VARCHAR"),
951 ("NATIONAL CHARACTER", "VARCHAR"),
952 ("NATIONAL CHARACTER VARYING", "VARCHAR"),
953 ("VARCHAR", "VARCHAR"),
954 ("BIT", "BIT"),
955 ("DATE", "DATE"),
956 ("TIME", "TIME"),
957 ("TIME WITH TIME ZONE", "TIME WITH TIME ZONE"),
958 ("TIME WITHOUT TIME ZONE", "TIME"),
959 ("TIMESTAMP", "TIMESTAMP"),
960 ("TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH TIME ZONE"),
961 ("TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP"),
962 ("INTERVAL", "INTERVAL"),
963 ("STRUCT", "STRUCT"),
964 ("UNION", "UNION"),
965 ("MAP", "MAP"),
966 ];
967 let written: Vec<&str> = base.split_whitespace().collect();
968 let written = written.join(" ");
969 NAMES
970 .iter()
971 .find(|(spelling, _)| spelling.eq_ignore_ascii_case(&written))
972 .map(|(_, name)| *name)
973}
974
975fn exprs(ast: &Ast, list: Slice) -> String {
977 let written: Vec<String> = ast.expr_list(list).iter().map(|&item| expr(ast, item)).collect();
978 written.join(", ")
979}
980
981fn names(ast: &Ast, list: Slice) -> String {
983 ast.name(list).map(quoted).collect::<Vec<_>>().join(", ")
984}
985
986fn parts(ast: &Ast, list: Slice) -> String {
988 ast.name(list).map(quoted).collect::<Vec<_>>().join(".")
989}
990
991#[cfg(test)]
992mod tests {
993 use super::create_view;
994 use crate::ast::Statement;
995 use crate::transform::parse_ast;
996
997 fn whole(sql: &str) -> String {
999 let ast = parse_ast(sql).unwrap_or_else(|error| panic!("{sql} should parse: {error}"));
1000 let Statement::CreateView(index) = ast.statements[0] else {
1001 panic!("that was not a create view");
1002 };
1003 create_view(&ast, index)
1004 }
1005
1006 fn body(query: &str) -> String {
1008 let written = whole(&format!("CREATE VIEW v AS {query}"));
1009 written
1010 .strip_prefix("CREATE VIEW v AS ")
1011 .and_then(|rest| rest.strip_suffix(';'))
1012 .expect("the statement wrapper is there")
1013 .to_string()
1014 }
1015
1016 #[test]
1017 fn a_statement_loses_its_qualification_and_its_or_replace() {
1018 assert_eq!(whole("CREATE VIEW main.v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1019 assert_eq!(whole("CREATE OR REPLACE VIEW v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1020 assert_eq!(whole("CREATE VIEW IF NOT EXISTS v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
1021 assert_eq!(whole("CREATE TEMP VIEW v AS SELECT 1"), "CREATE TEMP VIEW v AS SELECT 1;");
1022 }
1023
1024 #[test]
1026 fn an_alias_list_is_written_with_a_space_in_front_of_it() {
1027 assert_eq!(
1028 whole(r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2"#),
1029 r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2;"#
1030 );
1031 }
1032
1033 #[test]
1034 fn comments_and_spacing_go_and_the_case_of_a_name_stays() {
1035 assert_eq!(
1036 whole("CREATE VIEW v AS SELECT X /* a note */ FROM T"),
1037 "CREATE VIEW v AS SELECT X FROM T;"
1038 );
1039 }
1040
1041 #[test]
1042 fn every_binary_operation_is_parenthesised_and_every_unary_one_parenthesises_its_operand() {
1043 assert_eq!(body("SELECT x + y * 2 - 1 FROM t"), "SELECT ((x + (y * 2)) - 1) FROM t");
1044 assert_eq!(
1045 body("SELECT x > 1 AND y < 2 OR b FROM t"),
1046 "SELECT (((x > 1) AND (y < 2)) OR b) FROM t"
1047 );
1048 assert_eq!(body("SELECT NOT b FROM t"), "SELECT (NOT b) FROM t");
1049 assert_eq!(body("SELECT ~x FROM t"), "SELECT ~(x) FROM t");
1050 assert_eq!(body("SELECT +x FROM t"), "SELECT +(x) FROM t");
1051 assert_eq!(body("SELECT -x FROM t"), "SELECT -(x) FROM t");
1052 }
1053
1054 #[test]
1057 fn a_minus_in_front_of_a_constant_folds_into_it() {
1058 assert_eq!(body("SELECT -1"), "SELECT -1");
1059 assert_eq!(body("SELECT - -3"), "SELECT 3");
1060 assert_eq!(body("SELECT +3"), "SELECT +(3)");
1061 }
1062
1063 #[test]
1064 fn the_null_tests_and_the_boolean_tests() {
1065 assert_eq!(body("SELECT x IS NULL FROM t"), "SELECT (x IS NULL) FROM t");
1066 assert_eq!(body("SELECT x ISNULL FROM t"), "SELECT (x IS NULL) FROM t");
1067 assert_eq!(body("SELECT x NOTNULL FROM t"), "SELECT (x IS NOT NULL) FROM t");
1068 assert_eq!(
1069 body("SELECT b IS TRUE FROM t"),
1070 "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM true) FROM t"
1071 );
1072 assert_eq!(
1073 body("SELECT b IS NOT TRUE FROM t"),
1074 "SELECT (CAST(b AS BOOLEAN) IS DISTINCT FROM true) FROM t"
1075 );
1076 assert_eq!(
1077 body("SELECT b IS FALSE FROM t"),
1078 "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM false) FROM t"
1079 );
1080 assert_eq!(body("SELECT b IS UNKNOWN FROM t"), "SELECT (b IS NULL) FROM t");
1081 assert_eq!(body("SELECT b IS NOT UNKNOWN FROM t"), "SELECT (b IS NOT NULL) FROM t");
1082 assert_eq!(
1083 body("SELECT x IS DISTINCT FROM y FROM t"),
1084 "SELECT (x IS DISTINCT FROM y) FROM t"
1085 );
1086 }
1087
1088 #[test]
1089 fn a_negated_between_or_in_is_a_not_around_the_plain_one() {
1090 assert_eq!(body("SELECT x BETWEEN 1 AND 10 FROM t"), "SELECT (x BETWEEN 1 AND 10) FROM t");
1091 assert_eq!(
1092 body("SELECT x NOT BETWEEN 1 AND 2 FROM t"),
1093 "SELECT (NOT (x BETWEEN 1 AND 2)) FROM t"
1094 );
1095 assert_eq!(body("SELECT x IN (1, 2, 3) FROM t"), "SELECT (x IN (1, 2, 3)) FROM t");
1096 assert_eq!(body("SELECT x NOT IN (1, 2) FROM t"), "SELECT (NOT (x IN (1, 2))) FROM t");
1097 assert_eq!(body("SELECT x IN (SELECT y FROM t)"), "SELECT (x = ANY(SELECT y FROM t))");
1098 assert_eq!(
1099 body("SELECT x NOT IN (SELECT y FROM t)"),
1100 "SELECT (NOT (x = ANY(SELECT y FROM t)))"
1101 );
1102 assert_eq!(body("SELECT x = ANY (SELECT y FROM t)"), "SELECT (x = ANY(SELECT y FROM t))");
1103 assert_eq!(
1104 body("SELECT x > ALL (SELECT y FROM t)"),
1105 "SELECT (NOT (x <= ANY(SELECT y FROM t)))"
1106 );
1107 }
1108
1109 #[test]
1112 fn the_pattern_operators_come_back_as_symbols() {
1113 assert_eq!(body("SELECT s LIKE 'a' FROM t"), "SELECT (s ~~ 'a') FROM t");
1114 assert_eq!(body("SELECT s NOT LIKE 'a' FROM t"), "SELECT (s !~~ 'a') FROM t");
1115 assert_eq!(body("SELECT s ILIKE 'a' FROM t"), "SELECT (s ~~* 'a') FROM t");
1116 assert_eq!(body("SELECT s NOT ILIKE 'a' FROM t"), "SELECT (s !~~* 'a') FROM t");
1117 assert_eq!(body("SELECT s GLOB 'a' FROM t"), "SELECT (s ~~~ 'a') FROM t");
1118 assert_eq!(body("SELECT s !~ 'a' FROM t"), "SELECT (s !~ 'a') FROM t");
1119 assert_eq!(
1120 body("SELECT s NOT SIMILAR TO 'a' FROM t"),
1121 "SELECT (NOT regexp_full_match(s, 'a')) FROM t"
1122 );
1123 }
1124
1125 #[test]
1126 fn collate_has_no_parentheses_and_the_rest_of_the_operators_keep_their_spelling() {
1127 assert_eq!(body("SELECT s COLLATE NOCASE FROM t"), "SELECT s COLLATE NOCASE FROM t");
1128 assert_eq!(body("SELECT x // y FROM t"), "SELECT (x // y) FROM t");
1129 assert_eq!(body("SELECT x || y FROM t"), "SELECT (x || y) FROM t");
1130 assert_eq!(body("SELECT x @> y FROM t"), "SELECT (x @> y) FROM t");
1131 assert_eq!(body("SELECT x <=> y FROM t"), "SELECT (x <=> y) FROM t");
1132 }
1133
1134 #[test]
1136 fn a_case_is_written_the_long_way_round() {
1137 assert_eq!(
1138 body("SELECT CASE WHEN x > 0 THEN 'a' WHEN x < 0 THEN 'b' ELSE 'c' END FROM t"),
1139 "SELECT CASE WHEN ((x > 0)) THEN ('a') WHEN ((x < 0)) THEN ('b') ELSE 'c' END FROM t"
1140 );
1141 assert_eq!(
1142 body("SELECT CASE x WHEN 1 THEN 'a' END FROM t"),
1143 "SELECT CASE WHEN ((x = 1)) THEN ('a') ELSE NULL END FROM t"
1144 );
1145 }
1146
1147 #[test]
1148 fn a_cast_writes_its_type_in_upper_case_with_a_space_after_the_comma() {
1149 assert_eq!(body("SELECT x::varchar FROM t"), "SELECT CAST(x AS VARCHAR) FROM t");
1150 assert_eq!(
1151 body("SELECT cast(x as decimal(4,1)) FROM t"),
1152 "SELECT CAST(x AS DECIMAL(4, 1)) FROM t"
1153 );
1154 assert_eq!(
1155 body("SELECT TRY_CAST(s AS INTEGER) FROM t"),
1156 "SELECT TRY_CAST(s AS INTEGER) FROM t"
1157 );
1158 }
1159
1160 #[test]
1162 fn a_standard_type_name_is_resolved_and_the_modifiers_it_was_written_with_survive() {
1163 let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
1164 assert_eq!(cast("int"), "SELECT CAST(x AS INTEGER)");
1165 assert_eq!(cast("numeric(5)"), "SELECT CAST(x AS DECIMAL(5))");
1166 assert_eq!(cast("decimal"), "SELECT CAST(x AS DECIMAL)");
1167 assert_eq!(cast("varchar(10)"), "SELECT CAST(x AS VARCHAR(10))");
1168 assert_eq!(cast("national character(2)"), "SELECT CAST(x AS VARCHAR(2))");
1169 assert_eq!(cast("float(10)"), "SELECT CAST(x AS FLOAT)");
1172 assert_eq!(cast("real"), "SELECT CAST(x AS FLOAT)");
1173 assert_eq!(cast("double precision"), "SELECT CAST(x AS DOUBLE)");
1174 assert_eq!(cast("time with time zone"), "SELECT CAST(x AS TIME WITH TIME ZONE)");
1175 assert_eq!(cast("int[]"), "SELECT CAST(x AS INTEGER[])");
1176 assert_eq!(cast("int[2][3]"), "SELECT CAST(x AS INTEGER[2][3])");
1177 assert_eq!(cast("map(int, varchar)"), "SELECT CAST(x AS MAP(INTEGER, VARCHAR))");
1178 assert_eq!(cast("union(a int)"), "SELECT CAST(x AS UNION(a INTEGER))");
1179 }
1180
1181 #[test]
1183 fn a_struct_field_keeps_its_name_and_its_type_goes_round_again() {
1184 assert_eq!(body("SELECT CAST(x AS struct(a bool))"), "SELECT CAST(x AS STRUCT(a bool))");
1185 assert_eq!(
1186 body("SELECT CAST(x AS struct(\"A b\" int))"),
1187 "SELECT CAST(x AS STRUCT(\"A b\" INTEGER))"
1188 );
1189 }
1190
1191 #[test]
1193 fn a_type_name_the_grammar_has_no_rule_for_keeps_the_case_it_was_written_in() {
1194 let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
1195 assert_eq!(cast("text"), "SELECT CAST(x AS text)");
1196 assert_eq!(cast("TEXT"), "SELECT CAST(x AS TEXT)");
1197 assert_eq!(cast("DOUBLE"), "SELECT CAST(x AS DOUBLE)");
1198 assert_eq!(cast("bool"), "SELECT CAST(x AS bool)");
1199 assert_eq!(cast("\"bool\""), "SELECT CAST(x AS bool)");
1200 assert_eq!(cast("int4[]"), "SELECT CAST(x AS int4[])");
1201 assert_eq!(cast("TIMESTAMPTZ"), "SELECT CAST(x AS TIMESTAMPTZ)");
1202 assert_eq!(cast("JSON"), "SELECT CAST(x AS \"JSON\")");
1204 assert_eq!(cast("json"), "SELECT CAST(x AS \"json\")");
1205 assert_eq!(cast("json[]"), "SELECT CAST(x AS \"json\"[])");
1206 assert_eq!(cast("struct(a json)"), "SELECT CAST(x AS STRUCT(a \"json\"))");
1207 }
1208
1209 #[test]
1210 fn a_star_count_is_a_function_of_its_own_and_a_list_is_a_call() {
1211 assert_eq!(body("SELECT count(*) FROM t"), "SELECT count_star() FROM t");
1212 assert_eq!(body("SELECT count() FROM t"), "SELECT count_star() FROM t");
1213 assert_eq!(body("SELECT count(DISTINCT x) FROM t"), "SELECT count(DISTINCT x) FROM t");
1214 assert_eq!(body("SELECT [1, 2, 3]"), "SELECT list_value(1, 2, 3)");
1215 assert_eq!(body("SELECT []"), "SELECT list_value()");
1216 }
1217
1218 #[test]
1221 fn a_function_name_is_quoted_when_it_is_a_keyword() {
1222 assert_eq!(body("SELECT nullif(x, 1) FROM t"), "SELECT \"nullif\"(x, 1) FROM t");
1223 assert_eq!(body("SELECT length(s) FROM t"), "SELECT length(s) FROM t");
1224 }
1225
1226 #[test]
1227 fn the_literals() {
1228 assert_eq!(body("SELECT NULL, TRUE, FALSE"), "SELECT NULL, true, false");
1229 assert_eq!(body("SELECT 1.50, .5, 1_000"), "SELECT 1.50, .5, 1000");
1230 assert_eq!(body("SELECT 'it''s'"), "SELECT 'it''s'");
1231 }
1232
1233 #[test]
1235 fn a_number_is_written_back_as_the_value_the_shape_of_it_made() {
1236 assert_eq!(body("SELECT 007, 1_000"), "SELECT 7, 1000");
1237 assert_eq!(body("SELECT 1.50, 00.5, 1., 0.0"), "SELECT 1.50, 0.5, 1, 0.0");
1238 assert_eq!(body("SELECT 1e3, 1.5e2, 1e-3, 5e-4"), "SELECT 1000.0, 150.0, 0.001, 0.0005");
1239 assert_eq!(body("SELECT 5e-5, 2.5e-5, 1e-10"), "SELECT 5e-05, 2.5e-05, 1e-10");
1240 assert_eq!(body("SELECT 1e15, 1e16, 1e100"), "SELECT 1000000000000000.0, 1e+16, 1e+100");
1241 }
1242
1243 #[test]
1245 fn an_extract_is_a_date_part_call_and_the_keyword_it_named_has_one_spelling() {
1246 assert_eq!(body("SELECT extract(year FROM d)"), "SELECT date_part('YEAR', d)");
1247 assert_eq!(body("SELECT extract(years FROM d)"), "SELECT date_part('YEAR', d)");
1248 assert_eq!(body("SELECT extract(seconds FROM d)"), "SELECT date_part('SECOND', d)");
1249 assert_eq!(
1251 body("SELECT extract(millisecond FROM d)"),
1252 "SELECT date_part('MILLISECONDS', d)"
1253 );
1254 assert_eq!(
1255 body("SELECT extract(microseconds FROM d)"),
1256 "SELECT date_part('MICROSECONDS', d)"
1257 );
1258 assert_eq!(body("SELECT extract(millennia FROM d)"), "SELECT date_part('MILLENNIUM', d)");
1259 assert_eq!(body("SELECT extract(epoch FROM d)"), "SELECT date_part('epoch', d)");
1261 assert_eq!(body("SELECT extract(dow FROM d)"), "SELECT date_part('dow', d)");
1262 }
1263
1264 #[test]
1266 fn coalesce_and_ifnull_are_one_operator_and_it_is_written_in_upper_case() {
1267 assert_eq!(body("SELECT coalesce(x, y)"), "SELECT COALESCE(x, y)");
1268 assert_eq!(body("SELECT IfNull(x, y)"), "SELECT COALESCE(x, y)");
1269 assert_eq!(body("SELECT coalesce(x)"), "SELECT COALESCE(x)");
1271 assert_eq!(body("SELECT nullif(x, y)"), "SELECT \"nullif\"(x, y)");
1273 assert_eq!(body("SELECT greatest(x, y)"), "SELECT greatest(x, y)");
1274 }
1275
1276 #[test]
1277 fn the_modifiers_hang_off_the_query_and_not_off_the_select() {
1278 assert_eq!(body("SELECT x FROM t LIMIT 5 OFFSET 2"), "SELECT x FROM t LIMIT 5 OFFSET 2");
1279 assert_eq!(body("SELECT x FROM t LIMIT 10 PERCENT"), "SELECT x FROM t LIMIT (10) %");
1280 assert_eq!(
1281 body("SELECT x FROM t ORDER BY x ASC, y NULLS LAST"),
1282 "SELECT x FROM t ORDER BY x ASC, y NULLS LAST"
1283 );
1284 assert_eq!(body("SELECT x FROM t ORDER BY ALL"), "SELECT x FROM t ORDER BY COLUMNS(*)");
1285 assert_eq!(body("SELECT x FROM t GROUP BY ALL"), "SELECT x FROM t GROUP BY ALL");
1286 assert_eq!(
1287 body("SELECT x FROM t GROUP BY x HAVING x > 0"),
1288 "SELECT x FROM t GROUP BY x HAVING (x > 0)"
1289 );
1290 assert_eq!(
1291 body("SELECT DISTINCT ON (x) x, y FROM t"),
1292 "SELECT DISTINCT ON (x) x, y FROM t"
1293 );
1294 }
1295
1296 #[test]
1299 fn a_chain_of_set_operations_loses_a_space_in_the_middle() {
1300 assert_eq!(
1301 body("SELECT x FROM t UNION ALL SELECT y FROM t"),
1302 "(SELECT x FROM t) UNION ALL (SELECT y FROM t)"
1303 );
1304 assert_eq!(
1305 body("SELECT x FROM t UNION SELECT y FROM t UNION SELECT 1"),
1306 "(SELECT x FROM t) UNION (SELECT y FROM t)UNION (SELECT 1)"
1307 );
1308 assert_eq!(
1309 body("SELECT x FROM t UNION DISTINCT SELECT y FROM t"),
1310 "(SELECT x FROM t) UNION (SELECT y FROM t)"
1311 );
1312 }
1313
1314 #[test]
1315 fn a_values_body_is_wrapped_in_a_select_that_names_it() {
1316 assert_eq!(
1317 body("VALUES (1, 'a'), (2, 'b')"),
1318 "SELECT * FROM (VALUES (1, 'a'), (2, 'b')) AS valueslist"
1319 );
1320 }
1321
1322 #[test]
1324 fn a_from_list_has_a_space_before_the_comma() {
1325 assert_eq!(body("SELECT 1 FROM t AS t1, t AS t2"), "SELECT 1 FROM t AS t1 , t AS t2");
1326 }
1327
1328 #[test]
1329 fn a_from_item_and_its_aliases() {
1330 assert_eq!(body("SELECT 1 FROM t AS r(n)"), "SELECT 1 FROM t AS r(n)");
1331 assert_eq!(body("SELECT 1 FROM main.t"), "SELECT 1 FROM main.t");
1332 assert_eq!(
1333 body("SELECT 1 FROM (SELECT x FROM t) AS sub"),
1334 "SELECT 1 FROM (SELECT x FROM t) AS sub"
1335 );
1336 assert_eq!(body("SELECT 1 FROM range(10)"), "SELECT 1 FROM \"range\"(10)");
1337 }
1338
1339 #[test]
1342 fn a_join_is_parenthesised_and_so_is_its_condition_twice() {
1343 assert_eq!(
1344 body("SELECT 1 FROM t AS a JOIN t AS b ON a.x = b.y"),
1345 "SELECT 1 FROM (t AS a INNER JOIN t AS b ON ((a.x = b.y)))"
1346 );
1347 assert_eq!(
1348 body("SELECT 1 FROM t LEFT JOIN t AS u USING (x)"),
1349 "SELECT 1 FROM (t LEFT JOIN t AS u USING (x))"
1350 );
1351 assert_eq!(
1352 body("SELECT 1 FROM t CROSS JOIN t AS u"),
1353 "SELECT 1 FROM (t CROSS JOIN t AS u)"
1354 );
1355 assert_eq!(
1356 body("SELECT 1 FROM t FULL OUTER JOIN t AS u ON t.x = u.x"),
1357 "SELECT 1 FROM (t FULL JOIN t AS u ON ((t.x = u.x)))"
1358 );
1359 assert_eq!(
1360 body("SELECT 1 FROM t NATURAL JOIN t AS u"),
1361 "SELECT 1 FROM (t NATURAL INNER JOIN t AS u)"
1362 );
1363 assert_eq!(
1364 body("SELECT 1 FROM t POSITIONAL JOIN t AS u"),
1365 "SELECT 1 FROM (t POSITIONAL JOIN t AS u)"
1366 );
1367 }
1368
1369 #[test]
1370 fn a_target_keeps_its_alias_and_a_star_keeps_its_replace_list() {
1371 assert_eq!(body("SELECT 1 + 2 AS \"quoted alias\""), "SELECT (1 + 2) AS \"quoted alias\"");
1372 assert_eq!(body("SELECT x AS \"select\" FROM t"), "SELECT x AS \"select\" FROM t");
1373 assert_eq!(body("SELECT t.* FROM t"), "SELECT t.* FROM t");
1374 assert_eq!(
1375 body("SELECT * REPLACE (x + 1 AS x) FROM t"),
1376 "SELECT * REPLACE ((x + 1) AS x) FROM t"
1377 );
1378 }
1379
1380 #[test]
1381 fn a_describe_gets_parentheses_round_what_it_describes() {
1382 assert_eq!(body("DESCRIBE SELECT 1"), "DESCRIBE (SELECT 1)");
1383 }
1384
1385 #[test]
1388 fn a_window_is_written_with_the_parts_that_were_written_in_it() {
1389 assert_eq!(
1390 body("SELECT row_number() OVER () AS n FROM t"),
1391 "SELECT row_number() OVER () AS n FROM t"
1392 );
1393 assert_eq!(
1394 body(
1395 "SELECT row_number() OVER (PARTITION BY a ORDER BY b DESC NULLS FIRST) AS n FROM t"
1396 ),
1397 "SELECT row_number() OVER (PARTITION BY a ORDER BY b DESC NULLS FIRST) AS n FROM t"
1398 );
1399 assert_eq!(
1400 body(
1401 "SELECT sum(i) OVER (PARTITION BY i, i+1 ORDER BY i ASC NULLS LAST, i DESC) AS n FROM t"
1402 ),
1403 "SELECT sum(i) OVER (PARTITION BY i, (i + 1) ORDER BY i ASC NULLS LAST, i DESC) AS n FROM t"
1404 );
1405 assert_eq!(
1406 body("SELECT sum(DISTINCT i) OVER (ORDER BY i) AS n FROM t"),
1407 "SELECT sum(DISTINCT i) OVER (ORDER BY i) AS n FROM t"
1408 );
1409 assert_eq!(
1410 body("SELECT first_value(i IGNORE NULLS) OVER (ORDER BY i) AS n FROM t"),
1411 "SELECT first_value(i IGNORE NULLS) OVER (ORDER BY i) AS n FROM t"
1412 );
1413 assert_eq!(
1414 body("SELECT first_value(i RESPECT NULLS) OVER (ORDER BY i) AS n FROM t"),
1415 "SELECT first_value(i) OVER (ORDER BY i) AS n FROM t"
1416 );
1417 assert_eq!(
1418 body("SELECT count(*) OVER () AS n FROM t"),
1419 "SELECT count() OVER () AS n FROM t"
1420 );
1421 assert_eq!(
1422 body("SELECT main.sum(i) OVER (ORDER BY i) AS n FROM t"),
1423 "SELECT main.sum(i) OVER (ORDER BY i) AS n FROM t"
1424 );
1425 }
1426
1427 #[test]
1430 fn a_filter_is_written_after_the_call_and_before_the_over() {
1431 assert_eq!(
1432 body("SELECT sum(x) FILTER (WHERE y > 1) FROM t"),
1433 "SELECT sum(x) FILTER (WHERE (y > 1)) FROM t"
1434 );
1435 assert_eq!(
1436 body("SELECT sum(x) FILTER (y > 1) FROM t"),
1437 "SELECT sum(x) FILTER (WHERE (y > 1)) FROM t"
1438 );
1439 assert_eq!(
1440 body("SELECT count(*) FILTER (WHERE b) FROM t"),
1441 "SELECT count_star() FILTER (WHERE b) FROM t"
1442 );
1443 assert_eq!(
1444 body("SELECT count() FILTER (WHERE b) FROM t"),
1445 "SELECT count_star() FILTER (WHERE b) FROM t"
1446 );
1447 assert_eq!(
1448 body("SELECT sum(x) FILTER (WHERE y > 1) OVER (ORDER BY x) FROM t"),
1449 "SELECT sum(x) FILTER (WHERE (y > 1)) OVER (ORDER BY x) FROM t"
1450 );
1451 assert_eq!(
1452 body("SELECT sum(DISTINCT x) FILTER (WHERE b) OVER () FROM t"),
1453 "SELECT sum(DISTINCT x) FILTER (WHERE b) OVER () FROM t"
1454 );
1455 assert_eq!(
1456 body("SELECT count(*) FILTER (WHERE b) OVER () FROM t"),
1457 "SELECT count() FILTER (WHERE b) OVER () FROM t"
1458 );
1459 }
1460
1461 #[test]
1463 fn a_frame_is_written_only_when_it_is_not_the_one_that_was_assumed() {
1464 assert_eq!(
1465 body(
1466 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1467 ),
1468 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1469 );
1470 assert_eq!(
1471 body("SELECT sum(i) OVER (ORDER BY i RANGE UNBOUNDED PRECEDING) AS n FROM t"),
1472 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1473 );
1474 assert_eq!(
1475 body(
1476 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS n FROM t"
1477 ),
1478 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1479 );
1480 assert_eq!(
1481 body("SELECT sum(i) OVER (ORDER BY i ROWS CURRENT ROW) AS n FROM t"),
1482 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW) AS n FROM t"
1483 );
1484 assert_eq!(
1485 body(
1486 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN (1+1) PRECEDING AND CURRENT ROW) AS n FROM t"
1487 ),
1488 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN (1 + 1) PRECEDING AND CURRENT ROW) AS n FROM t"
1489 );
1490 assert_eq!(
1491 body(
1492 "SELECT sum(i) OVER (ORDER BY i GROUPS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS n FROM t"
1493 ),
1494 "SELECT sum(i) OVER (ORDER BY i GROUPS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS n FROM t"
1495 );
1496 assert_eq!(
1497 body(
1498 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP) AS n FROM t"
1499 ),
1500 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP) AS n FROM t"
1501 );
1502 assert_eq!(
1503 body(
1504 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS n FROM t"
1505 ),
1506 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS n FROM t"
1507 );
1508 }
1509
1510 #[test]
1513 fn a_frame_that_covers_the_partition_is_written_as_a_row_count() {
1514 for unit in ["ROWS", "RANGE", "GROUPS"] {
1515 assert_eq!(
1516 body(&format!(
1517 "SELECT sum(i) OVER (ORDER BY i {unit} BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS n FROM t"
1518 )),
1519 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS n FROM t"
1520 );
1521 }
1522 assert_eq!(
1523 body(
1524 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) AS n FROM t"
1525 ),
1526 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) AS n FROM t"
1527 );
1528 }
1529
1530 #[test]
1533 fn a_named_window_is_written_out_where_it_was_used() {
1534 assert_eq!(
1535 body("SELECT sum(i) OVER w AS n FROM t WINDOW w AS (PARTITION BY i 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 (w) AS n FROM t WINDOW w AS (ORDER BY i)"),
1540 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1541 );
1542 assert_eq!(
1543 body(
1544 "SELECT sum(i) OVER (w ROWS UNBOUNDED PRECEDING) AS n FROM t WINDOW w AS (PARTITION BY i)"
1545 ),
1546 "SELECT sum(i) OVER (PARTITION BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1547 );
1548 assert_eq!(
1549 body("SELECT sum(i) OVER (w PARTITION BY i) AS n FROM t WINDOW w AS (ORDER BY i)"),
1550 "SELECT sum(i) OVER (PARTITION BY i ORDER BY i) AS n FROM t"
1551 );
1552 assert_eq!(
1553 body("SELECT sum(i) OVER v AS n FROM t WINDOW w AS (ORDER BY i), v AS (w)"),
1554 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1555 );
1556 }
1557}