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 } => call(ast, name, args, distinct),
322 Expr::Window { name, args, distinct, ignore_nulls, spec } => {
323 window(ast, name, args, distinct, 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) -> String {
604 let written = parts(ast, name);
605 let list = ast.expr_list(args);
606 if list.len() == 1
609 && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
610 if qualifier.is_empty() && replacements.is_empty())
611 && written.eq_ignore_ascii_case("count")
612 {
613 return "count_star()".to_string();
614 }
615 let word = if distinct { "DISTINCT " } else { "" };
616 format!("{}({word}{})", operator(ast, name, &written), exprs(ast, args))
617}
618
619fn window(
626 ast: &Ast,
627 name: Slice,
628 args: Slice,
629 distinct: bool,
630 ignore_nulls: bool,
631 spec: WindowRef,
632) -> String {
633 let word = if distinct { "DISTINCT " } else { "" };
634 let nulls = if ignore_nulls { " IGNORE NULLS" } else { "" };
636 let written = parts(ast, name);
637 let list = ast.expr_list(args);
641 let bare = list.len() == 1
642 && matches!(ast.expr(list[0]), Expr::Star { qualifier, replacements }
643 if qualifier.is_empty() && replacements.is_empty());
644 let inner = if bare { String::new() } else { exprs(ast, args) };
645 let call = format!("{}({word}{inner}{nulls})", operator(ast, name, &written));
646 let held = ast.window(spec);
647 let mut inside: Vec<String> = Vec::new();
648 if !held.partition.is_empty() {
649 inside.push(format!("PARTITION BY {}", exprs(ast, held.partition)));
650 }
651 if !held.order.is_empty() {
652 let items: Vec<String> =
653 ast.order_list(held.order).iter().map(|item| order(ast, item)).collect();
654 inside.push(format!("ORDER BY {}", items.join(", ")));
655 }
656 if !held.frame_is_default() {
657 let unit = match held.unit {
658 WindowUnit::Rows => "ROWS",
659 WindowUnit::Range => "RANGE",
660 WindowUnit::Groups => "GROUPS",
661 };
662 let mut frame =
663 format!("{unit} BETWEEN {} AND {}", bound(ast, held.start), bound(ast, held.end));
664 frame += match held.exclude {
665 WindowExclude::NoOthers => "",
666 WindowExclude::CurrentRow => " EXCLUDE CURRENT ROW",
667 WindowExclude::Group => " EXCLUDE GROUP",
668 WindowExclude::Ties => " EXCLUDE TIES",
669 };
670 inside.push(frame);
671 }
672 format!("{call} OVER ({})", inside.join(" "))
673}
674
675fn bound(ast: &Ast, end: WindowBound) -> String {
677 match end {
678 WindowBound::UnboundedPreceding => "UNBOUNDED PRECEDING".to_string(),
679 WindowBound::Preceding(offset) => format!("{} PRECEDING", expr(ast, offset)),
680 WindowBound::CurrentRow => "CURRENT ROW".to_string(),
681 WindowBound::Following(offset) => format!("{} FOLLOWING", expr(ast, offset)),
682 WindowBound::UnboundedFollowing => "UNBOUNDED FOLLOWING".to_string(),
683 }
684}
685
686fn operator(ast: &Ast, name: Slice, written: &str) -> String {
693 let one = ast.name(name).next().unwrap_or_default();
694 let alone = ast.name(name).count() == 1;
695 if alone && (one.eq_ignore_ascii_case("coalesce") || one.eq_ignore_ascii_case("ifnull")) {
696 return "COALESCE".to_string();
697 }
698 written.to_string()
699}
700
701fn case(ast: &Ast, operand: ExprRef, arms: Slice, otherwise: ExprRef) -> String {
707 let mut out = "CASE ".to_string();
708 for arm in ast.arm_list(arms) {
709 let when = when(ast, operand, arm);
710 out += &format!(" WHEN ({when}) THEN ({})", expr(ast, arm.then));
711 }
712 let last = if otherwise == NONE { "NULL".to_string() } else { expr(ast, otherwise) };
713 out + &format!(" ELSE {last} END")
714}
715
716fn when(ast: &Ast, operand: ExprRef, arm: &CaseArm) -> String {
719 if operand == NONE {
720 return expr(ast, arm.when);
721 }
722 format!("({} = {})", expr(ast, operand), expr(ast, arm.when))
723}
724
725fn typename(text: &str) -> String {
739 let text = text.trim();
740 if let Some(open) = suffix(text) {
743 return typename(&text[..open]) + &text[open..];
744 }
745 let (base, arguments) = arguments(text);
746 let Some(name) = standard(base) else {
747 let base = unquote(base);
748 return match arguments {
749 Some(arguments) => format!("{}({arguments})", catalogued(&base)),
750 None => catalogued(&base),
751 };
752 };
753 match (name, arguments) {
754 ("STRUCT" | "UNION", Some(inside)) => {
757 let written: Vec<String> = pieces(inside).iter().map(|piece| field(piece)).collect();
758 format!("{name}({})", written.join(", "))
759 }
760 ("MAP", Some(inside)) => {
761 let written: Vec<String> = pieces(inside).iter().map(|piece| typename(piece)).collect();
762 format!("{name}({})", written.join(", "))
763 }
764 ("DECIMAL" | "VARCHAR", Some(inside)) => {
767 format!("{name}({})", pieces(inside).join(", "))
768 }
769 _ => name.to_string(),
772 }
773}
774
775fn catalogued(base: &str) -> String {
788 if base.eq_ignore_ascii_case("json") { quoted(base) } else { base.to_string() }
789}
790
791fn unquote(base: &str) -> String {
793 match base.strip_prefix('"').and_then(|rest| rest.strip_suffix('"')) {
794 Some(inside) => inside.replace("\"\"", "\""),
795 None => base.to_string(),
796 }
797}
798
799fn suffix(text: &str) -> Option<usize> {
801 let rest = text.strip_suffix(']')?;
802 let open = rest.rfind('[')?;
803 rest[open + 1..].bytes().all(|byte| byte.is_ascii_digit()).then_some(open)
804}
805
806fn arguments(text: &str) -> (&str, Option<&str>) {
808 let Some(rest) = text.strip_suffix(')') else {
809 return (text, None);
810 };
811 let mut depth = 0usize;
812 for (at, byte) in rest.bytes().enumerate() {
813 match byte {
814 b'(' if depth == 0 => depth = 1,
815 b'(' => depth += 1,
816 b')' => depth -= 1,
817 _ => continue,
818 }
819 if depth == 1 && byte == b'(' {
820 return (rest[..at].trim(), Some(rest[at + 1..].trim()));
821 }
822 }
823 (text, None)
824}
825
826fn pieces(inside: &str) -> Vec<&str> {
828 let mut found = Vec::new();
829 let (mut depth, mut quoted, mut start) = (0usize, false, 0usize);
830 for (at, byte) in inside.bytes().enumerate() {
831 match byte {
832 b'"' => quoted = !quoted,
833 b'(' | b'[' if !quoted => depth += 1,
834 b')' | b']' if !quoted => depth = depth.saturating_sub(1),
835 b',' if !quoted && depth == 0 => {
836 found.push(inside[start..at].trim());
837 start = at + 1;
838 }
839 _ => {}
840 }
841 }
842 found.push(inside[start..].trim());
843 found
844}
845
846fn field(piece: &str) -> String {
848 let mut quoting = false;
849 for (at, byte) in piece.bytes().enumerate() {
850 match byte {
851 b'"' => quoting = !quoting,
852 byte if byte.is_ascii_whitespace() && !quoting => {
853 let name = piece[..at].trim();
854 let name =
855 if name.starts_with('"') { quoted(&unquote(name)) } else { name.to_string() };
856 return format!("{name} {}", typename(&piece[at + 1..]));
857 }
858 _ => {}
859 }
860 }
861 piece.to_string()
862}
863
864fn standard(base: &str) -> Option<&'static str> {
870 const NAMES: &[(&str, &str)] = &[
871 ("BOOLEAN", "BOOLEAN"),
872 ("INT", "INTEGER"),
873 ("INTEGER", "INTEGER"),
874 ("SMALLINT", "SMALLINT"),
875 ("BIGINT", "BIGINT"),
876 ("DEC", "DECIMAL"),
877 ("DECIMAL", "DECIMAL"),
878 ("NUMERIC", "DECIMAL"),
879 ("REAL", "FLOAT"),
880 ("FLOAT", "FLOAT"),
881 ("DOUBLE PRECISION", "DOUBLE"),
882 ("CHAR", "VARCHAR"),
883 ("CHARACTER", "VARCHAR"),
884 ("CHARACTER VARYING", "VARCHAR"),
885 ("NATIONAL CHARACTER", "VARCHAR"),
886 ("NATIONAL CHARACTER VARYING", "VARCHAR"),
887 ("VARCHAR", "VARCHAR"),
888 ("BIT", "BIT"),
889 ("DATE", "DATE"),
890 ("TIME", "TIME"),
891 ("TIME WITH TIME ZONE", "TIME WITH TIME ZONE"),
892 ("TIME WITHOUT TIME ZONE", "TIME"),
893 ("TIMESTAMP", "TIMESTAMP"),
894 ("TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH TIME ZONE"),
895 ("TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP"),
896 ("INTERVAL", "INTERVAL"),
897 ("STRUCT", "STRUCT"),
898 ("UNION", "UNION"),
899 ("MAP", "MAP"),
900 ];
901 let written: Vec<&str> = base.split_whitespace().collect();
902 let written = written.join(" ");
903 NAMES
904 .iter()
905 .find(|(spelling, _)| spelling.eq_ignore_ascii_case(&written))
906 .map(|(_, name)| *name)
907}
908
909fn exprs(ast: &Ast, list: Slice) -> String {
911 let written: Vec<String> = ast.expr_list(list).iter().map(|&item| expr(ast, item)).collect();
912 written.join(", ")
913}
914
915fn names(ast: &Ast, list: Slice) -> String {
917 ast.name(list).map(quoted).collect::<Vec<_>>().join(", ")
918}
919
920fn parts(ast: &Ast, list: Slice) -> String {
922 ast.name(list).map(quoted).collect::<Vec<_>>().join(".")
923}
924
925#[cfg(test)]
926mod tests {
927 use super::create_view;
928 use crate::ast::Statement;
929 use crate::transform::parse_ast;
930
931 fn whole(sql: &str) -> String {
933 let ast = parse_ast(sql).unwrap_or_else(|error| panic!("{sql} should parse: {error}"));
934 let Statement::CreateView(index) = ast.statements[0] else {
935 panic!("that was not a create view");
936 };
937 create_view(&ast, index)
938 }
939
940 fn body(query: &str) -> String {
942 let written = whole(&format!("CREATE VIEW v AS {query}"));
943 written
944 .strip_prefix("CREATE VIEW v AS ")
945 .and_then(|rest| rest.strip_suffix(';'))
946 .expect("the statement wrapper is there")
947 .to_string()
948 }
949
950 #[test]
951 fn a_statement_loses_its_qualification_and_its_or_replace() {
952 assert_eq!(whole("CREATE VIEW main.v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
953 assert_eq!(whole("CREATE OR REPLACE VIEW v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
954 assert_eq!(whole("CREATE VIEW IF NOT EXISTS v AS SELECT 1"), "CREATE VIEW v AS SELECT 1;");
955 assert_eq!(whole("CREATE TEMP VIEW v AS SELECT 1"), "CREATE TEMP VIEW v AS SELECT 1;");
956 }
957
958 #[test]
960 fn an_alias_list_is_written_with_a_space_in_front_of_it() {
961 assert_eq!(
962 whole(r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2"#),
963 r#"CREATE VIEW v ("Weird Name", "x y") AS SELECT 1, 2;"#
964 );
965 }
966
967 #[test]
968 fn comments_and_spacing_go_and_the_case_of_a_name_stays() {
969 assert_eq!(
970 whole("CREATE VIEW v AS SELECT X /* a note */ FROM T"),
971 "CREATE VIEW v AS SELECT X FROM T;"
972 );
973 }
974
975 #[test]
976 fn every_binary_operation_is_parenthesised_and_every_unary_one_parenthesises_its_operand() {
977 assert_eq!(body("SELECT x + y * 2 - 1 FROM t"), "SELECT ((x + (y * 2)) - 1) FROM t");
978 assert_eq!(
979 body("SELECT x > 1 AND y < 2 OR b FROM t"),
980 "SELECT (((x > 1) AND (y < 2)) OR b) FROM t"
981 );
982 assert_eq!(body("SELECT NOT b FROM t"), "SELECT (NOT b) FROM t");
983 assert_eq!(body("SELECT ~x FROM t"), "SELECT ~(x) FROM t");
984 assert_eq!(body("SELECT +x FROM t"), "SELECT +(x) FROM t");
985 assert_eq!(body("SELECT -x FROM t"), "SELECT -(x) FROM t");
986 }
987
988 #[test]
991 fn a_minus_in_front_of_a_constant_folds_into_it() {
992 assert_eq!(body("SELECT -1"), "SELECT -1");
993 assert_eq!(body("SELECT - -3"), "SELECT 3");
994 assert_eq!(body("SELECT +3"), "SELECT +(3)");
995 }
996
997 #[test]
998 fn the_null_tests_and_the_boolean_tests() {
999 assert_eq!(body("SELECT x IS NULL FROM t"), "SELECT (x IS NULL) FROM t");
1000 assert_eq!(body("SELECT x ISNULL FROM t"), "SELECT (x IS NULL) FROM t");
1001 assert_eq!(body("SELECT x NOTNULL FROM t"), "SELECT (x IS NOT NULL) FROM t");
1002 assert_eq!(
1003 body("SELECT b IS TRUE FROM t"),
1004 "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM true) FROM t"
1005 );
1006 assert_eq!(
1007 body("SELECT b IS NOT TRUE FROM t"),
1008 "SELECT (CAST(b AS BOOLEAN) IS DISTINCT FROM true) FROM t"
1009 );
1010 assert_eq!(
1011 body("SELECT b IS FALSE FROM t"),
1012 "SELECT (CAST(b AS BOOLEAN) IS NOT DISTINCT FROM false) FROM t"
1013 );
1014 assert_eq!(body("SELECT b IS UNKNOWN FROM t"), "SELECT (b IS NULL) FROM t");
1015 assert_eq!(body("SELECT b IS NOT UNKNOWN FROM t"), "SELECT (b IS NOT NULL) FROM t");
1016 assert_eq!(
1017 body("SELECT x IS DISTINCT FROM y FROM t"),
1018 "SELECT (x IS DISTINCT FROM y) FROM t"
1019 );
1020 }
1021
1022 #[test]
1023 fn a_negated_between_or_in_is_a_not_around_the_plain_one() {
1024 assert_eq!(body("SELECT x BETWEEN 1 AND 10 FROM t"), "SELECT (x BETWEEN 1 AND 10) FROM t");
1025 assert_eq!(
1026 body("SELECT x NOT BETWEEN 1 AND 2 FROM t"),
1027 "SELECT (NOT (x BETWEEN 1 AND 2)) FROM t"
1028 );
1029 assert_eq!(body("SELECT x IN (1, 2, 3) FROM t"), "SELECT (x IN (1, 2, 3)) FROM t");
1030 assert_eq!(body("SELECT x NOT IN (1, 2) FROM t"), "SELECT (NOT (x IN (1, 2))) FROM t");
1031 assert_eq!(body("SELECT x IN (SELECT y FROM t)"), "SELECT (x = ANY(SELECT y FROM t))");
1032 assert_eq!(
1033 body("SELECT x NOT IN (SELECT y FROM t)"),
1034 "SELECT (NOT (x = ANY(SELECT y FROM t)))"
1035 );
1036 assert_eq!(body("SELECT x = ANY (SELECT y FROM t)"), "SELECT (x = ANY(SELECT y FROM t))");
1037 assert_eq!(
1038 body("SELECT x > ALL (SELECT y FROM t)"),
1039 "SELECT (NOT (x <= ANY(SELECT y FROM t)))"
1040 );
1041 }
1042
1043 #[test]
1046 fn the_pattern_operators_come_back_as_symbols() {
1047 assert_eq!(body("SELECT s LIKE 'a' FROM t"), "SELECT (s ~~ 'a') FROM t");
1048 assert_eq!(body("SELECT s NOT LIKE 'a' FROM t"), "SELECT (s !~~ 'a') FROM t");
1049 assert_eq!(body("SELECT s ILIKE 'a' FROM t"), "SELECT (s ~~* 'a') FROM t");
1050 assert_eq!(body("SELECT s NOT ILIKE 'a' FROM t"), "SELECT (s !~~* 'a') FROM t");
1051 assert_eq!(body("SELECT s GLOB 'a' FROM t"), "SELECT (s ~~~ 'a') FROM t");
1052 assert_eq!(body("SELECT s !~ 'a' FROM t"), "SELECT (s !~ 'a') FROM t");
1053 assert_eq!(
1054 body("SELECT s NOT SIMILAR TO 'a' FROM t"),
1055 "SELECT (NOT regexp_full_match(s, 'a')) FROM t"
1056 );
1057 }
1058
1059 #[test]
1060 fn collate_has_no_parentheses_and_the_rest_of_the_operators_keep_their_spelling() {
1061 assert_eq!(body("SELECT s COLLATE NOCASE FROM t"), "SELECT s COLLATE NOCASE FROM t");
1062 assert_eq!(body("SELECT x // y FROM t"), "SELECT (x // y) FROM t");
1063 assert_eq!(body("SELECT x || y FROM t"), "SELECT (x || y) FROM t");
1064 assert_eq!(body("SELECT x @> y FROM t"), "SELECT (x @> y) FROM t");
1065 assert_eq!(body("SELECT x <=> y FROM t"), "SELECT (x <=> y) FROM t");
1066 }
1067
1068 #[test]
1070 fn a_case_is_written_the_long_way_round() {
1071 assert_eq!(
1072 body("SELECT CASE WHEN x > 0 THEN 'a' WHEN x < 0 THEN 'b' ELSE 'c' END FROM t"),
1073 "SELECT CASE WHEN ((x > 0)) THEN ('a') WHEN ((x < 0)) THEN ('b') ELSE 'c' END FROM t"
1074 );
1075 assert_eq!(
1076 body("SELECT CASE x WHEN 1 THEN 'a' END FROM t"),
1077 "SELECT CASE WHEN ((x = 1)) THEN ('a') ELSE NULL END FROM t"
1078 );
1079 }
1080
1081 #[test]
1082 fn a_cast_writes_its_type_in_upper_case_with_a_space_after_the_comma() {
1083 assert_eq!(body("SELECT x::varchar FROM t"), "SELECT CAST(x AS VARCHAR) FROM t");
1084 assert_eq!(
1085 body("SELECT cast(x as decimal(4,1)) FROM t"),
1086 "SELECT CAST(x AS DECIMAL(4, 1)) FROM t"
1087 );
1088 assert_eq!(
1089 body("SELECT TRY_CAST(s AS INTEGER) FROM t"),
1090 "SELECT TRY_CAST(s AS INTEGER) FROM t"
1091 );
1092 }
1093
1094 #[test]
1096 fn a_standard_type_name_is_resolved_and_the_modifiers_it_was_written_with_survive() {
1097 let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
1098 assert_eq!(cast("int"), "SELECT CAST(x AS INTEGER)");
1099 assert_eq!(cast("numeric(5)"), "SELECT CAST(x AS DECIMAL(5))");
1100 assert_eq!(cast("decimal"), "SELECT CAST(x AS DECIMAL)");
1101 assert_eq!(cast("varchar(10)"), "SELECT CAST(x AS VARCHAR(10))");
1102 assert_eq!(cast("national character(2)"), "SELECT CAST(x AS VARCHAR(2))");
1103 assert_eq!(cast("float(10)"), "SELECT CAST(x AS FLOAT)");
1106 assert_eq!(cast("real"), "SELECT CAST(x AS FLOAT)");
1107 assert_eq!(cast("double precision"), "SELECT CAST(x AS DOUBLE)");
1108 assert_eq!(cast("time with time zone"), "SELECT CAST(x AS TIME WITH TIME ZONE)");
1109 assert_eq!(cast("int[]"), "SELECT CAST(x AS INTEGER[])");
1110 assert_eq!(cast("int[2][3]"), "SELECT CAST(x AS INTEGER[2][3])");
1111 assert_eq!(cast("map(int, varchar)"), "SELECT CAST(x AS MAP(INTEGER, VARCHAR))");
1112 assert_eq!(cast("union(a int)"), "SELECT CAST(x AS UNION(a INTEGER))");
1113 }
1114
1115 #[test]
1117 fn a_struct_field_keeps_its_name_and_its_type_goes_round_again() {
1118 assert_eq!(body("SELECT CAST(x AS struct(a bool))"), "SELECT CAST(x AS STRUCT(a bool))");
1119 assert_eq!(
1120 body("SELECT CAST(x AS struct(\"A b\" int))"),
1121 "SELECT CAST(x AS STRUCT(\"A b\" INTEGER))"
1122 );
1123 }
1124
1125 #[test]
1127 fn a_type_name_the_grammar_has_no_rule_for_keeps_the_case_it_was_written_in() {
1128 let cast = |written: &str| body(&format!("SELECT CAST(x AS {written})"));
1129 assert_eq!(cast("text"), "SELECT CAST(x AS text)");
1130 assert_eq!(cast("TEXT"), "SELECT CAST(x AS TEXT)");
1131 assert_eq!(cast("DOUBLE"), "SELECT CAST(x AS DOUBLE)");
1132 assert_eq!(cast("bool"), "SELECT CAST(x AS bool)");
1133 assert_eq!(cast("\"bool\""), "SELECT CAST(x AS bool)");
1134 assert_eq!(cast("int4[]"), "SELECT CAST(x AS int4[])");
1135 assert_eq!(cast("TIMESTAMPTZ"), "SELECT CAST(x AS TIMESTAMPTZ)");
1136 assert_eq!(cast("JSON"), "SELECT CAST(x AS \"JSON\")");
1138 assert_eq!(cast("json"), "SELECT CAST(x AS \"json\")");
1139 assert_eq!(cast("json[]"), "SELECT CAST(x AS \"json\"[])");
1140 assert_eq!(cast("struct(a json)"), "SELECT CAST(x AS STRUCT(a \"json\"))");
1141 }
1142
1143 #[test]
1144 fn a_star_count_is_a_function_of_its_own_and_a_list_is_a_call() {
1145 assert_eq!(body("SELECT count(*) FROM t"), "SELECT count_star() FROM t");
1146 assert_eq!(body("SELECT count(DISTINCT x) FROM t"), "SELECT count(DISTINCT x) FROM t");
1147 assert_eq!(body("SELECT [1, 2, 3]"), "SELECT list_value(1, 2, 3)");
1148 assert_eq!(body("SELECT []"), "SELECT list_value()");
1149 }
1150
1151 #[test]
1154 fn a_function_name_is_quoted_when_it_is_a_keyword() {
1155 assert_eq!(body("SELECT nullif(x, 1) FROM t"), "SELECT \"nullif\"(x, 1) FROM t");
1156 assert_eq!(body("SELECT length(s) FROM t"), "SELECT length(s) FROM t");
1157 }
1158
1159 #[test]
1160 fn the_literals() {
1161 assert_eq!(body("SELECT NULL, TRUE, FALSE"), "SELECT NULL, true, false");
1162 assert_eq!(body("SELECT 1.50, .5, 1_000"), "SELECT 1.50, .5, 1000");
1163 assert_eq!(body("SELECT 'it''s'"), "SELECT 'it''s'");
1164 }
1165
1166 #[test]
1168 fn a_number_is_written_back_as_the_value_the_shape_of_it_made() {
1169 assert_eq!(body("SELECT 007, 1_000"), "SELECT 7, 1000");
1170 assert_eq!(body("SELECT 1.50, 00.5, 1., 0.0"), "SELECT 1.50, 0.5, 1, 0.0");
1171 assert_eq!(body("SELECT 1e3, 1.5e2, 1e-3, 5e-4"), "SELECT 1000.0, 150.0, 0.001, 0.0005");
1172 assert_eq!(body("SELECT 5e-5, 2.5e-5, 1e-10"), "SELECT 5e-05, 2.5e-05, 1e-10");
1173 assert_eq!(body("SELECT 1e15, 1e16, 1e100"), "SELECT 1000000000000000.0, 1e+16, 1e+100");
1174 }
1175
1176 #[test]
1178 fn an_extract_is_a_date_part_call_and_the_keyword_it_named_has_one_spelling() {
1179 assert_eq!(body("SELECT extract(year FROM d)"), "SELECT date_part('YEAR', d)");
1180 assert_eq!(body("SELECT extract(years FROM d)"), "SELECT date_part('YEAR', d)");
1181 assert_eq!(body("SELECT extract(seconds FROM d)"), "SELECT date_part('SECOND', d)");
1182 assert_eq!(
1184 body("SELECT extract(millisecond FROM d)"),
1185 "SELECT date_part('MILLISECONDS', d)"
1186 );
1187 assert_eq!(
1188 body("SELECT extract(microseconds FROM d)"),
1189 "SELECT date_part('MICROSECONDS', d)"
1190 );
1191 assert_eq!(body("SELECT extract(millennia FROM d)"), "SELECT date_part('MILLENNIUM', d)");
1192 assert_eq!(body("SELECT extract(epoch FROM d)"), "SELECT date_part('epoch', d)");
1194 assert_eq!(body("SELECT extract(dow FROM d)"), "SELECT date_part('dow', d)");
1195 }
1196
1197 #[test]
1199 fn coalesce_and_ifnull_are_one_operator_and_it_is_written_in_upper_case() {
1200 assert_eq!(body("SELECT coalesce(x, y)"), "SELECT COALESCE(x, y)");
1201 assert_eq!(body("SELECT IfNull(x, y)"), "SELECT COALESCE(x, y)");
1202 assert_eq!(body("SELECT coalesce(x)"), "SELECT COALESCE(x)");
1204 assert_eq!(body("SELECT nullif(x, y)"), "SELECT \"nullif\"(x, y)");
1206 assert_eq!(body("SELECT greatest(x, y)"), "SELECT greatest(x, y)");
1207 }
1208
1209 #[test]
1210 fn the_modifiers_hang_off_the_query_and_not_off_the_select() {
1211 assert_eq!(body("SELECT x FROM t LIMIT 5 OFFSET 2"), "SELECT x FROM t LIMIT 5 OFFSET 2");
1212 assert_eq!(body("SELECT x FROM t LIMIT 10 PERCENT"), "SELECT x FROM t LIMIT (10) %");
1213 assert_eq!(
1214 body("SELECT x FROM t ORDER BY x ASC, y NULLS LAST"),
1215 "SELECT x FROM t ORDER BY x ASC, y NULLS LAST"
1216 );
1217 assert_eq!(body("SELECT x FROM t ORDER BY ALL"), "SELECT x FROM t ORDER BY COLUMNS(*)");
1218 assert_eq!(body("SELECT x FROM t GROUP BY ALL"), "SELECT x FROM t GROUP BY ALL");
1219 assert_eq!(
1220 body("SELECT x FROM t GROUP BY x HAVING x > 0"),
1221 "SELECT x FROM t GROUP BY x HAVING (x > 0)"
1222 );
1223 assert_eq!(
1224 body("SELECT DISTINCT ON (x) x, y FROM t"),
1225 "SELECT DISTINCT ON (x) x, y FROM t"
1226 );
1227 }
1228
1229 #[test]
1232 fn a_chain_of_set_operations_loses_a_space_in_the_middle() {
1233 assert_eq!(
1234 body("SELECT x FROM t UNION ALL SELECT y FROM t"),
1235 "(SELECT x FROM t) UNION ALL (SELECT y FROM t)"
1236 );
1237 assert_eq!(
1238 body("SELECT x FROM t UNION SELECT y FROM t UNION SELECT 1"),
1239 "(SELECT x FROM t) UNION (SELECT y FROM t)UNION (SELECT 1)"
1240 );
1241 assert_eq!(
1242 body("SELECT x FROM t UNION DISTINCT SELECT y FROM t"),
1243 "(SELECT x FROM t) UNION (SELECT y FROM t)"
1244 );
1245 }
1246
1247 #[test]
1248 fn a_values_body_is_wrapped_in_a_select_that_names_it() {
1249 assert_eq!(
1250 body("VALUES (1, 'a'), (2, 'b')"),
1251 "SELECT * FROM (VALUES (1, 'a'), (2, 'b')) AS valueslist"
1252 );
1253 }
1254
1255 #[test]
1257 fn a_from_list_has_a_space_before_the_comma() {
1258 assert_eq!(body("SELECT 1 FROM t AS t1, t AS t2"), "SELECT 1 FROM t AS t1 , t AS t2");
1259 }
1260
1261 #[test]
1262 fn a_from_item_and_its_aliases() {
1263 assert_eq!(body("SELECT 1 FROM t AS r(n)"), "SELECT 1 FROM t AS r(n)");
1264 assert_eq!(body("SELECT 1 FROM main.t"), "SELECT 1 FROM main.t");
1265 assert_eq!(
1266 body("SELECT 1 FROM (SELECT x FROM t) AS sub"),
1267 "SELECT 1 FROM (SELECT x FROM t) AS sub"
1268 );
1269 assert_eq!(body("SELECT 1 FROM range(10)"), "SELECT 1 FROM \"range\"(10)");
1270 }
1271
1272 #[test]
1275 fn a_join_is_parenthesised_and_so_is_its_condition_twice() {
1276 assert_eq!(
1277 body("SELECT 1 FROM t AS a JOIN t AS b ON a.x = b.y"),
1278 "SELECT 1 FROM (t AS a INNER JOIN t AS b ON ((a.x = b.y)))"
1279 );
1280 assert_eq!(
1281 body("SELECT 1 FROM t LEFT JOIN t AS u USING (x)"),
1282 "SELECT 1 FROM (t LEFT JOIN t AS u USING (x))"
1283 );
1284 assert_eq!(
1285 body("SELECT 1 FROM t CROSS JOIN t AS u"),
1286 "SELECT 1 FROM (t CROSS JOIN t AS u)"
1287 );
1288 assert_eq!(
1289 body("SELECT 1 FROM t FULL OUTER JOIN t AS u ON t.x = u.x"),
1290 "SELECT 1 FROM (t FULL JOIN t AS u ON ((t.x = u.x)))"
1291 );
1292 assert_eq!(
1293 body("SELECT 1 FROM t NATURAL JOIN t AS u"),
1294 "SELECT 1 FROM (t NATURAL INNER JOIN t AS u)"
1295 );
1296 assert_eq!(
1297 body("SELECT 1 FROM t POSITIONAL JOIN t AS u"),
1298 "SELECT 1 FROM (t POSITIONAL JOIN t AS u)"
1299 );
1300 }
1301
1302 #[test]
1303 fn a_target_keeps_its_alias_and_a_star_keeps_its_replace_list() {
1304 assert_eq!(body("SELECT 1 + 2 AS \"quoted alias\""), "SELECT (1 + 2) AS \"quoted alias\"");
1305 assert_eq!(body("SELECT x AS \"select\" FROM t"), "SELECT x AS \"select\" FROM t");
1306 assert_eq!(body("SELECT t.* FROM t"), "SELECT t.* FROM t");
1307 assert_eq!(
1308 body("SELECT * REPLACE (x + 1 AS x) FROM t"),
1309 "SELECT * REPLACE ((x + 1) AS x) FROM t"
1310 );
1311 }
1312
1313 #[test]
1314 fn a_describe_gets_parentheses_round_what_it_describes() {
1315 assert_eq!(body("DESCRIBE SELECT 1"), "DESCRIBE (SELECT 1)");
1316 }
1317
1318 #[test]
1321 fn a_window_is_written_with_the_parts_that_were_written_in_it() {
1322 assert_eq!(
1323 body("SELECT row_number() OVER () AS n FROM t"),
1324 "SELECT row_number() OVER () AS n FROM t"
1325 );
1326 assert_eq!(
1327 body(
1328 "SELECT row_number() OVER (PARTITION BY a ORDER BY b DESC NULLS FIRST) AS n FROM t"
1329 ),
1330 "SELECT row_number() OVER (PARTITION BY a ORDER BY b DESC NULLS FIRST) AS n FROM t"
1331 );
1332 assert_eq!(
1333 body(
1334 "SELECT sum(i) OVER (PARTITION BY i, i+1 ORDER BY i ASC NULLS LAST, i DESC) AS n FROM t"
1335 ),
1336 "SELECT sum(i) OVER (PARTITION BY i, (i + 1) ORDER BY i ASC NULLS LAST, i DESC) AS n FROM t"
1337 );
1338 assert_eq!(
1339 body("SELECT sum(DISTINCT i) OVER (ORDER BY i) AS n FROM t"),
1340 "SELECT sum(DISTINCT i) OVER (ORDER BY i) AS n FROM t"
1341 );
1342 assert_eq!(
1343 body("SELECT first_value(i IGNORE NULLS) OVER (ORDER BY i) AS n FROM t"),
1344 "SELECT first_value(i IGNORE NULLS) OVER (ORDER BY i) AS n FROM t"
1345 );
1346 assert_eq!(
1347 body("SELECT first_value(i RESPECT NULLS) OVER (ORDER BY i) AS n FROM t"),
1348 "SELECT first_value(i) OVER (ORDER BY i) AS n FROM t"
1349 );
1350 assert_eq!(
1351 body("SELECT count(*) OVER () AS n FROM t"),
1352 "SELECT count() OVER () AS n FROM t"
1353 );
1354 assert_eq!(
1355 body("SELECT main.sum(i) OVER (ORDER BY i) AS n FROM t"),
1356 "SELECT main.sum(i) OVER (ORDER BY i) AS n FROM t"
1357 );
1358 }
1359
1360 #[test]
1362 fn a_frame_is_written_only_when_it_is_not_the_one_that_was_assumed() {
1363 assert_eq!(
1364 body(
1365 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1366 ),
1367 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1368 );
1369 assert_eq!(
1370 body("SELECT sum(i) OVER (ORDER BY i RANGE UNBOUNDED PRECEDING) AS n FROM t"),
1371 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1372 );
1373 assert_eq!(
1374 body(
1375 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS n FROM t"
1376 ),
1377 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1378 );
1379 assert_eq!(
1380 body("SELECT sum(i) OVER (ORDER BY i ROWS CURRENT ROW) AS n FROM t"),
1381 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND CURRENT ROW) AS n FROM t"
1382 );
1383 assert_eq!(
1384 body(
1385 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN (1+1) PRECEDING AND CURRENT ROW) AS n FROM t"
1386 ),
1387 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN (1 + 1) PRECEDING AND CURRENT ROW) AS n FROM t"
1388 );
1389 assert_eq!(
1390 body(
1391 "SELECT sum(i) OVER (ORDER BY i GROUPS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS n FROM t"
1392 ),
1393 "SELECT sum(i) OVER (ORDER BY i GROUPS BETWEEN CURRENT ROW AND 2 FOLLOWING EXCLUDE CURRENT ROW) AS n FROM t"
1394 );
1395 assert_eq!(
1396 body(
1397 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP) AS n FROM t"
1398 ),
1399 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP) AS n FROM t"
1400 );
1401 assert_eq!(
1402 body(
1403 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS n FROM t"
1404 ),
1405 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS n FROM t"
1406 );
1407 }
1408
1409 #[test]
1412 fn a_frame_that_covers_the_partition_is_written_as_a_row_count() {
1413 for unit in ["ROWS", "RANGE", "GROUPS"] {
1414 assert_eq!(
1415 body(&format!(
1416 "SELECT sum(i) OVER (ORDER BY i {unit} BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS n FROM t"
1417 )),
1418 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS n FROM t"
1419 );
1420 }
1421 assert_eq!(
1422 body(
1423 "SELECT sum(i) OVER (ORDER BY i RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) AS n FROM t"
1424 ),
1425 "SELECT sum(i) OVER (ORDER BY i ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) AS n FROM t"
1426 );
1427 }
1428
1429 #[test]
1432 fn a_named_window_is_written_out_where_it_was_used() {
1433 assert_eq!(
1434 body("SELECT sum(i) OVER w AS n FROM t WINDOW w AS (PARTITION BY i ORDER BY i)"),
1435 "SELECT sum(i) OVER (PARTITION BY i ORDER BY i) AS n FROM t"
1436 );
1437 assert_eq!(
1438 body("SELECT sum(i) OVER (w) AS n FROM t WINDOW w AS (ORDER BY i)"),
1439 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1440 );
1441 assert_eq!(
1442 body(
1443 "SELECT sum(i) OVER (w ROWS UNBOUNDED PRECEDING) AS n FROM t WINDOW w AS (PARTITION BY i)"
1444 ),
1445 "SELECT sum(i) OVER (PARTITION BY i ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS n FROM t"
1446 );
1447 assert_eq!(
1448 body("SELECT sum(i) OVER (w PARTITION BY i) AS n FROM t WINDOW w AS (ORDER BY i)"),
1449 "SELECT sum(i) OVER (PARTITION BY i ORDER BY i) AS n FROM t"
1450 );
1451 assert_eq!(
1452 body("SELECT sum(i) OVER v AS n FROM t WINDOW w AS (ORDER BY i), v AS (w)"),
1453 "SELECT sum(i) OVER (ORDER BY i) AS n FROM t"
1454 );
1455 }
1456}