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