1#[allow(clippy::wildcard_imports)]
8use crate::*;
9use fsqlite_types::SqliteValue;
10use smallvec::SmallVec;
11use std::fmt;
12
13fn comma_list<T: fmt::Display>(f: &mut fmt::Formatter<'_>, items: &[T]) -> fmt::Result {
18 for (i, item) in items.iter().enumerate() {
19 if i > 0 {
20 f.write_str(", ")?;
21 }
22 write!(f, "{item}")?;
23 }
24 Ok(())
25}
26
27fn comma_list_fn<T>(
28 f: &mut fmt::Formatter<'_>,
29 items: &[T],
30 fmt_item: impl Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
31) -> fmt::Result {
32 for (i, item) in items.iter().enumerate() {
33 if i > 0 {
34 f.write_str(", ")?;
35 }
36 fmt_item(item, f)?;
37 }
38 Ok(())
39}
40
41const SQL_KEYWORDS: &[&str] = &[
47 "ABORT",
48 "ACTION",
49 "ADD",
50 "AFTER",
51 "ALL",
52 "ALTER",
53 "ALWAYS",
54 "ANALYZE",
55 "AND",
56 "AS",
57 "ASC",
58 "ATTACH",
59 "AUTOINCREMENT",
60 "BEFORE",
61 "BEGIN",
62 "BETWEEN",
63 "BY",
64 "CASCADE",
65 "CASE",
66 "CAST",
67 "CHECK",
68 "COLLATE",
69 "COLUMN",
70 "COMMIT",
71 "COMMITSEQ",
72 "CONCURRENT",
73 "CONFLICT",
74 "CONSTRAINT",
75 "CREATE",
76 "CROSS",
77 "CURRENT_DATE",
78 "CURRENT_TIME",
79 "CURRENT_TIMESTAMP",
80 "DATABASE",
81 "DEFAULT",
82 "DEFERRABLE",
83 "DEFERRED",
84 "DELETE",
85 "DESC",
86 "DETACH",
87 "DISTINCT",
88 "DO",
89 "DROP",
90 "EACH",
91 "ELSE",
92 "END",
93 "ESCAPE",
94 "EXCEPT",
95 "EXCLUDE",
96 "EXCLUSIVE",
97 "EXISTS",
98 "EXPLAIN",
99 "FAIL",
100 "FALSE",
101 "FILTER",
102 "FIRST",
103 "FOLLOWING",
104 "FOR",
105 "FOREIGN",
106 "FROM",
107 "FULL",
108 "GENERATED",
109 "GLOB",
110 "GROUP",
111 "GROUPS",
112 "HAVING",
113 "IF",
114 "IGNORE",
115 "IMMEDIATE",
116 "IN",
117 "INDEX",
118 "INDEXED",
119 "INITIALLY",
120 "INNER",
121 "INSERT",
122 "INSTEAD",
123 "INTERSECT",
124 "INTO",
125 "IS",
126 "ISNULL",
127 "JOIN",
128 "KEY",
129 "LAST",
130 "LEFT",
131 "LIKE",
132 "LIMIT",
133 "MATCH",
134 "MATERIALIZED",
135 "NATURAL",
136 "NO",
137 "NOT",
138 "NOTHING",
139 "NOTNULL",
140 "NULL",
141 "NULLS",
142 "OF",
143 "OFFSET",
144 "ON",
145 "OR",
146 "ORDER",
147 "OTHERS",
148 "OUTER",
149 "OVER",
150 "PARTITION",
151 "PLAN",
152 "PRAGMA",
153 "PRECEDING",
154 "PRIMARY",
155 "QUERY",
156 "RAISE",
157 "RANGE",
158 "RECURSIVE",
159 "REFERENCES",
160 "REGEXP",
161 "REINDEX",
162 "RELEASE",
163 "RENAME",
164 "REPLACE",
165 "RESTRICT",
166 "RETURNING",
167 "RIGHT",
168 "ROLLBACK",
169 "ROW",
170 "ROWS",
171 "SAVEPOINT",
172 "SELECT",
173 "SET",
174 "STORED",
175 "STRICT",
176 "TABLE",
177 "TEMP",
178 "TEMPORARY",
179 "THEN",
180 "TIES",
181 "TO",
182 "TRANSACTION",
183 "TRIGGER",
184 "TRUE",
185 "UNBOUNDED",
186 "UNION",
187 "UNIQUE",
188 "UPDATE",
189 "USING",
190 "VACUUM",
191 "VALUES",
192 "VIEW",
193 "VIRTUAL",
194 "WHEN",
195 "WHERE",
196 "WINDOW",
197 "WITH",
198 "WITHOUT",
199];
200
201fn is_sql_keyword(name: &str) -> bool {
202 name.is_ascii()
203 && SQL_KEYWORDS
204 .iter()
205 .any(|keyword| keyword.eq_ignore_ascii_case(name))
206}
207
208fn needs_quoting(name: &str) -> bool {
210 if name.is_empty() {
211 return true;
212 }
213 let first = name.as_bytes()[0];
214 if !(first.is_ascii_alphabetic() || first == b'_') {
215 return true;
216 }
217 name.bytes()
218 .any(|b| !(b.is_ascii_alphanumeric() || b == b'_'))
219 || is_sql_keyword(name)
220}
221
222pub fn write_ident(f: &mut fmt::Formatter<'_>, name: &str) -> fmt::Result {
223 if needs_quoting(name) {
224 write!(f, "\"{}\"", name.replace('"', "\"\""))
225 } else {
226 f.write_str(name)
227 }
228}
229
230#[must_use]
236pub fn quote_ident_if_needed(name: &str) -> String {
237 if needs_quoting(name) {
238 format!("\"{}\"", name.replace('"', "\"\""))
239 } else {
240 name.to_owned()
241 }
242}
243
244pub fn write_qualified_name(
245 f: &mut fmt::Formatter<'_>,
246 name: &crate::QualifiedName,
247) -> fmt::Result {
248 if let Some(ref schema) = name.schema {
249 write_ident(f, schema)?;
250 f.write_str(".")?;
251 }
252 write_ident(f, &name.name)
253}
254
255const PREC_OR: u8 = 1;
256const PREC_AND: u8 = 3;
257const PREC_NOT: u8 = 5;
258const PREC_EQUALITY: u8 = 7;
259const PREC_COMPARISON: u8 = 9;
260const PREC_ESCAPE: u8 = 11;
261const PREC_BITWISE: u8 = 13;
262const PREC_ADD: u8 = 15;
263const PREC_MULTIPLY: u8 = 17;
264const PREC_CONCAT: u8 = 19;
265const PREC_COLLATE: u8 = 21;
266const PREC_UNARY: u8 = 23;
267const PREC_ATOM: u8 = u8::MAX;
268
269#[derive(Clone, Copy)]
270enum ExprParent {
271 Binary(BinaryOp),
272 Unary(UnaryOp),
273 Between,
274 In,
275 Like,
276 Escape,
277 Collate,
278 IsNull,
279 Json,
280}
281
282#[derive(Clone, Copy, PartialEq, Eq)]
283enum OperandSide {
284 Left,
285 Right,
286 Prefix,
287}
288
289fn binary_precedence(op: BinaryOp) -> u8 {
290 match op {
291 BinaryOp::Or => PREC_OR,
292 BinaryOp::And => PREC_AND,
293 BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Is | BinaryOp::IsNot => PREC_EQUALITY,
294 BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge => PREC_COMPARISON,
295 BinaryOp::BitAnd | BinaryOp::BitOr | BinaryOp::ShiftLeft | BinaryOp::ShiftRight => {
296 PREC_BITWISE
297 }
298 BinaryOp::Add | BinaryOp::Subtract => PREC_ADD,
299 BinaryOp::Multiply | BinaryOp::Divide | BinaryOp::Modulo => PREC_MULTIPLY,
300 BinaryOp::Concat => PREC_CONCAT,
301 }
302}
303
304fn parent_precedence(parent: ExprParent) -> u8 {
305 match parent {
306 ExprParent::Binary(op) => binary_precedence(op),
307 ExprParent::Unary(UnaryOp::Not) => PREC_NOT,
308 ExprParent::Unary(_) => PREC_UNARY,
309 ExprParent::Between | ExprParent::In | ExprParent::Like | ExprParent::IsNull => {
310 PREC_EQUALITY
311 }
312 ExprParent::Escape => PREC_ESCAPE,
313 ExprParent::Collate => PREC_COLLATE,
314 ExprParent::Json => PREC_CONCAT,
315 }
316}
317
318fn expr_precedence(expr: &Expr) -> u8 {
319 match expr {
320 Expr::BinaryOp { op, .. } => binary_precedence(*op),
321 Expr::UnaryOp {
322 op: UnaryOp::Not, ..
323 }
324 | Expr::Exists { not: true, .. } => PREC_NOT,
325 Expr::UnaryOp { .. } => PREC_UNARY,
326 Expr::Literal(Literal::Integer(value), _) if *value < 0 => PREC_UNARY,
327 Expr::Literal(Literal::Float(value), _) if !value.is_nan() && value.is_sign_negative() => {
328 PREC_UNARY
329 }
330 Expr::BoundOuterValue {
331 value: SqliteValue::Integer(value),
332 ..
333 } if *value < 0 => PREC_UNARY,
334 Expr::BoundOuterValue {
335 value: SqliteValue::Float(value),
336 ..
337 } if !value.is_nan() && value.is_sign_negative() => PREC_UNARY,
338 Expr::Between { .. } | Expr::In { .. } | Expr::Like { .. } | Expr::IsNull { .. } => {
339 PREC_EQUALITY
340 }
341 Expr::JsonAccess { .. } => PREC_CONCAT,
342 Expr::Collate { .. } => PREC_COLLATE,
343 Expr::Literal(..)
344 | Expr::BoundOuterValue { .. }
345 | Expr::Column(..)
346 | Expr::Case { .. }
347 | Expr::Cast { .. }
348 | Expr::Exists { .. }
349 | Expr::Subquery(..)
350 | Expr::FunctionCall { .. }
351 | Expr::Raise { .. }
352 | Expr::RowValue(..)
353 | Expr::Placeholder(..) => PREC_ATOM,
354 }
355}
356
357fn operand_needs_parentheses(expr: &Expr, parent: ExprParent, side: OperandSide) -> bool {
364 let child_precedence = expr_precedence(expr);
365 let parent_precedence = parent_precedence(parent);
366 if child_precedence != parent_precedence {
367 return child_precedence < parent_precedence;
368 }
369 if side == OperandSide::Left {
370 return false;
371 }
372 if side == OperandSide::Right {
373 return !matches!(
374 (parent, expr),
375 (
376 ExprParent::Binary(BinaryOp::And),
377 Expr::BinaryOp {
378 op: BinaryOp::And,
379 ..
380 }
381 ) | (
382 ExprParent::Binary(BinaryOp::Or),
383 Expr::BinaryOp {
384 op: BinaryOp::Or,
385 ..
386 }
387 )
388 );
389 }
390 true
391}
392
393enum ExprWriteTask<'a> {
394 Expr(&'a Expr),
395 Operand {
396 expr: &'a Expr,
397 parent: ExprParent,
398 side: OperandSide,
399 },
400 Statement(&'a Statement),
401 Select(&'a SelectStatement),
402 SelectBody(&'a SelectBody),
403 With(&'a WithClause),
404 Cte(&'a Cte),
405 SelectCore(&'a SelectCore),
406 ResultColumn(&'a ResultColumn),
407 From(&'a FromClause),
408 Table(&'a TableOrSubquery),
409 Join(&'a JoinClause),
410 JoinConstraint(&'a JoinConstraint),
411 WindowDef(&'a WindowDef),
412 Limit(&'a LimitClause),
413 Update(&'a UpdateStatement),
414 CreateTrigger(&'a CreateTriggerStatement),
415 Text(&'static str),
416 Ident(&'a str),
417 Literal(&'a Literal),
418 BoundOuterValue(&'a SqliteValue),
419 Column(&'a ColumnRef),
420 BinaryOp(&'a BinaryOp),
421 CompoundOp(&'a CompoundOp),
422 JoinType(&'a JoinType),
423 UnaryOp(&'a UnaryOp),
424 LikeOp(&'a LikeOp),
425 TypeName(&'a TypeName),
426 Placeholder(&'a PlaceholderType),
427 QualifiedName(&'a QualifiedName),
428 ParenthesizedSelect(&'a SelectStatement),
429 OrderingTerm(&'a OrderingTerm),
430 Window(&'a WindowSpec),
431 Frame(&'a FrameSpec),
432 FrameBound(&'a FrameBound),
433}
434
435const INLINE_EXPR_WRITE_TASKS: usize = 32;
436
437struct ExprWriteTaskStack<'a> {
438 tasks: SmallVec<[ExprWriteTask<'a>; INLINE_EXPR_WRITE_TASKS]>,
439 #[cfg(test)]
440 peak_len: usize,
441 #[cfg(test)]
442 ever_spilled: bool,
443}
444
445impl<'a> ExprWriteTaskStack<'a> {
446 fn new(task: ExprWriteTask<'a>) -> Self {
447 let mut tasks = SmallVec::new();
448 tasks.push(task);
449 Self {
450 tasks,
451 #[cfg(test)]
452 peak_len: 1,
453 #[cfg(test)]
454 ever_spilled: false,
455 }
456 }
457
458 fn push(&mut self, task: ExprWriteTask<'a>) {
459 self.tasks.push(task);
460 #[cfg(test)]
461 {
462 self.peak_len = self.peak_len.max(self.tasks.len());
463 self.ever_spilled |= self.tasks.spilled();
464 }
465 }
466
467 fn pop(&mut self) -> Option<ExprWriteTask<'a>> {
468 self.tasks.pop()
469 }
470
471 #[cfg(test)]
472 fn stats(&self) -> ExprWriteTaskStackStats {
473 ExprWriteTaskStackStats {
474 peak_len: self.peak_len,
475 spilled: self.ever_spilled,
476 }
477 }
478}
479
480#[cfg(test)]
481#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
482struct ExprWriteTaskStackStats {
483 peak_len: usize,
484 spilled: bool,
485}
486
487#[cfg(test)]
488std::thread_local! {
489 static LAST_EXPR_WRITE_TASK_STACK_STATS: std::cell::Cell<ExprWriteTaskStackStats> =
490 const { std::cell::Cell::new(ExprWriteTaskStackStats { peak_len: 0, spilled: false }) };
491}
492
493fn push_comma_separated_exprs<'a>(tasks: &mut ExprWriteTaskStack<'a>, exprs: &'a [Expr]) {
494 for (index, expr) in exprs.iter().enumerate().rev() {
495 tasks.push(ExprWriteTask::Expr(expr));
496 if index > 0 {
497 tasks.push(ExprWriteTask::Text(", "));
498 }
499 }
500}
501
502fn push_comma_separated_ordering_terms<'a>(
503 tasks: &mut ExprWriteTaskStack<'a>,
504 terms: &'a [OrderingTerm],
505) {
506 for (index, term) in terms.iter().enumerate().rev() {
507 tasks.push(ExprWriteTask::OrderingTerm(term));
508 if index > 0 {
509 tasks.push(ExprWriteTask::Text(", "));
510 }
511 }
512}
513
514fn push_comma_separated_result_columns<'a>(
515 tasks: &mut ExprWriteTaskStack<'a>,
516 columns: &'a [ResultColumn],
517) {
518 for (index, column) in columns.iter().enumerate().rev() {
519 tasks.push(ExprWriteTask::ResultColumn(column));
520 if index > 0 {
521 tasks.push(ExprWriteTask::Text(", "));
522 }
523 }
524}
525
526fn push_comma_separated_ctes<'a>(tasks: &mut ExprWriteTaskStack<'a>, ctes: &'a [Cte]) {
527 for (index, cte) in ctes.iter().enumerate().rev() {
528 tasks.push(ExprWriteTask::Cte(cte));
529 if index > 0 {
530 tasks.push(ExprWriteTask::Text(", "));
531 }
532 }
533}
534
535fn push_comma_separated_window_defs<'a>(
536 tasks: &mut ExprWriteTaskStack<'a>,
537 windows: &'a [WindowDef],
538) {
539 for (index, window) in windows.iter().enumerate().rev() {
540 tasks.push(ExprWriteTask::WindowDef(window));
541 if index > 0 {
542 tasks.push(ExprWriteTask::Text(", "));
543 }
544 }
545}
546
547#[allow(clippy::too_many_lines)]
548fn write_expr(f: &mut fmt::Formatter<'_>, root: &Expr) -> fmt::Result {
549 write_expr_tasks(f, ExprWriteTask::Expr(root))
550}
551
552fn write_select(f: &mut fmt::Formatter<'_>, root: &SelectStatement) -> fmt::Result {
553 write_expr_tasks(f, ExprWriteTask::Select(root))
554}
555
556fn write_select_body(f: &mut fmt::Formatter<'_>, root: &SelectBody) -> fmt::Result {
557 write_expr_tasks(f, ExprWriteTask::SelectBody(root))
558}
559
560fn write_select_core(f: &mut fmt::Formatter<'_>, root: &SelectCore) -> fmt::Result {
561 write_expr_tasks(f, ExprWriteTask::SelectCore(root))
562}
563
564fn write_from(f: &mut fmt::Formatter<'_>, root: &FromClause) -> fmt::Result {
565 write_expr_tasks(f, ExprWriteTask::From(root))
566}
567
568fn write_table(f: &mut fmt::Formatter<'_>, root: &TableOrSubquery) -> fmt::Result {
569 write_expr_tasks(f, ExprWriteTask::Table(root))
570}
571
572fn write_join(f: &mut fmt::Formatter<'_>, root: &JoinClause) -> fmt::Result {
573 write_expr_tasks(f, ExprWriteTask::Join(root))
574}
575
576fn write_update(f: &mut fmt::Formatter<'_>, root: &UpdateStatement) -> fmt::Result {
577 write_expr_tasks(f, ExprWriteTask::Update(root))
578}
579
580fn write_create_trigger(f: &mut fmt::Formatter<'_>, root: &CreateTriggerStatement) -> fmt::Result {
581 write_expr_tasks(f, ExprWriteTask::CreateTrigger(root))
582}
583
584fn write_statement(f: &mut fmt::Formatter<'_>, root: &Statement) -> fmt::Result {
585 write_expr_tasks(f, ExprWriteTask::Statement(root))
586}
587
588#[allow(clippy::too_many_lines)]
589fn write_expr_tasks(f: &mut fmt::Formatter<'_>, root: ExprWriteTask<'_>) -> fmt::Result {
590 #[cfg(test)]
591 LAST_EXPR_WRITE_TASK_STACK_STATS.set(ExprWriteTaskStackStats::default());
592 let mut tasks = ExprWriteTaskStack::new(root);
593 while let Some(task) = tasks.pop() {
594 match task {
595 ExprWriteTask::Text(text) => f.write_str(text)?,
596 ExprWriteTask::Ident(name) => write_ident(f, name)?,
597 ExprWriteTask::Literal(literal) => write!(f, "{literal}")?,
598 ExprWriteTask::BoundOuterValue(value) => write_sqlite_value_literal(f, value)?,
599 ExprWriteTask::Column(column) => write!(f, "{column}")?,
600 ExprWriteTask::BinaryOp(op) => write!(f, "{op}")?,
601 ExprWriteTask::CompoundOp(op) => write!(f, "{op}")?,
602 ExprWriteTask::JoinType(join_type) => write!(f, "{join_type}")?,
603 ExprWriteTask::UnaryOp(op) => write!(f, "{op}")?,
604 ExprWriteTask::LikeOp(op) => write!(f, "{op}")?,
605 ExprWriteTask::TypeName(type_name) => write!(f, "{type_name}")?,
606 ExprWriteTask::Placeholder(placeholder) => write!(f, "{placeholder}")?,
607 ExprWriteTask::QualifiedName(name) => write!(f, "{name}")?,
608 ExprWriteTask::ParenthesizedSelect(select) => {
609 tasks.push(ExprWriteTask::Text(")"));
610 tasks.push(ExprWriteTask::Select(select));
611 tasks.push(ExprWriteTask::Text("("));
612 }
613 ExprWriteTask::Operand { expr, parent, side } => {
614 if operand_needs_parentheses(expr, parent, side) {
615 tasks.push(ExprWriteTask::Text(")"));
616 tasks.push(ExprWriteTask::Expr(expr));
617 tasks.push(ExprWriteTask::Text("("));
618 } else {
619 tasks.push(ExprWriteTask::Expr(expr));
620 }
621 }
622 ExprWriteTask::Statement(statement) => match statement {
623 Statement::Select(select) => tasks.push(ExprWriteTask::Select(select)),
624 Statement::Update(update) => tasks.push(ExprWriteTask::Update(update)),
625 Statement::CreateTrigger(trigger) => {
626 tasks.push(ExprWriteTask::CreateTrigger(trigger));
627 }
628 Statement::Explain { query_plan, stmt } => {
629 tasks.push(ExprWriteTask::Statement(stmt));
630 if *query_plan {
631 tasks.push(ExprWriteTask::Text("EXPLAIN QUERY PLAN "));
632 } else {
633 tasks.push(ExprWriteTask::Text("EXPLAIN "));
634 }
635 }
636 Statement::Insert(insert) => write!(f, "{insert}")?,
637 Statement::Delete(delete) => write!(f, "{delete}")?,
638 Statement::CreateTable(create) => write!(f, "{create}")?,
639 Statement::CreateIndex(create) => write!(f, "{create}")?,
640 Statement::CreateView(create) => write!(f, "{create}")?,
641 Statement::CreateVirtualTable(create) => write!(f, "{create}")?,
642 Statement::Drop(drop) => write!(f, "{drop}")?,
643 Statement::AlterTable(alter) => write!(f, "{alter}")?,
644 Statement::Begin(begin) => write!(f, "{begin}")?,
645 Statement::Commit => f.write_str("COMMIT")?,
646 Statement::Rollback(rollback) => write!(f, "{rollback}")?,
647 Statement::Savepoint(name) => {
648 f.write_str("SAVEPOINT ")?;
649 write_ident(f, name)?;
650 }
651 Statement::Release(name) => {
652 f.write_str("RELEASE ")?;
653 write_ident(f, name)?;
654 }
655 Statement::Attach(attach) => write!(f, "{attach}")?,
656 Statement::Detach(schema) => {
657 f.write_str("DETACH ")?;
658 write_ident(f, schema)?;
659 }
660 Statement::Pragma(pragma) => write!(f, "{pragma}")?,
661 Statement::Vacuum(vacuum) => write!(f, "{vacuum}")?,
662 Statement::Reindex(None) => f.write_str("REINDEX")?,
663 Statement::Reindex(Some(name)) => write!(f, "REINDEX {name}")?,
664 Statement::Analyze(None) => f.write_str("ANALYZE")?,
665 Statement::Analyze(Some(name)) => write!(f, "ANALYZE {name}")?,
666 },
667 ExprWriteTask::Select(select) => {
668 if let Some(limit) = &select.limit {
669 tasks.push(ExprWriteTask::Limit(limit));
670 tasks.push(ExprWriteTask::Text(" "));
671 }
672 if !select.order_by.is_empty() {
673 push_comma_separated_ordering_terms(&mut tasks, &select.order_by);
674 tasks.push(ExprWriteTask::Text(" ORDER BY "));
675 }
676 for (op, core) in select.body.compounds.iter().rev() {
677 tasks.push(ExprWriteTask::SelectCore(core));
678 tasks.push(ExprWriteTask::Text(" "));
679 tasks.push(ExprWriteTask::CompoundOp(op));
680 tasks.push(ExprWriteTask::Text(" "));
681 }
682 tasks.push(ExprWriteTask::SelectCore(&select.body.select));
683 if let Some(with) = &select.with {
684 tasks.push(ExprWriteTask::Text(" "));
685 tasks.push(ExprWriteTask::With(with));
686 }
687 }
688 ExprWriteTask::SelectBody(body) => {
689 for (op, core) in body.compounds.iter().rev() {
690 tasks.push(ExprWriteTask::SelectCore(core));
691 tasks.push(ExprWriteTask::Text(" "));
692 tasks.push(ExprWriteTask::CompoundOp(op));
693 tasks.push(ExprWriteTask::Text(" "));
694 }
695 tasks.push(ExprWriteTask::SelectCore(&body.select));
696 }
697 ExprWriteTask::With(with) => {
698 push_comma_separated_ctes(&mut tasks, &with.ctes);
699 if with.recursive {
700 tasks.push(ExprWriteTask::Text("WITH RECURSIVE "));
701 } else {
702 tasks.push(ExprWriteTask::Text("WITH "));
703 }
704 }
705 ExprWriteTask::Cte(cte) => {
706 tasks.push(ExprWriteTask::Text(")"));
707 tasks.push(ExprWriteTask::Select(&cte.query));
708 tasks.push(ExprWriteTask::Text("("));
709 match cte.materialized {
710 Some(CteMaterialized::Materialized) => {
711 tasks.push(ExprWriteTask::Text("MATERIALIZED "));
712 }
713 Some(CteMaterialized::NotMaterialized) => {
714 tasks.push(ExprWriteTask::Text("NOT MATERIALIZED "));
715 }
716 None => {}
717 }
718 tasks.push(ExprWriteTask::Text(" AS "));
719 if !cte.columns.is_empty() {
720 tasks.push(ExprWriteTask::Text(")"));
721 for (index, column) in cte.columns.iter().enumerate().rev() {
722 tasks.push(ExprWriteTask::Ident(column));
723 if index > 0 {
724 tasks.push(ExprWriteTask::Text(", "));
725 }
726 }
727 tasks.push(ExprWriteTask::Text("("));
728 }
729 tasks.push(ExprWriteTask::Ident(&cte.name));
730 }
731 ExprWriteTask::SelectCore(core) => match core {
732 SelectCore::Select {
733 distinct,
734 columns,
735 from,
736 where_clause,
737 group_by,
738 having,
739 windows,
740 } => {
741 if !windows.is_empty() {
742 push_comma_separated_window_defs(&mut tasks, windows);
743 tasks.push(ExprWriteTask::Text(" WINDOW "));
744 }
745 if let Some(having) = having {
746 tasks.push(ExprWriteTask::Expr(having));
747 tasks.push(ExprWriteTask::Text(" HAVING "));
748 }
749 if !group_by.is_empty() {
750 push_comma_separated_exprs(&mut tasks, group_by);
751 tasks.push(ExprWriteTask::Text(" GROUP BY "));
752 }
753 if let Some(where_clause) = where_clause {
754 tasks.push(ExprWriteTask::Expr(where_clause));
755 tasks.push(ExprWriteTask::Text(" WHERE "));
756 }
757 if let Some(from) = from {
758 tasks.push(ExprWriteTask::From(from));
759 tasks.push(ExprWriteTask::Text(" FROM "));
760 }
761 push_comma_separated_result_columns(&mut tasks, columns);
762 if *distinct == Distinctness::Distinct {
763 tasks.push(ExprWriteTask::Text("SELECT DISTINCT "));
764 } else {
765 tasks.push(ExprWriteTask::Text("SELECT "));
766 }
767 }
768 SelectCore::Values(rows) => {
769 for (row_index, row) in rows.iter().enumerate().rev() {
770 tasks.push(ExprWriteTask::Text(")"));
771 push_comma_separated_exprs(&mut tasks, row);
772 tasks.push(ExprWriteTask::Text("("));
773 if row_index > 0 {
774 tasks.push(ExprWriteTask::Text(", "));
775 }
776 }
777 tasks.push(ExprWriteTask::Text("VALUES "));
778 }
779 },
780 ExprWriteTask::ResultColumn(column) => match column {
781 ResultColumn::Star => tasks.push(ExprWriteTask::Text("*")),
782 ResultColumn::TableStar(name) => {
783 tasks.push(ExprWriteTask::Text(".*"));
784 tasks.push(ExprWriteTask::QualifiedName(name));
785 }
786 ResultColumn::Expr { expr, alias } => {
787 if let Some(alias) = alias {
788 tasks.push(ExprWriteTask::Ident(alias));
789 tasks.push(ExprWriteTask::Text(" AS "));
790 }
791 tasks.push(ExprWriteTask::Expr(expr));
792 }
793 },
794 ExprWriteTask::From(from) => {
795 for join in from.joins.iter().rev() {
796 tasks.push(ExprWriteTask::Join(join));
797 tasks.push(ExprWriteTask::Text(" "));
798 }
799 tasks.push(ExprWriteTask::Table(&from.source));
800 }
801 ExprWriteTask::Table(table) => match table {
802 TableOrSubquery::Table {
803 name,
804 alias,
805 index_hint,
806 time_travel,
807 } => {
808 if let Some(time_travel) = time_travel {
809 write!(f, "{name}")?;
810 if let Some(alias) = alias {
811 f.write_str(" AS ")?;
812 write_ident(f, alias)?;
813 }
814 if let Some(index_hint) = index_hint {
815 write!(f, " {index_hint}")?;
816 }
817 write!(f, " {time_travel}")?;
818 } else {
819 if let Some(index_hint) = index_hint {
820 match index_hint {
821 IndexHint::IndexedBy(name) => {
822 tasks.push(ExprWriteTask::Ident(name));
823 tasks.push(ExprWriteTask::Text(" INDEXED BY "));
824 }
825 IndexHint::NotIndexed => {
826 tasks.push(ExprWriteTask::Text(" NOT INDEXED"));
827 }
828 }
829 }
830 if let Some(alias) = alias {
831 tasks.push(ExprWriteTask::Ident(alias));
832 tasks.push(ExprWriteTask::Text(" AS "));
833 }
834 tasks.push(ExprWriteTask::QualifiedName(name));
835 }
836 }
837 TableOrSubquery::Subquery { query, alias } => {
838 if let Some(alias) = alias {
839 tasks.push(ExprWriteTask::Ident(alias));
840 tasks.push(ExprWriteTask::Text(" AS "));
841 }
842 tasks.push(ExprWriteTask::Text(")"));
843 tasks.push(ExprWriteTask::Select(query));
844 tasks.push(ExprWriteTask::Text("("));
845 }
846 TableOrSubquery::TableFunction { name, args, alias } => {
847 if let Some(alias) = alias {
848 tasks.push(ExprWriteTask::Ident(alias));
849 tasks.push(ExprWriteTask::Text(" AS "));
850 }
851 tasks.push(ExprWriteTask::Text(")"));
852 push_comma_separated_exprs(&mut tasks, args);
853 tasks.push(ExprWriteTask::Text("("));
854 tasks.push(ExprWriteTask::Ident(name));
855 }
856 TableOrSubquery::ParenJoin(inner) => {
857 tasks.push(ExprWriteTask::Text(")"));
858 tasks.push(ExprWriteTask::From(inner));
859 tasks.push(ExprWriteTask::Text("("));
860 }
861 },
862 ExprWriteTask::Join(join) => {
863 if let Some(constraint) = &join.constraint {
864 tasks.push(ExprWriteTask::JoinConstraint(constraint));
865 tasks.push(ExprWriteTask::Text(" "));
866 }
867 tasks.push(ExprWriteTask::Table(&join.table));
868 tasks.push(ExprWriteTask::Text(" "));
869 tasks.push(ExprWriteTask::JoinType(&join.join_type));
870 }
871 ExprWriteTask::JoinConstraint(constraint) => match constraint {
872 JoinConstraint::On(expr) => {
873 tasks.push(ExprWriteTask::Expr(expr));
874 tasks.push(ExprWriteTask::Text("ON "));
875 }
876 JoinConstraint::Using(columns) => {
877 tasks.push(ExprWriteTask::Text(")"));
878 for (index, column) in columns.iter().enumerate().rev() {
879 tasks.push(ExprWriteTask::Ident(column));
880 if index > 0 {
881 tasks.push(ExprWriteTask::Text(", "));
882 }
883 }
884 tasks.push(ExprWriteTask::Text("USING ("));
885 }
886 },
887 ExprWriteTask::WindowDef(window) => {
888 tasks.push(ExprWriteTask::Window(&window.spec));
889 tasks.push(ExprWriteTask::Text(" AS "));
890 tasks.push(ExprWriteTask::Ident(&window.name));
891 }
892 ExprWriteTask::Limit(limit) => {
893 if let Some(offset) = &limit.offset {
894 tasks.push(ExprWriteTask::Expr(offset));
895 tasks.push(ExprWriteTask::Text(" OFFSET "));
896 }
897 tasks.push(ExprWriteTask::Expr(&limit.limit));
898 tasks.push(ExprWriteTask::Text("LIMIT "));
899 }
900 ExprWriteTask::Update(update) => {
901 if let Some(limit) = &update.limit {
902 tasks.push(ExprWriteTask::Limit(limit));
903 tasks.push(ExprWriteTask::Text(" "));
904 }
905 if !update.order_by.is_empty() {
906 push_comma_separated_ordering_terms(&mut tasks, &update.order_by);
907 tasks.push(ExprWriteTask::Text(" ORDER BY "));
908 }
909 if !update.returning.is_empty() {
910 push_comma_separated_result_columns(&mut tasks, &update.returning);
911 tasks.push(ExprWriteTask::Text(" RETURNING "));
912 }
913 if let Some(where_clause) = &update.where_clause {
914 tasks.push(ExprWriteTask::Expr(where_clause));
915 tasks.push(ExprWriteTask::Text(" WHERE "));
916 }
917 if let Some(from) = &update.from {
918 tasks.push(ExprWriteTask::From(from));
919 tasks.push(ExprWriteTask::Text(" FROM "));
920 }
921
922 if let Some(with) = &update.with {
923 write!(f, "{with} ")?;
924 }
925 f.write_str("UPDATE")?;
926 if let Some(action) = &update.or_conflict {
927 write!(f, " OR {action}")?;
928 }
929 write!(f, " {} SET ", update.table)?;
930 comma_list(f, &update.assignments)?;
931 }
932 ExprWriteTask::CreateTrigger(trigger) => {
933 tasks.push(ExprWriteTask::Text("END"));
934 for statement in trigger.body.iter().rev() {
935 tasks.push(ExprWriteTask::Text("; "));
936 tasks.push(ExprWriteTask::Statement(statement));
937 }
938
939 f.write_str("CREATE ")?;
940 if trigger.temporary {
941 f.write_str("TEMP ")?;
942 }
943 f.write_str("TRIGGER ")?;
944 if trigger.if_not_exists {
945 f.write_str("IF NOT EXISTS ")?;
946 }
947 write!(
948 f,
949 "{} {} {} ON ",
950 trigger.name, trigger.timing, trigger.event
951 )?;
952 write_ident(f, &trigger.table)?;
953 if trigger.for_each_row {
954 f.write_str(" FOR EACH ROW")?;
955 }
956 if let Some(when) = &trigger.when {
957 write!(f, " WHEN {when}")?;
958 }
959 f.write_str(" BEGIN ")?;
960 }
961 ExprWriteTask::OrderingTerm(term) => {
962 if let Some(nulls) = term.nulls {
963 match nulls {
964 NullsOrder::First => tasks.push(ExprWriteTask::Text(" NULLS FIRST")),
965 NullsOrder::Last => tasks.push(ExprWriteTask::Text(" NULLS LAST")),
966 }
967 }
968 if let Some(direction) = term.direction {
969 match direction {
970 SortDirection::Asc => tasks.push(ExprWriteTask::Text(" ASC")),
971 SortDirection::Desc => tasks.push(ExprWriteTask::Text(" DESC")),
972 }
973 }
974 tasks.push(ExprWriteTask::Expr(&term.expr));
975 }
976 ExprWriteTask::Window(window) => {
977 let has_base = window.window_ref.is_some();
978 let has_partition = !window.partition_by.is_empty();
979 let has_order = !window.order_by.is_empty();
980 tasks.push(ExprWriteTask::Text(")"));
981 if let Some(frame) = &window.frame {
982 tasks.push(ExprWriteTask::Frame(frame));
983 if has_base || has_partition || has_order {
984 tasks.push(ExprWriteTask::Text(" "));
985 }
986 }
987 if has_order {
988 push_comma_separated_ordering_terms(&mut tasks, &window.order_by);
989 tasks.push(ExprWriteTask::Text("ORDER BY "));
990 if has_base || has_partition {
991 tasks.push(ExprWriteTask::Text(" "));
992 }
993 }
994 if has_partition {
995 push_comma_separated_exprs(&mut tasks, &window.partition_by);
996 tasks.push(ExprWriteTask::Text("PARTITION BY "));
997 if has_base {
998 tasks.push(ExprWriteTask::Text(" "));
999 }
1000 }
1001 if let Some(window_ref) = &window.window_ref {
1002 tasks.push(ExprWriteTask::Ident(window_ref.name()));
1003 }
1004 tasks.push(ExprWriteTask::Text("("));
1005 }
1006 ExprWriteTask::Frame(frame) => {
1007 if let Some(exclude) = frame.exclude {
1008 match exclude {
1009 FrameExclude::NoOthers => {
1010 tasks.push(ExprWriteTask::Text(" EXCLUDE NO OTHERS"));
1011 }
1012 FrameExclude::CurrentRow => {
1013 tasks.push(ExprWriteTask::Text(" EXCLUDE CURRENT ROW"));
1014 }
1015 FrameExclude::Group => {
1016 tasks.push(ExprWriteTask::Text(" EXCLUDE GROUP"));
1017 }
1018 FrameExclude::Ties => {
1019 tasks.push(ExprWriteTask::Text(" EXCLUDE TIES"));
1020 }
1021 }
1022 }
1023 if let Some(end) = &frame.end {
1024 tasks.push(ExprWriteTask::FrameBound(end));
1025 tasks.push(ExprWriteTask::Text(" AND "));
1026 tasks.push(ExprWriteTask::FrameBound(&frame.start));
1027 tasks.push(ExprWriteTask::Text(" BETWEEN "));
1028 } else {
1029 tasks.push(ExprWriteTask::FrameBound(&frame.start));
1030 tasks.push(ExprWriteTask::Text(" "));
1031 }
1032 match frame.frame_type {
1033 FrameType::Rows => tasks.push(ExprWriteTask::Text("ROWS")),
1034 FrameType::Range => tasks.push(ExprWriteTask::Text("RANGE")),
1035 FrameType::Groups => tasks.push(ExprWriteTask::Text("GROUPS")),
1036 }
1037 }
1038 ExprWriteTask::FrameBound(bound) => match bound {
1039 FrameBound::UnboundedPreceding => {
1040 tasks.push(ExprWriteTask::Text("UNBOUNDED PRECEDING"));
1041 }
1042 FrameBound::Preceding(expr) => {
1043 tasks.push(ExprWriteTask::Text(" PRECEDING"));
1044 tasks.push(ExprWriteTask::Expr(expr));
1045 }
1046 FrameBound::CurrentRow => tasks.push(ExprWriteTask::Text("CURRENT ROW")),
1047 FrameBound::Following(expr) => {
1048 tasks.push(ExprWriteTask::Text(" FOLLOWING"));
1049 tasks.push(ExprWriteTask::Expr(expr));
1050 }
1051 FrameBound::UnboundedFollowing => {
1052 tasks.push(ExprWriteTask::Text("UNBOUNDED FOLLOWING"));
1053 }
1054 },
1055 ExprWriteTask::Expr(expr) => match expr {
1056 Expr::Literal(literal, _) => tasks.push(ExprWriteTask::Literal(literal)),
1057 Expr::BoundOuterValue { value, .. } => {
1058 tasks.push(ExprWriteTask::BoundOuterValue(value));
1059 }
1060 Expr::Column(column, _) => tasks.push(ExprWriteTask::Column(column)),
1061 Expr::BinaryOp {
1062 left, op, right, ..
1063 } => {
1064 tasks.push(ExprWriteTask::Operand {
1065 expr: right,
1066 parent: ExprParent::Binary(*op),
1067 side: OperandSide::Right,
1068 });
1069 tasks.push(ExprWriteTask::Text(" "));
1070 tasks.push(ExprWriteTask::BinaryOp(op));
1071 tasks.push(ExprWriteTask::Text(" "));
1072 tasks.push(ExprWriteTask::Operand {
1073 expr: left,
1074 parent: ExprParent::Binary(*op),
1075 side: OperandSide::Left,
1076 });
1077 }
1078 Expr::UnaryOp { op, expr, .. } => {
1079 tasks.push(ExprWriteTask::Operand {
1080 expr,
1081 parent: ExprParent::Unary(*op),
1082 side: OperandSide::Prefix,
1083 });
1084 if matches!(op, UnaryOp::Not) {
1085 tasks.push(ExprWriteTask::Text("NOT "));
1086 } else {
1087 tasks.push(ExprWriteTask::UnaryOp(op));
1088 }
1089 }
1090 Expr::Between {
1091 expr,
1092 low,
1093 high,
1094 not,
1095 ..
1096 } => {
1097 tasks.push(ExprWriteTask::Operand {
1098 expr: high,
1099 parent: ExprParent::Between,
1100 side: OperandSide::Right,
1101 });
1102 tasks.push(ExprWriteTask::Text(" AND "));
1103 tasks.push(ExprWriteTask::Operand {
1104 expr: low,
1105 parent: ExprParent::Between,
1106 side: OperandSide::Right,
1107 });
1108 tasks.push(ExprWriteTask::Text(" BETWEEN "));
1109 if *not {
1110 tasks.push(ExprWriteTask::Text(" NOT"));
1111 }
1112 tasks.push(ExprWriteTask::Operand {
1113 expr,
1114 parent: ExprParent::Between,
1115 side: OperandSide::Left,
1116 });
1117 }
1118 Expr::In { expr, set, not, .. } => {
1119 match set {
1120 InSet::List(items) => {
1121 tasks.push(ExprWriteTask::Text(")"));
1122 push_comma_separated_exprs(&mut tasks, items);
1123 tasks.push(ExprWriteTask::Text("("));
1124 }
1125 InSet::Subquery(select) => {
1126 tasks.push(ExprWriteTask::ParenthesizedSelect(select));
1127 }
1128 InSet::Table(name) => tasks.push(ExprWriteTask::QualifiedName(name)),
1129 }
1130 tasks.push(ExprWriteTask::Text(" IN "));
1131 if *not {
1132 tasks.push(ExprWriteTask::Text(" NOT"));
1133 }
1134 tasks.push(ExprWriteTask::Operand {
1135 expr,
1136 parent: ExprParent::In,
1137 side: OperandSide::Left,
1138 });
1139 }
1140 Expr::Like {
1141 expr,
1142 pattern,
1143 escape,
1144 op,
1145 not,
1146 ..
1147 } => {
1148 if let Some(escape) = escape {
1149 tasks.push(ExprWriteTask::Operand {
1150 expr: escape,
1151 parent: ExprParent::Escape,
1152 side: OperandSide::Right,
1153 });
1154 tasks.push(ExprWriteTask::Text(" ESCAPE "));
1155 }
1156 tasks.push(ExprWriteTask::Operand {
1157 expr: pattern,
1158 parent: ExprParent::Like,
1159 side: OperandSide::Right,
1160 });
1161 tasks.push(ExprWriteTask::Text(" "));
1162 tasks.push(ExprWriteTask::LikeOp(op));
1163 tasks.push(ExprWriteTask::Text(" "));
1164 if *not {
1165 tasks.push(ExprWriteTask::Text(" NOT"));
1166 }
1167 tasks.push(ExprWriteTask::Operand {
1168 expr,
1169 parent: ExprParent::Like,
1170 side: OperandSide::Left,
1171 });
1172 }
1173 Expr::Case {
1174 operand,
1175 whens,
1176 else_expr,
1177 ..
1178 } => {
1179 tasks.push(ExprWriteTask::Text(" END"));
1180 if let Some(else_expr) = else_expr {
1181 tasks.push(ExprWriteTask::Expr(else_expr));
1182 tasks.push(ExprWriteTask::Text(" ELSE "));
1183 }
1184 for (condition, result) in whens.iter().rev() {
1185 tasks.push(ExprWriteTask::Expr(result));
1186 tasks.push(ExprWriteTask::Text(" THEN "));
1187 tasks.push(ExprWriteTask::Expr(condition));
1188 tasks.push(ExprWriteTask::Text(" WHEN "));
1189 }
1190 if let Some(operand) = operand {
1191 tasks.push(ExprWriteTask::Expr(operand));
1192 tasks.push(ExprWriteTask::Text(" "));
1193 }
1194 tasks.push(ExprWriteTask::Text("CASE"));
1195 }
1196 Expr::Cast {
1197 expr, type_name, ..
1198 } => {
1199 tasks.push(ExprWriteTask::Text(")"));
1200 tasks.push(ExprWriteTask::TypeName(type_name));
1201 tasks.push(ExprWriteTask::Text(" AS "));
1202 tasks.push(ExprWriteTask::Expr(expr));
1203 tasks.push(ExprWriteTask::Text("CAST("));
1204 }
1205 Expr::Exists { subquery, not, .. } => {
1206 tasks.push(ExprWriteTask::Text(")"));
1207 tasks.push(ExprWriteTask::Select(subquery));
1208 if *not {
1209 tasks.push(ExprWriteTask::Text("NOT EXISTS ("));
1210 } else {
1211 tasks.push(ExprWriteTask::Text("EXISTS ("));
1212 }
1213 }
1214 Expr::Subquery(select, _) => {
1215 tasks.push(ExprWriteTask::Text(")"));
1216 tasks.push(ExprWriteTask::Select(select));
1217 tasks.push(ExprWriteTask::Text("("));
1218 }
1219 Expr::FunctionCall {
1220 name,
1221 args,
1222 distinct,
1223 order_by,
1224 filter,
1225 over,
1226 ..
1227 } => {
1228 if let Some(window) = over {
1229 match &window.window_ref {
1230 Some(WindowReference::Direct(name))
1231 if window.partition_by.is_empty()
1232 && window.order_by.is_empty()
1233 && window.frame.is_none() =>
1234 {
1235 tasks.push(ExprWriteTask::Ident(name));
1236 }
1237 _ => tasks.push(ExprWriteTask::Window(window)),
1238 }
1239 tasks.push(ExprWriteTask::Text(" OVER "));
1240 }
1241 if let Some(filter) = filter {
1242 tasks.push(ExprWriteTask::Text(")"));
1243 tasks.push(ExprWriteTask::Expr(filter));
1244 tasks.push(ExprWriteTask::Text(" FILTER (WHERE "));
1245 }
1246 tasks.push(ExprWriteTask::Text(")"));
1247 if !order_by.is_empty() {
1248 push_comma_separated_ordering_terms(&mut tasks, order_by);
1249 tasks.push(ExprWriteTask::Text(" ORDER BY "));
1250 }
1251 match args {
1252 FunctionArgs::Star => tasks.push(ExprWriteTask::Text("*")),
1253 FunctionArgs::List(items) => {
1254 push_comma_separated_exprs(&mut tasks, items);
1255 }
1256 }
1257 if *distinct {
1258 tasks.push(ExprWriteTask::Text("DISTINCT "));
1259 }
1260 tasks.push(ExprWriteTask::Text("("));
1261 tasks.push(ExprWriteTask::Ident(name));
1262 }
1263 Expr::Collate {
1264 expr, collation, ..
1265 } => {
1266 tasks.push(ExprWriteTask::Ident(collation));
1267 tasks.push(ExprWriteTask::Text(" COLLATE "));
1268 tasks.push(ExprWriteTask::Operand {
1269 expr,
1270 parent: ExprParent::Collate,
1271 side: OperandSide::Left,
1272 });
1273 }
1274 Expr::IsNull { expr, not, .. } => {
1275 if *not {
1276 tasks.push(ExprWriteTask::Text(" IS NOT NULL"));
1277 } else {
1278 tasks.push(ExprWriteTask::Text(" IS NULL"));
1279 }
1280 tasks.push(ExprWriteTask::Operand {
1281 expr,
1282 parent: ExprParent::IsNull,
1283 side: OperandSide::Left,
1284 });
1285 }
1286 Expr::Raise {
1287 action, message, ..
1288 } => {
1289 write!(f, "RAISE({action}")?;
1290 if let Some(message) = message {
1291 write!(f, ", '{}'", message.replace('\'', "''"))?;
1292 }
1293 f.write_str(")")?;
1294 }
1295 Expr::JsonAccess {
1296 expr, path, arrow, ..
1297 } => {
1298 tasks.push(ExprWriteTask::Operand {
1299 expr: path,
1300 parent: ExprParent::Json,
1301 side: OperandSide::Right,
1302 });
1303 match arrow {
1304 JsonArrow::Arrow => tasks.push(ExprWriteTask::Text(" -> ")),
1305 JsonArrow::DoubleArrow => tasks.push(ExprWriteTask::Text(" ->> ")),
1306 }
1307 tasks.push(ExprWriteTask::Operand {
1308 expr,
1309 parent: ExprParent::Json,
1310 side: OperandSide::Left,
1311 });
1312 }
1313 Expr::RowValue(exprs, _) => {
1314 tasks.push(ExprWriteTask::Text(")"));
1315 push_comma_separated_exprs(&mut tasks, exprs);
1316 tasks.push(ExprWriteTask::Text("("));
1317 }
1318 Expr::Placeholder(placeholder, _) => {
1319 tasks.push(ExprWriteTask::Placeholder(placeholder));
1320 }
1321 },
1322 }
1323 }
1324 #[cfg(test)]
1325 LAST_EXPR_WRITE_TASK_STACK_STATS.set(tasks.stats());
1326 Ok(())
1327}
1328
1329fn write_sqlite_float_literal(f: &mut fmt::Formatter<'_>, value: f64) -> fmt::Result {
1334 if value.is_nan() {
1335 f.write_str("NULL")
1338 } else if value.is_infinite() {
1339 if value.is_sign_negative() {
1340 f.write_str("-9e999")
1341 } else {
1342 f.write_str("9e999")
1343 }
1344 } else if value.fract() == 0.0 {
1346 write!(f, "{value:.1}")
1347 } else {
1348 write!(f, "{value}")
1349 }
1350}
1351
1352fn write_sql_string_literal(f: &mut fmt::Formatter<'_>, value: &str) -> fmt::Result {
1353 write!(f, "'{}'", value.replace('\'', "''"))
1354}
1355
1356fn write_sql_blob_literal(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result {
1357 f.write_str("X'")?;
1358 for byte in bytes {
1359 write!(f, "{byte:02X}")?;
1360 }
1361 f.write_str("'")
1362}
1363
1364fn write_sqlite_value_literal(f: &mut fmt::Formatter<'_>, value: &SqliteValue) -> fmt::Result {
1365 match value {
1366 SqliteValue::Null => f.write_str("NULL"),
1367 SqliteValue::Integer(value) => write!(f, "{value}"),
1368 SqliteValue::Float(value) => write_sqlite_float_literal(f, *value),
1369 SqliteValue::Text(value) => write_sql_string_literal(f, value.as_str()),
1370 SqliteValue::Blob(value) => write_sql_blob_literal(f, value),
1371 }
1372}
1373
1374impl fmt::Display for Literal {
1375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1376 match self {
1377 Self::Integer(n) => write!(f, "{n}"),
1378 Self::Float(v) => write_sqlite_float_literal(f, *v),
1379 Self::String(s) => write_sql_string_literal(f, s),
1380 Self::Blob(bytes) => write_sql_blob_literal(f, bytes),
1381 Self::Null => f.write_str("NULL"),
1382 Self::True => f.write_str("TRUE"),
1383 Self::False => f.write_str("FALSE"),
1384 Self::CurrentTime => f.write_str("CURRENT_TIME"),
1385 Self::CurrentDate => f.write_str("CURRENT_DATE"),
1386 Self::CurrentTimestamp => f.write_str("CURRENT_TIMESTAMP"),
1387 }
1388 }
1389}
1390
1391impl fmt::Display for ColumnRef {
1396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1397 if let Some(ref s) = self.schema {
1398 write_ident(f, s)?;
1399 f.write_str(".")?;
1400 }
1401 if let Some(ref t) = self.table {
1402 write_ident(f, t)?;
1403 f.write_str(".")?;
1404 }
1405 write_ident(f, &self.column)
1406 }
1407}
1408
1409impl fmt::Display for TypeName {
1414 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1415 f.write_str(&self.name)?;
1416 match (&self.arg1, &self.arg2) {
1417 (Some(a1), Some(a2)) => write!(f, "({a1}, {a2})"),
1418 (Some(a1), None) => write!(f, "({a1})"),
1419 _ => Ok(()),
1420 }
1421 }
1422}
1423
1424impl fmt::Display for PlaceholderType {
1429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1430 match self {
1431 Self::Anonymous => f.write_str("?"),
1432 Self::Numbered(n) => write!(f, "?{n}"),
1433 Self::ColonNamed(s) => write!(f, ":{s}"),
1434 Self::AtNamed(s) => write!(f, "@{s}"),
1435 Self::DollarNamed(s) => write!(f, "${s}"),
1436 }
1437 }
1438}
1439
1440impl fmt::Display for LikeOp {
1445 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1446 f.write_str(match self {
1447 Self::Like => "LIKE",
1448 Self::Glob => "GLOB",
1449 Self::Match => "MATCH",
1450 Self::Regexp => "REGEXP",
1451 })
1452 }
1453}
1454
1455impl fmt::Display for RaiseAction {
1460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1461 f.write_str(match self {
1462 Self::Ignore => "IGNORE",
1463 Self::Rollback => "ROLLBACK",
1464 Self::Abort => "ABORT",
1465 Self::Fail => "FAIL",
1466 })
1467 }
1468}
1469
1470impl fmt::Display for Expr {
1475 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1476 write_expr(f, self)
1477 }
1478}
1479
1480impl fmt::Display for WindowSpec {
1485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1486 if let Some(WindowReference::Direct(name)) = &self.window_ref
1487 && self.partition_by.is_empty()
1488 && self.order_by.is_empty()
1489 && self.frame.is_none()
1490 {
1491 return write_ident(f, name);
1492 }
1493 f.write_str("(")?;
1494 let mut need_space = if let Some(window_ref) = &self.window_ref {
1495 write_ident(f, window_ref.name())?;
1496 true
1497 } else {
1498 false
1499 };
1500 if !self.partition_by.is_empty() {
1501 if need_space {
1502 f.write_str(" ")?;
1503 }
1504 f.write_str("PARTITION BY ")?;
1505 comma_list(f, &self.partition_by)?;
1506 need_space = true;
1507 }
1508 if !self.order_by.is_empty() {
1509 if need_space {
1510 f.write_str(" ")?;
1511 }
1512 f.write_str("ORDER BY ")?;
1513 comma_list(f, &self.order_by)?;
1514 need_space = true;
1515 }
1516 if let Some(frame) = &self.frame {
1517 if need_space {
1518 f.write_str(" ")?;
1519 }
1520 write!(f, "{frame}")?;
1521 }
1522 f.write_str(")")
1523 }
1524}
1525
1526impl fmt::Display for FrameSpec {
1531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1532 write!(f, "{}", self.frame_type)?;
1533 if let Some(ref end) = self.end {
1534 write!(f, " BETWEEN {} AND {end}", self.start)?;
1535 } else {
1536 write!(f, " {}", self.start)?;
1537 }
1538 if let Some(ref excl) = self.exclude {
1539 write!(f, " EXCLUDE {excl}")?;
1540 }
1541 Ok(())
1542 }
1543}
1544
1545impl fmt::Display for FrameType {
1546 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1547 f.write_str(match self {
1548 Self::Rows => "ROWS",
1549 Self::Range => "RANGE",
1550 Self::Groups => "GROUPS",
1551 })
1552 }
1553}
1554
1555impl fmt::Display for FrameBound {
1556 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1557 match self {
1558 Self::UnboundedPreceding => f.write_str("UNBOUNDED PRECEDING"),
1559 Self::Preceding(e) => write!(f, "{e} PRECEDING"),
1560 Self::CurrentRow => f.write_str("CURRENT ROW"),
1561 Self::Following(e) => write!(f, "{e} FOLLOWING"),
1562 Self::UnboundedFollowing => f.write_str("UNBOUNDED FOLLOWING"),
1563 }
1564 }
1565}
1566
1567impl fmt::Display for FrameExclude {
1568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1569 f.write_str(match self {
1570 Self::NoOthers => "NO OTHERS",
1571 Self::CurrentRow => "CURRENT ROW",
1572 Self::Group => "GROUP",
1573 Self::Ties => "TIES",
1574 })
1575 }
1576}
1577
1578impl fmt::Display for OrderingTerm {
1583 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1584 write!(f, "{}", self.expr)?;
1585 if let Some(dir) = self.direction {
1586 write!(f, " {dir}")?;
1587 }
1588 if let Some(nulls) = self.nulls {
1589 write!(f, " {nulls}")?;
1590 }
1591 Ok(())
1592 }
1593}
1594
1595impl fmt::Display for SortDirection {
1596 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1597 f.write_str(match self {
1598 Self::Asc => "ASC",
1599 Self::Desc => "DESC",
1600 })
1601 }
1602}
1603
1604impl fmt::Display for NullsOrder {
1605 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1606 f.write_str(match self {
1607 Self::First => "NULLS FIRST",
1608 Self::Last => "NULLS LAST",
1609 })
1610 }
1611}
1612
1613impl fmt::Display for ResultColumn {
1618 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1619 match self {
1620 Self::Star => f.write_str("*"),
1621 Self::TableStar(t) => {
1622 write_qualified_name(f, t)?;
1623 f.write_str(".*")
1624 }
1625 Self::Expr { expr, alias } => {
1626 write!(f, "{expr}")?;
1627 if let Some(a) = alias {
1628 f.write_str(" AS ")?;
1629 write_ident(f, a)?;
1630 }
1631 Ok(())
1632 }
1633 }
1634 }
1635}
1636
1637impl fmt::Display for SelectStatement {
1642 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1643 write_select(f, self)
1644 }
1645}
1646
1647impl fmt::Display for WithClause {
1652 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1653 f.write_str("WITH ")?;
1654 if self.recursive {
1655 f.write_str("RECURSIVE ")?;
1656 }
1657 comma_list(f, &self.ctes)
1658 }
1659}
1660
1661impl fmt::Display for Cte {
1662 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1663 write_ident(f, &self.name)?;
1664 if !self.columns.is_empty() {
1665 f.write_str("(")?;
1666 comma_list_fn(f, &self.columns, |col, f| write_ident(f, col))?;
1667 f.write_str(")")?;
1668 }
1669 f.write_str(" AS ")?;
1670 if let Some(mat) = self.materialized {
1671 write!(f, "{mat} ")?;
1672 }
1673 write!(f, "({})", self.query)
1674 }
1675}
1676
1677impl fmt::Display for CteMaterialized {
1678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1679 f.write_str(match self {
1680 Self::Materialized => "MATERIALIZED",
1681 Self::NotMaterialized => "NOT MATERIALIZED",
1682 })
1683 }
1684}
1685
1686impl fmt::Display for SelectBody {
1691 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1692 write_select_body(f, self)
1693 }
1694}
1695
1696impl fmt::Display for CompoundOp {
1697 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1698 f.write_str(match self {
1699 Self::Union => "UNION",
1700 Self::UnionAll => "UNION ALL",
1701 Self::Intersect => "INTERSECT",
1702 Self::Except => "EXCEPT",
1703 })
1704 }
1705}
1706
1707impl fmt::Display for SelectCore {
1708 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1709 write_select_core(f, self)
1710 }
1711}
1712
1713impl fmt::Display for FromClause {
1718 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1719 write_from(f, self)
1720 }
1721}
1722
1723impl fmt::Display for TableOrSubquery {
1724 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1725 write_table(f, self)
1726 }
1727}
1728
1729impl fmt::Display for IndexHint {
1730 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1731 match self {
1732 Self::IndexedBy(name) => {
1733 f.write_str("INDEXED BY ")?;
1734 write_ident(f, name)
1735 }
1736 Self::NotIndexed => f.write_str("NOT INDEXED"),
1737 }
1738 }
1739}
1740
1741impl fmt::Display for TimeTravelClause {
1746 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1747 write!(f, "FOR SYSTEM_TIME AS OF {}", self.target)
1748 }
1749}
1750
1751impl fmt::Display for TimeTravelTarget {
1752 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1753 match self {
1754 Self::CommitSequence(seq) => write!(f, "COMMITSEQ {seq}"),
1755 Self::Timestamp(ts) => write!(f, "'{}'", ts.replace('\'', "''")),
1756 }
1757 }
1758}
1759
1760impl fmt::Display for JoinClause {
1765 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1766 write_join(f, self)
1767 }
1768}
1769
1770impl fmt::Display for JoinType {
1771 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1772 if self.natural {
1773 f.write_str("NATURAL ")?;
1774 }
1775 write!(f, "{}", self.kind)?;
1776 f.write_str(" JOIN")
1777 }
1778}
1779
1780impl fmt::Display for JoinKind {
1781 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1782 f.write_str(match self {
1783 Self::Cross => "CROSS",
1784 Self::Inner => "INNER",
1785 Self::Left => "LEFT",
1786 Self::Right => "RIGHT",
1787 Self::Full => "FULL",
1788 })
1789 }
1790}
1791
1792impl fmt::Display for JoinConstraint {
1793 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1794 match self {
1795 Self::On(expr) => write!(f, "ON {expr}"),
1796 Self::Using(cols) => {
1797 f.write_str("USING (")?;
1798 comma_list_fn(f, cols, |col, f| write_ident(f, col))?;
1799 f.write_str(")")
1800 }
1801 }
1802 }
1803}
1804
1805impl fmt::Display for WindowDef {
1810 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1811 write_ident(f, &self.name)?;
1812 write!(f, " AS {}", self.spec)
1813 }
1814}
1815
1816impl fmt::Display for LimitClause {
1821 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1822 write!(f, "LIMIT {}", self.limit)?;
1823 if let Some(ref off) = self.offset {
1824 write!(f, " OFFSET {off}")?;
1825 }
1826 Ok(())
1827 }
1828}
1829
1830impl fmt::Display for ConflictAction {
1835 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1836 f.write_str(match self {
1837 Self::Rollback => "ROLLBACK",
1838 Self::Abort => "ABORT",
1839 Self::Fail => "FAIL",
1840 Self::Ignore => "IGNORE",
1841 Self::Replace => "REPLACE",
1842 })
1843 }
1844}
1845
1846impl fmt::Display for InsertStatement {
1851 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1852 if let Some(ref with) = self.with {
1853 write!(f, "{with} ")?;
1854 }
1855 if self.or_conflict == Some(ConflictAction::Replace) {
1856 f.write_str("REPLACE")?;
1857 } else {
1858 f.write_str("INSERT")?;
1859 if let Some(ref action) = self.or_conflict {
1860 write!(f, " OR {action}")?;
1861 }
1862 }
1863 write!(f, " INTO {}", self.table)?;
1864 if let Some(ref a) = self.alias {
1865 f.write_str(" AS ")?;
1866 write_ident(f, a)?;
1867 }
1868 if !self.columns.is_empty() {
1869 f.write_str(" (")?;
1870 comma_list_fn(f, &self.columns, |col, f| write_ident(f, col))?;
1871 f.write_str(")")?;
1872 }
1873 write!(f, " {}", self.source)?;
1874 for upsert in &self.upsert {
1875 write!(f, " {upsert}")?;
1876 }
1877 if !self.returning.is_empty() {
1878 f.write_str(" RETURNING ")?;
1879 comma_list(f, &self.returning)?;
1880 }
1881 Ok(())
1882 }
1883}
1884
1885impl fmt::Display for InsertSource {
1886 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1887 match self {
1888 Self::Values(rows) => {
1889 f.write_str("VALUES ")?;
1890 for (i, row) in rows.iter().enumerate() {
1891 if i > 0 {
1892 f.write_str(", ")?;
1893 }
1894 f.write_str("(")?;
1895 comma_list(f, row)?;
1896 f.write_str(")")?;
1897 }
1898 Ok(())
1899 }
1900 Self::Select(q) => write!(f, "{q}"),
1901 Self::DefaultValues => f.write_str("DEFAULT VALUES"),
1902 }
1903 }
1904}
1905
1906impl fmt::Display for UpsertClause {
1907 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1908 f.write_str("ON CONFLICT")?;
1909 if let Some(ref target) = self.target {
1910 f.write_str(" (")?;
1911 comma_list(f, &target.columns)?;
1912 f.write_str(")")?;
1913 if let Some(ref w) = target.where_clause {
1914 write!(f, " WHERE {w}")?;
1915 }
1916 }
1917 write!(f, " {}", self.action)
1918 }
1919}
1920
1921impl fmt::Display for IndexedColumn {
1922 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1923 write!(f, "{}", self.expr)?;
1924 if let Some(ref col) = self.collation {
1925 write!(f, " COLLATE {col}")?;
1926 }
1927 if let Some(dir) = self.direction {
1928 write!(f, " {dir}")?;
1929 }
1930 Ok(())
1931 }
1932}
1933
1934impl fmt::Display for UpsertAction {
1935 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1936 match self {
1937 Self::Nothing => f.write_str("DO NOTHING"),
1938 Self::Update {
1939 assignments,
1940 where_clause,
1941 } => {
1942 f.write_str("DO UPDATE SET ")?;
1943 comma_list(f, assignments)?;
1944 if let Some(w) = where_clause {
1945 write!(f, " WHERE {w}")?;
1946 }
1947 Ok(())
1948 }
1949 }
1950 }
1951}
1952
1953impl fmt::Display for Assignment {
1954 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1955 write!(f, "{} = {}", self.target, self.value)
1956 }
1957}
1958
1959impl fmt::Display for AssignmentTarget {
1960 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1961 match self {
1962 Self::Column(name) => write_ident(f, name),
1963 Self::ColumnList(names) => {
1964 f.write_str("(")?;
1965 comma_list_fn(f, names, |col, f| write_ident(f, col))?;
1966 f.write_str(")")
1967 }
1968 }
1969 }
1970}
1971
1972impl fmt::Display for UpdateStatement {
1977 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1978 write_update(f, self)
1979 }
1980}
1981
1982impl fmt::Display for QualifiedTableRef {
1983 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1984 write!(f, "{}", self.name)?;
1985 if let Some(ref a) = self.alias {
1986 f.write_str(" AS ")?;
1987 write_ident(f, a)?;
1988 }
1989 if let Some(ref hint) = self.index_hint {
1990 write!(f, " {hint}")?;
1991 }
1992 if let Some(ref tt) = self.time_travel {
1993 write!(f, " {tt}")?;
1994 }
1995 Ok(())
1996 }
1997}
1998
1999impl fmt::Display for DeleteStatement {
2004 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2005 if let Some(ref with) = self.with {
2006 write!(f, "{with} ")?;
2007 }
2008 write!(f, "DELETE FROM {}", self.table)?;
2009 if let Some(ref w) = self.where_clause {
2010 write!(f, " WHERE {w}")?;
2011 }
2012 if !self.returning.is_empty() {
2013 f.write_str(" RETURNING ")?;
2014 comma_list(f, &self.returning)?;
2015 }
2016 if !self.order_by.is_empty() {
2017 f.write_str(" ORDER BY ")?;
2018 comma_list(f, &self.order_by)?;
2019 }
2020 if let Some(ref lim) = self.limit {
2021 write!(f, " {lim}")?;
2022 }
2023 Ok(())
2024 }
2025}
2026
2027impl fmt::Display for CreateTableStatement {
2032 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2033 f.write_str("CREATE ")?;
2034 if self.temporary {
2035 f.write_str("TEMP ")?;
2036 }
2037 f.write_str("TABLE ")?;
2038 if self.if_not_exists {
2039 f.write_str("IF NOT EXISTS ")?;
2040 }
2041 write!(f, "{}", self.name)?;
2042 match &self.body {
2043 CreateTableBody::Columns {
2044 columns,
2045 constraints,
2046 } => {
2047 f.write_str(" (")?;
2048 for (i, col) in columns.iter().enumerate() {
2049 if i > 0 {
2050 f.write_str(", ")?;
2051 }
2052 write!(f, "{col}")?;
2053 }
2054 for constraint in constraints {
2055 f.write_str(", ")?;
2056 write!(f, "{constraint}")?;
2057 }
2058 f.write_str(")")?;
2059 }
2060 CreateTableBody::AsSelect(q) => {
2061 write!(f, " AS {q}")?;
2062 }
2063 }
2064 let mut table_options = Vec::new();
2065 if self.without_rowid {
2066 table_options.push("WITHOUT ROWID");
2067 }
2068 if self.strict {
2069 table_options.push("STRICT");
2070 }
2071 if !table_options.is_empty() {
2072 write!(f, " {}", table_options.join(", "))?;
2073 }
2074 Ok(())
2075 }
2076}
2077
2078impl fmt::Display for ColumnDef {
2079 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2080 write_ident(f, &self.name)?;
2081 if let Some(ref tn) = self.type_name {
2082 write!(f, " {tn}")?;
2083 }
2084 for c in &self.constraints {
2085 write!(f, " {c}")?;
2086 }
2087 Ok(())
2088 }
2089}
2090
2091impl fmt::Display for ColumnConstraint {
2092 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2093 if let Some(ref name) = self.name {
2094 f.write_str("CONSTRAINT ")?;
2095 write_ident(f, name)?;
2096 f.write_str(" ")?;
2097 }
2098 write!(f, "{}", self.kind)
2099 }
2100}
2101
2102impl fmt::Display for ColumnConstraintKind {
2103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2104 match self {
2105 Self::PrimaryKey {
2106 direction,
2107 conflict,
2108 autoincrement,
2109 } => {
2110 f.write_str("PRIMARY KEY")?;
2111 if let Some(dir) = direction {
2112 write!(f, " {dir}")?;
2113 }
2114 if let Some(action) = conflict {
2115 write!(f, " ON CONFLICT {action}")?;
2116 }
2117 if *autoincrement {
2118 f.write_str(" AUTOINCREMENT")?;
2119 }
2120 Ok(())
2121 }
2122 Self::NotNull { conflict } => {
2123 f.write_str("NOT NULL")?;
2124 if let Some(action) = conflict {
2125 write!(f, " ON CONFLICT {action}")?;
2126 }
2127 Ok(())
2128 }
2129 Self::Null => f.write_str("NULL"),
2130 Self::Unique { conflict } => {
2131 f.write_str("UNIQUE")?;
2132 if let Some(action) = conflict {
2133 write!(f, " ON CONFLICT {action}")?;
2134 }
2135 Ok(())
2136 }
2137 Self::Check(expr) => write!(f, "CHECK ({expr})"),
2138 Self::Default(val) => {
2139 f.write_str("DEFAULT ")?;
2140 match val {
2141 DefaultValue::Expr(e) => write!(f, "{e}"),
2142 DefaultValue::ParenExpr(e) => write!(f, "({e})"),
2143 }
2144 }
2145 Self::Collate(name) => write!(f, "COLLATE {name}"),
2146 Self::ForeignKey(fk) => write!(f, "{fk}"),
2147 Self::Generated { expr, storage } => {
2148 write!(f, "GENERATED ALWAYS AS ({expr})")?;
2149 if let Some(s) = storage {
2150 write!(f, " {s}")?;
2151 }
2152 Ok(())
2153 }
2154 }
2155 }
2156}
2157
2158impl fmt::Display for GeneratedStorage {
2159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2160 f.write_str(match self {
2161 Self::Stored => "STORED",
2162 Self::Virtual => "VIRTUAL",
2163 })
2164 }
2165}
2166
2167impl fmt::Display for TableConstraint {
2168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2169 if let Some(ref name) = self.name {
2170 f.write_str("CONSTRAINT ")?;
2171 write_ident(f, name)?;
2172 f.write_str(" ")?;
2173 }
2174 write!(f, "{}", self.kind)
2175 }
2176}
2177
2178impl fmt::Display for TableConstraintKind {
2179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2180 match self {
2181 Self::PrimaryKey { columns, conflict } => {
2182 f.write_str("PRIMARY KEY (")?;
2183 comma_list(f, columns)?;
2184 f.write_str(")")?;
2185 if let Some(action) = conflict {
2186 write!(f, " ON CONFLICT {action}")?;
2187 }
2188 Ok(())
2189 }
2190 Self::Unique { columns, conflict } => {
2191 f.write_str("UNIQUE (")?;
2192 comma_list(f, columns)?;
2193 f.write_str(")")?;
2194 if let Some(action) = conflict {
2195 write!(f, " ON CONFLICT {action}")?;
2196 }
2197 Ok(())
2198 }
2199 Self::Check(expr) => write!(f, "CHECK ({expr})"),
2200 Self::ForeignKey { columns, clause } => {
2201 f.write_str("FOREIGN KEY (")?;
2202 comma_list_fn(f, columns, |col, f| write_ident(f, col))?;
2203 write!(f, ") {clause}")
2204 }
2205 }
2206 }
2207}
2208
2209impl fmt::Display for ForeignKeyClause {
2210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2211 f.write_str("REFERENCES ")?;
2212 write_ident(f, &self.table)?;
2213 if !self.columns.is_empty() {
2214 f.write_str("(")?;
2218 comma_list_fn(f, &self.columns, |col, f| write_ident(f, col))?;
2219 f.write_str(")")?;
2220 }
2221 for action in &self.actions {
2222 write!(f, " {action}")?;
2223 }
2224 if let Some(ref def) = self.deferrable {
2225 write!(f, " {def}")?;
2226 }
2227 Ok(())
2228 }
2229}
2230
2231impl fmt::Display for ForeignKeyAction {
2232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2233 write!(f, "{} {}", self.trigger, self.action)
2234 }
2235}
2236
2237impl fmt::Display for ForeignKeyTrigger {
2238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2239 f.write_str(match self {
2240 Self::OnDelete => "ON DELETE",
2241 Self::OnUpdate => "ON UPDATE",
2242 })
2243 }
2244}
2245
2246impl fmt::Display for ForeignKeyActionType {
2247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2248 f.write_str(match self {
2249 Self::SetNull => "SET NULL",
2250 Self::SetDefault => "SET DEFAULT",
2251 Self::Cascade => "CASCADE",
2252 Self::Restrict => "RESTRICT",
2253 Self::NoAction => "NO ACTION",
2254 })
2255 }
2256}
2257
2258impl fmt::Display for Deferrable {
2259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2260 if self.not {
2261 f.write_str("NOT ")?;
2262 }
2263 f.write_str("DEFERRABLE")?;
2264 if let Some(init) = self.initially {
2265 write!(f, " {init}")?;
2266 }
2267 Ok(())
2268 }
2269}
2270
2271impl fmt::Display for DeferrableInitially {
2272 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2273 f.write_str(match self {
2274 Self::Deferred => "INITIALLY DEFERRED",
2275 Self::Immediate => "INITIALLY IMMEDIATE",
2276 })
2277 }
2278}
2279
2280impl fmt::Display for CreateIndexStatement {
2285 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2286 f.write_str("CREATE ")?;
2287 if self.unique {
2288 f.write_str("UNIQUE ")?;
2289 }
2290 f.write_str("INDEX ")?;
2291 if self.if_not_exists {
2292 f.write_str("IF NOT EXISTS ")?;
2293 }
2294 write!(f, "{} ON ", self.name)?;
2295 write_ident(f, &self.table)?;
2296 f.write_str("(")?;
2297 comma_list(f, &self.columns)?;
2298 f.write_str(")")?;
2299 if let Some(ref w) = self.where_clause {
2300 write!(f, " WHERE {w}")?;
2301 }
2302 Ok(())
2303 }
2304}
2305
2306impl fmt::Display for CreateViewStatement {
2311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2312 f.write_str("CREATE ")?;
2313 if self.temporary {
2314 f.write_str("TEMP ")?;
2315 }
2316 f.write_str("VIEW ")?;
2317 if self.if_not_exists {
2318 f.write_str("IF NOT EXISTS ")?;
2319 }
2320 write!(f, "{}", self.name)?;
2321 if !self.columns.is_empty() {
2322 f.write_str(" (")?;
2323 comma_list_fn(f, &self.columns, |col, f| write_ident(f, col))?;
2324 f.write_str(")")?;
2325 }
2326 write!(f, " AS {}", self.query)
2327 }
2328}
2329
2330impl fmt::Display for CreateTriggerStatement {
2335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2336 write_create_trigger(f, self)
2337 }
2338}
2339
2340impl fmt::Display for TriggerTiming {
2341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2342 f.write_str(match self {
2343 Self::Before => "BEFORE",
2344 Self::After => "AFTER",
2345 Self::InsteadOf => "INSTEAD OF",
2346 })
2347 }
2348}
2349
2350impl fmt::Display for TriggerEvent {
2351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2352 match self {
2353 Self::Insert => f.write_str("INSERT"),
2354 Self::Delete => f.write_str("DELETE"),
2355 Self::Update(cols) => {
2356 f.write_str("UPDATE")?;
2357 if !cols.is_empty() {
2358 f.write_str(" OF ")?;
2359 comma_list_fn(f, cols, |col, f| write_ident(f, col))?;
2360 }
2361 Ok(())
2362 }
2363 }
2364 }
2365}
2366
2367impl fmt::Display for CreateVirtualTableStatement {
2372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2373 f.write_str("CREATE VIRTUAL TABLE ")?;
2374 if self.if_not_exists {
2375 f.write_str("IF NOT EXISTS ")?;
2376 }
2377 write!(f, "{} USING ", self.name)?;
2378 write_ident(f, &self.module)?;
2379 if !self.args.is_empty() {
2380 f.write_str("(")?;
2381 for (i, arg) in self.args.iter().enumerate() {
2382 if i > 0 {
2383 f.write_str(", ")?;
2384 }
2385 f.write_str(arg)?;
2386 }
2387 f.write_str(")")?;
2388 }
2389 Ok(())
2390 }
2391}
2392
2393impl fmt::Display for DropStatement {
2398 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2399 write!(f, "DROP {}", self.object_type)?;
2400 if self.if_exists {
2401 f.write_str(" IF EXISTS")?;
2402 }
2403 write!(f, " {}", self.name)
2404 }
2405}
2406
2407impl fmt::Display for DropObjectType {
2408 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2409 f.write_str(match self {
2410 Self::Table => "TABLE",
2411 Self::View => "VIEW",
2412 Self::Index => "INDEX",
2413 Self::Trigger => "TRIGGER",
2414 })
2415 }
2416}
2417
2418impl fmt::Display for AlterTableStatement {
2423 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2424 write!(f, "ALTER TABLE {} {}", self.table, self.action)
2425 }
2426}
2427
2428impl fmt::Display for AlterTableAction {
2429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2430 match self {
2431 Self::RenameTo(name) => {
2432 f.write_str("RENAME TO ")?;
2433 write_ident(f, name)
2434 }
2435 Self::RenameColumn { old, new } => {
2436 f.write_str("RENAME COLUMN ")?;
2437 write_ident(f, old)?;
2438 f.write_str(" TO ")?;
2439 write_ident(f, new)
2440 }
2441 Self::AddColumn(col) => write!(f, "ADD COLUMN {col}"),
2442 Self::DropColumn(name) => {
2443 f.write_str("DROP COLUMN ")?;
2444 write_ident(f, name)
2445 }
2446 }
2447 }
2448}
2449
2450impl fmt::Display for BeginStatement {
2455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2456 f.write_str("BEGIN")?;
2457 if let Some(mode) = self.mode {
2458 write!(f, " {mode}")?;
2459 }
2460 Ok(())
2461 }
2462}
2463
2464impl fmt::Display for TransactionMode {
2465 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2466 f.write_str(match self {
2467 Self::Deferred => "DEFERRED",
2468 Self::Immediate => "IMMEDIATE",
2469 Self::Exclusive => "EXCLUSIVE",
2470 Self::Concurrent => "CONCURRENT",
2471 })
2472 }
2473}
2474
2475impl fmt::Display for RollbackStatement {
2476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2477 f.write_str("ROLLBACK")?;
2478 if let Some(ref sp) = self.to_savepoint {
2479 f.write_str(" TO SAVEPOINT ")?;
2480 write_ident(f, sp)?;
2481 }
2482 Ok(())
2483 }
2484}
2485
2486impl fmt::Display for AttachStatement {
2491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2492 write!(f, "ATTACH {} AS ", self.expr)?;
2493 write_ident(f, &self.schema)
2494 }
2495}
2496
2497impl fmt::Display for PragmaStatement {
2498 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2499 write!(f, "PRAGMA {}", self.name)?;
2500 if let Some(ref val) = self.value {
2501 match val {
2502 PragmaValue::Assign(e) => write!(f, " = {e}")?,
2503 PragmaValue::Call(e) => write!(f, "({e})")?,
2504 }
2505 }
2506 Ok(())
2507 }
2508}
2509
2510impl fmt::Display for VacuumStatement {
2511 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2512 f.write_str("VACUUM")?;
2513 if let Some(ref s) = self.schema {
2514 f.write_str(" ")?;
2515 write_ident(f, s)?;
2516 }
2517 if let Some(ref expr) = self.into {
2518 write!(f, " INTO {expr}")?;
2519 }
2520 Ok(())
2521 }
2522}
2523
2524impl fmt::Display for Statement {
2529 #[allow(clippy::too_many_lines)]
2530 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2531 write_statement(f, self)
2532 }
2533}
2534
2535#[cfg(test)]
2536mod expr_display_tests {
2537 use super::*;
2538 use std::fmt::Write as _;
2539
2540 fn column(name: &str) -> Expr {
2541 Expr::Column(ColumnRef::bare(name), Span::ZERO)
2542 }
2543
2544 fn integer(value: i64) -> Expr {
2545 Expr::Literal(Literal::Integer(value), Span::ZERO)
2546 }
2547
2548 fn binary(left: Expr, op: BinaryOp, right: Expr) -> Expr {
2549 Expr::BinaryOp {
2550 left: Box::new(left),
2551 op,
2552 right: Box::new(right),
2553 span: Span::ZERO,
2554 }
2555 }
2556
2557 fn table_source(name: &str) -> TableOrSubquery {
2558 TableOrSubquery::Table {
2559 name: QualifiedName::bare(name),
2560 alias: None,
2561 index_hint: None,
2562 time_travel: None,
2563 }
2564 }
2565
2566 fn nested_from(height: usize) -> FromClause {
2567 let mut from = FromClause {
2568 source: table_source("leaf"),
2569 joins: Vec::new(),
2570 };
2571 for _ in 0..height {
2572 from = FromClause {
2573 source: TableOrSubquery::ParenJoin(Box::new(from)),
2574 joins: Vec::new(),
2575 };
2576 }
2577 from
2578 }
2579
2580 fn update_with_from(from: FromClause) -> UpdateStatement {
2581 UpdateStatement {
2582 with: None,
2583 or_conflict: None,
2584 table: QualifiedTableRef {
2585 name: QualifiedName::bare("target"),
2586 alias: None,
2587 index_hint: None,
2588 time_travel: None,
2589 },
2590 assignments: vec![Assignment {
2591 target: AssignmentTarget::Column("x".to_owned()),
2592 value: integer(1),
2593 }],
2594 from: Some(from),
2595 where_clause: None,
2596 returning: Vec::new(),
2597 order_by: Vec::new(),
2598 limit: None,
2599 }
2600 }
2601
2602 fn drop_table(name: &str) -> Statement {
2603 Statement::Drop(DropStatement {
2604 object_type: DropObjectType::Table,
2605 if_exists: false,
2606 name: QualifiedName::bare(name),
2607 })
2608 }
2609
2610 fn drop_from_iteratively(root: FromClause) {
2611 let mut from_clauses = vec![root];
2612 let mut tables = Vec::new();
2613 while let Some(FromClause { source, joins }) = from_clauses.pop() {
2614 tables.push(source);
2615 for join in joins {
2616 tables.push(join.table);
2617 drop(join.constraint);
2618 }
2619 while let Some(table) = tables.pop() {
2620 match table {
2621 TableOrSubquery::ParenJoin(inner) => from_clauses.push(*inner),
2622 leaf => drop(leaf),
2623 }
2624 }
2625 }
2626 }
2627
2628 fn drop_table_iteratively(table: TableOrSubquery) {
2629 match table {
2630 TableOrSubquery::ParenJoin(inner) => drop_from_iteratively(*inner),
2631 leaf => drop(leaf),
2632 }
2633 }
2634
2635 fn drop_select_core_iteratively(core: SelectCore) {
2636 match core {
2637 SelectCore::Select { from, .. } => {
2638 if let Some(from) = from {
2639 drop_from_iteratively(from);
2640 }
2641 }
2642 SelectCore::Values(_) => {}
2643 }
2644 }
2645
2646 fn drop_select_body_iteratively(body: SelectBody) {
2647 drop_select_core_iteratively(body.select);
2648 for (_, core) in body.compounds {
2649 drop_select_core_iteratively(core);
2650 }
2651 }
2652
2653 fn drop_statement_iteratively(mut statement: Statement) {
2654 loop {
2655 match statement {
2656 Statement::Explain { stmt, .. } => statement = *stmt,
2657 Statement::CreateTrigger(mut trigger) if trigger.body.len() == 1 => {
2658 statement = trigger
2659 .body
2660 .pop()
2661 .expect("single trigger body statement must exist");
2662 }
2663 Statement::Update(mut update) => {
2664 if let Some(from) = update.from.take() {
2665 drop_from_iteratively(from);
2666 }
2667 break;
2668 }
2669 leaf => {
2670 drop(leaf);
2671 break;
2672 }
2673 }
2674 }
2675 }
2676
2677 fn drop_scalar_subquery_chain_iteratively(mut expr: Expr) {
2678 loop {
2679 match expr {
2680 Expr::Subquery(select, _) => {
2681 let SelectStatement {
2682 with,
2683 body,
2684 order_by,
2685 limit,
2686 } = *select;
2687 assert!(with.is_none());
2688 assert!(order_by.is_empty());
2689 assert!(limit.is_none());
2690 let SelectBody { select, compounds } = body;
2691 assert!(compounds.is_empty());
2692 let SelectCore::Select {
2693 mut columns,
2694 from,
2695 where_clause,
2696 group_by,
2697 having,
2698 windows,
2699 ..
2700 } = select
2701 else {
2702 panic!("scalar-subquery chain must contain SELECT cores");
2703 };
2704 assert!(from.is_none());
2705 assert!(where_clause.is_none());
2706 assert!(group_by.is_empty());
2707 assert!(having.is_none());
2708 assert!(windows.is_empty());
2709 assert_eq!(columns.len(), 1);
2710 let ResultColumn::Expr { expr: child, alias } = columns
2711 .pop()
2712 .expect("scalar-subquery SELECT must contain one column")
2713 else {
2714 panic!("scalar-subquery SELECT column must be an expression");
2715 };
2716 assert!(alias.is_none());
2717 expr = child;
2718 }
2719 leaf => {
2720 drop(leaf);
2721 break;
2722 }
2723 }
2724 }
2725 }
2726
2727 fn format_statement_on_one_mib_stack(statement: Statement) -> String {
2728 let (rendered, statement) = std::thread::Builder::new()
2729 .stack_size(1024 * 1024)
2730 .spawn(move || (statement.to_string(), statement))
2731 .expect("1 MiB formatter thread must spawn")
2732 .join()
2733 .expect("formatting on a 1 MiB stack must not overflow");
2734 drop_statement_iteratively(statement);
2735 rendered
2736 }
2737
2738 #[test]
2739 fn expression_task_stack_uses_inline_boundary_and_preserves_lifo() {
2740 let mut stack = ExprWriteTaskStack::new(ExprWriteTask::Text("first"));
2741 stack.push(ExprWriteTask::Text("second"));
2742 stack.push(ExprWriteTask::Text("third"));
2743 assert!(matches!(stack.pop(), Some(ExprWriteTask::Text("third"))));
2744 assert!(matches!(stack.pop(), Some(ExprWriteTask::Text("second"))));
2745 assert!(matches!(stack.pop(), Some(ExprWriteTask::Text("first"))));
2746
2747 let mut boundary = ExprWriteTaskStack::new(ExprWriteTask::Text("inline"));
2748 for _ in 1..INLINE_EXPR_WRITE_TASKS {
2749 boundary.push(ExprWriteTask::Text("inline"));
2750 }
2751 assert!(!boundary.tasks.spilled());
2752 boundary.push(ExprWriteTask::Text("spill"));
2753 assert!(boundary.tasks.spilled());
2754 while boundary.pop().is_some() {}
2755 assert!(
2756 boundary.stats().spilled,
2757 "spill history must survive draining the task stack"
2758 );
2759 }
2760
2761 #[test]
2762 fn formatter_error_resets_task_stack_stats() {
2763 struct FailingWriter;
2764
2765 impl fmt::Write for FailingWriter {
2766 fn write_str(&mut self, _: &str) -> fmt::Result {
2767 Err(fmt::Error)
2768 }
2769 }
2770
2771 let mut deep = integer(1);
2772 for _ in 1..100 {
2773 deep = binary(deep, BinaryOp::Add, integer(1));
2774 }
2775 let _ = deep.to_string();
2776 assert!(
2777 LAST_EXPR_WRITE_TASK_STACK_STATS
2778 .with(std::cell::Cell::get)
2779 .spilled
2780 );
2781
2782 let mut writer = FailingWriter;
2783 write!(&mut writer, "{}", column("value"))
2784 .expect_err("the test writer must reject formatter output");
2785 assert_eq!(
2786 LAST_EXPR_WRITE_TASK_STACK_STATS.with(std::cell::Cell::get),
2787 ExprWriteTaskStackStats::default(),
2788 "an early formatter error must not expose the previous call's task stats"
2789 );
2790 }
2791
2792 #[test]
2793 fn public_from_roots_format_height_1000_and_1001_on_one_mib_stack() {
2794 for height in [1000, 1001] {
2795 let from_root = nested_from(height);
2796 let table_root = TableOrSubquery::ParenJoin(Box::new(nested_from(height)));
2797 let join_root = JoinClause {
2798 join_type: JoinType {
2799 natural: false,
2800 kind: JoinKind::Inner,
2801 },
2802 table: TableOrSubquery::ParenJoin(Box::new(nested_from(height))),
2803 constraint: None,
2804 };
2805 let select_core_root = SelectCore::Select {
2806 distinct: Distinctness::All,
2807 columns: vec![ResultColumn::Star],
2808 from: Some(nested_from(height)),
2809 where_clause: None,
2810 group_by: Vec::new(),
2811 having: None,
2812 windows: Vec::new(),
2813 };
2814 let select_body_root = SelectBody {
2815 select: SelectCore::Select {
2816 distinct: Distinctness::All,
2817 columns: vec![ResultColumn::Star],
2818 from: Some(nested_from(height)),
2819 where_clause: None,
2820 group_by: Vec::new(),
2821 having: None,
2822 windows: Vec::new(),
2823 },
2824 compounds: Vec::new(),
2825 };
2826 let update_root = Statement::Update(update_with_from(nested_from(height)));
2827
2828 let (rendered, roots) = std::thread::Builder::new()
2829 .stack_size(1024 * 1024)
2830 .spawn(move || {
2831 let rendered = [
2832 from_root.to_string(),
2833 table_root.to_string(),
2834 join_root.to_string(),
2835 select_core_root.to_string(),
2836 select_body_root.to_string(),
2837 update_root.to_string(),
2838 ];
2839 (
2840 rendered,
2841 (
2842 from_root,
2843 table_root,
2844 join_root,
2845 select_core_root,
2846 select_body_root,
2847 update_root,
2848 ),
2849 )
2850 })
2851 .expect("1 MiB formatter thread must spawn")
2852 .join()
2853 .expect("all public FROM roots must format without stack overflow");
2854 let (
2855 from_root,
2856 table_root,
2857 JoinClause {
2858 table: join_table, ..
2859 },
2860 select_core_root,
2861 select_body_root,
2862 update_root,
2863 ) = roots;
2864 drop_from_iteratively(from_root);
2865 drop_table_iteratively(table_root);
2866 drop_table_iteratively(join_table);
2867 drop_select_core_iteratively(select_core_root);
2868 drop_select_body_iteratively(select_body_root);
2869 drop_statement_iteratively(update_root);
2870
2871 let expected_parentheses = [height, height + 1, height + 1, height, height, height];
2872 for (sql, expected) in rendered.iter().zip(expected_parentheses) {
2873 assert_eq!(sql.matches('(').count(), expected);
2874 assert_eq!(sql.matches(')').count(), expected);
2875 assert!(sql.contains("leaf"));
2876 }
2877 assert!(rendered[2].starts_with("INNER JOIN "));
2878 assert!(rendered[3].starts_with("SELECT * FROM "));
2879 assert!(rendered[4].starts_with("SELECT * FROM "));
2880 assert!(rendered[5].starts_with("UPDATE target SET x = 1 FROM "));
2881 }
2882 }
2883
2884 #[test]
2885 fn nested_explain_height_1000_and_1001_formats_on_one_mib_stack() {
2886 for height in [1000, 1001] {
2887 let mut statement = drop_table("leaf");
2888 for level in 0..height {
2889 statement = Statement::Explain {
2890 query_plan: level % 2 == 0,
2891 stmt: Box::new(statement),
2892 };
2893 }
2894
2895 let rendered = format_statement_on_one_mib_stack(statement);
2896 let mut tail = rendered.as_str();
2897 for level in (0..height).rev() {
2898 let prefix = if level % 2 == 0 {
2899 "EXPLAIN QUERY PLAN "
2900 } else {
2901 "EXPLAIN "
2902 };
2903 tail = tail
2904 .strip_prefix(prefix)
2905 .expect("EXPLAIN wrappers must retain their exact order");
2906 }
2907 assert_eq!(tail, "DROP TABLE leaf");
2908 }
2909 }
2910
2911 #[test]
2912 fn nested_trigger_body_height_1000_and_1001_formats_on_one_mib_stack() {
2913 for height in [1000, 1001] {
2914 let mut statement = drop_table("leaf");
2915 for level in 0..height {
2916 statement = Statement::CreateTrigger(CreateTriggerStatement {
2917 if_not_exists: false,
2918 temporary: false,
2919 name: QualifiedName::bare(format!("trigger_{level}")),
2920 timing: TriggerTiming::After,
2921 event: TriggerEvent::Insert,
2922 table: "target".to_owned(),
2923 for_each_row: false,
2924 when: None,
2925 body: vec![statement],
2926 });
2927 }
2928
2929 let rendered = format_statement_on_one_mib_stack(statement);
2930 assert_eq!(rendered.matches("CREATE TRIGGER ").count(), height);
2931 assert_eq!(rendered.matches("; END").count(), height);
2932 assert!(rendered.contains("DROP TABLE leaf"));
2933 assert!(rendered.ends_with("END"));
2934 }
2935 }
2936
2937 #[test]
2938 fn iterative_public_roots_preserve_shallow_sql() {
2939 let joined = FromClause {
2940 source: table_source("a"),
2941 joins: vec![JoinClause {
2942 join_type: JoinType {
2943 natural: false,
2944 kind: JoinKind::Inner,
2945 },
2946 table: table_source("b"),
2947 constraint: Some(JoinConstraint::On(binary(
2948 column("a_id"),
2949 BinaryOp::Eq,
2950 column("b_id"),
2951 ))),
2952 }],
2953 };
2954 assert_eq!(joined.to_string(), "a INNER JOIN b ON a_id = b_id");
2955
2956 let body = SelectBody {
2957 select: SelectCore::Select {
2958 distinct: Distinctness::All,
2959 columns: vec![ResultColumn::Star],
2960 from: Some(joined),
2961 where_clause: None,
2962 group_by: Vec::new(),
2963 having: None,
2964 windows: Vec::new(),
2965 },
2966 compounds: Vec::new(),
2967 };
2968 assert_eq!(
2969 body.to_string(),
2970 "SELECT * FROM a INNER JOIN b ON a_id = b_id"
2971 );
2972
2973 let compounds = SelectBody {
2974 select: SelectCore::Values(vec![vec![integer(1)]].into()),
2975 compounds: vec![
2976 (
2977 CompoundOp::UnionAll,
2978 SelectCore::Values(vec![vec![integer(2)]].into()),
2979 ),
2980 (
2981 CompoundOp::Except,
2982 SelectCore::Values(vec![vec![integer(3)]].into()),
2983 ),
2984 ],
2985 };
2986 assert_eq!(
2987 compounds.to_string(),
2988 "VALUES (1) UNION ALL VALUES (2) EXCEPT VALUES (3)"
2989 );
2990
2991 let update = UpdateStatement {
2992 with: None,
2993 or_conflict: None,
2994 table: QualifiedTableRef {
2995 name: QualifiedName::bare("target"),
2996 alias: None,
2997 index_hint: None,
2998 time_travel: None,
2999 },
3000 assignments: vec![Assignment {
3001 target: AssignmentTarget::Column("x".to_owned()),
3002 value: integer(1),
3003 }],
3004 from: Some(FromClause {
3005 source: table_source("source"),
3006 joins: Vec::new(),
3007 }),
3008 where_clause: Some(binary(column("id"), BinaryOp::Eq, integer(7))),
3009 returning: vec![ResultColumn::Expr {
3010 expr: column("x"),
3011 alias: Some("updated".to_owned()),
3012 }],
3013 order_by: vec![OrderingTerm {
3014 expr: column("id"),
3015 direction: Some(SortDirection::Desc),
3016 nulls: Some(NullsOrder::Last),
3017 }],
3018 limit: Some(LimitClause {
3019 limit: integer(10),
3020 offset: Some(integer(2)),
3021 }),
3022 };
3023 assert_eq!(
3024 update.to_string(),
3025 "UPDATE target SET x = 1 FROM source WHERE id = 7 RETURNING x AS updated \
3026 ORDER BY id DESC NULLS LAST LIMIT 10 OFFSET 2"
3027 );
3028
3029 let explained = Statement::Explain {
3030 query_plan: true,
3031 stmt: Box::new(drop_table("old")),
3032 };
3033 assert_eq!(explained.to_string(), "EXPLAIN QUERY PLAN DROP TABLE old");
3034
3035 let trigger = CreateTriggerStatement {
3036 if_not_exists: false,
3037 temporary: false,
3038 name: QualifiedName::bare("tr"),
3039 timing: TriggerTiming::After,
3040 event: TriggerEvent::Insert,
3041 table: "target".to_owned(),
3042 for_each_row: false,
3043 when: None,
3044 body: vec![drop_table("old"), drop_table("older")],
3045 };
3046 assert_eq!(
3047 trigger.to_string(),
3048 "CREATE TRIGGER tr AFTER INSERT ON target BEGIN \
3049 DROP TABLE old; DROP TABLE older; END"
3050 );
3051 }
3052
3053 #[test]
3054 fn representative_rich_select_stays_in_inline_task_stack() {
3055 let cte_query = SelectStatement {
3056 with: None,
3057 body: SelectBody {
3058 select: SelectCore::Values(vec![vec![integer(1)]].into()),
3059 compounds: Vec::new(),
3060 },
3061 order_by: Vec::new(),
3062 limit: None,
3063 };
3064 let select = SelectStatement {
3065 with: Some(WithClause {
3066 recursive: false,
3067 ctes: vec![Cte {
3068 name: "seed".to_owned(),
3069 columns: vec!["id".to_owned()],
3070 materialized: Some(CteMaterialized::NotMaterialized),
3071 query: cte_query,
3072 }],
3073 }),
3074 body: SelectBody {
3075 select: SelectCore::Select {
3076 distinct: Distinctness::Distinct,
3077 columns: vec![
3078 ResultColumn::Expr {
3079 expr: column("a_id"),
3080 alias: Some("id".to_owned()),
3081 },
3082 ResultColumn::Expr {
3083 expr: column("b_value"),
3084 alias: None,
3085 },
3086 ],
3087 from: Some(FromClause {
3088 source: table_source("a"),
3089 joins: vec![JoinClause {
3090 join_type: JoinType {
3091 natural: false,
3092 kind: JoinKind::Left,
3093 },
3094 table: table_source("b"),
3095 constraint: Some(JoinConstraint::On(binary(
3096 column("a_id"),
3097 BinaryOp::Eq,
3098 column("b_id"),
3099 ))),
3100 }],
3101 }),
3102 where_clause: Some(Box::new(binary(column("a_id"), BinaryOp::Gt, integer(0)))),
3103 group_by: vec![column("a_id")],
3104 having: Some(Box::new(binary(
3105 column("b_value"),
3106 BinaryOp::IsNot,
3107 Expr::Literal(Literal::Null, Span::ZERO),
3108 ))),
3109 windows: vec![WindowDef {
3110 name: "w".to_owned(),
3111 spec: WindowSpec {
3112 window_ref: None,
3113 partition_by: vec![column("a_id")],
3114 order_by: vec![OrderingTerm {
3115 expr: column("b_value"),
3116 direction: Some(SortDirection::Desc),
3117 nulls: None,
3118 }],
3119 frame: None,
3120 },
3121 }],
3122 },
3123 compounds: Vec::new(),
3124 },
3125 order_by: vec![OrderingTerm {
3126 expr: column("a_id"),
3127 direction: Some(SortDirection::Asc),
3128 nulls: Some(NullsOrder::First),
3129 }],
3130 limit: Some(LimitClause {
3131 limit: integer(25),
3132 offset: Some(integer(5)),
3133 }),
3134 };
3135
3136 assert_eq!(
3137 select.to_string(),
3138 "WITH seed(id) AS NOT MATERIALIZED (VALUES (1)) \
3139 SELECT DISTINCT a_id AS id, b_value FROM a \
3140 LEFT JOIN b ON a_id = b_id WHERE a_id > 0 GROUP BY a_id \
3141 HAVING b_value IS NOT NULL WINDOW w AS \
3142 (PARTITION BY a_id ORDER BY b_value DESC) \
3143 ORDER BY a_id ASC NULLS FIRST LIMIT 25 OFFSET 5"
3144 );
3145 let task_stats = LAST_EXPR_WRITE_TASK_STACK_STATS.with(std::cell::Cell::get);
3146 assert!(
3147 !task_stats.spilled,
3148 "representative rich SELECT should remain in the inline task stack"
3149 );
3150 assert!(task_stats.peak_len <= INLINE_EXPR_WRITE_TASKS);
3151 }
3152
3153 #[test]
3154 fn binary_operands_use_minimal_semantics_preserving_parentheses() {
3155 let tighter_right = binary(
3156 column("a"),
3157 BinaryOp::Add,
3158 binary(column("b"), BinaryOp::Multiply, integer(2)),
3159 );
3160 assert_eq!(tighter_right.to_string(), "a + b * 2");
3161
3162 let looser_right = binary(
3163 column("a"),
3164 BinaryOp::Multiply,
3165 binary(column("b"), BinaryOp::Add, column("c")),
3166 );
3167 assert_eq!(looser_right.to_string(), "a * (b + c)");
3168
3169 let left_associative = binary(
3170 binary(column("a"), BinaryOp::Subtract, column("b")),
3171 BinaryOp::Subtract,
3172 column("c"),
3173 );
3174 assert_eq!(left_associative.to_string(), "a - b - c");
3175
3176 let right_subtract = binary(
3177 column("a"),
3178 BinaryOp::Subtract,
3179 binary(column("b"), BinaryOp::Subtract, column("c")),
3180 );
3181 assert_eq!(right_subtract.to_string(), "a - (b - c)");
3182
3183 let right_divide = binary(
3184 column("a"),
3185 BinaryOp::Divide,
3186 binary(column("b"), BinaryOp::Divide, column("c")),
3187 );
3188 assert_eq!(right_divide.to_string(), "a / (b / c)");
3189
3190 let and_chain = binary(
3191 column("a"),
3192 BinaryOp::And,
3193 binary(column("b"), BinaryOp::And, column("c")),
3194 );
3195 assert_eq!(and_chain.to_string(), "a AND b AND c");
3196
3197 let or_chain = binary(
3198 column("a"),
3199 BinaryOp::Or,
3200 binary(column("b"), BinaryOp::Or, column("c")),
3201 );
3202 assert_eq!(or_chain.to_string(), "a OR b OR c");
3203 }
3204
3205 #[test]
3206 fn expression_display_height_1000_uses_bounded_work_stack() {
3207 let mut expr = Expr::Literal(Literal::Integer(1), Span::ZERO);
3208 for _ in 1..1000 {
3209 expr = Expr::BinaryOp {
3210 left: Box::new(expr),
3211 op: BinaryOp::Add,
3212 right: Box::new(Expr::Literal(Literal::Integer(1), Span::ZERO)),
3213 span: Span::ZERO,
3214 };
3215 }
3216
3217 let rendered = expr.to_string();
3218 let task_stats = LAST_EXPR_WRITE_TASK_STACK_STATS.with(std::cell::Cell::get);
3219 assert!(
3220 task_stats.spilled,
3221 "height-1000 expression should exercise the heap spill path"
3222 );
3223 assert!(task_stats.peak_len > INLINE_EXPR_WRITE_TASKS);
3224 assert_eq!(rendered.matches('+').count(), 999);
3225 assert!(rendered.ends_with(" + 1"));
3226 }
3227
3228 #[test]
3229 fn scalar_subquery_display_height_1000_uses_one_mib_stack() {
3230 let mut expr = Expr::Literal(Literal::Integer(1), Span::ZERO);
3231 for _ in 1..1000 {
3232 expr = Expr::Subquery(
3233 Box::new(SelectStatement {
3234 with: None,
3235 body: SelectBody {
3236 select: SelectCore::Select {
3237 distinct: Distinctness::All,
3238 columns: vec![ResultColumn::Expr { expr, alias: None }],
3239 from: None,
3240 where_clause: None,
3241 group_by: Vec::new(),
3242 having: None,
3243 windows: Vec::new(),
3244 },
3245 compounds: Vec::new(),
3246 },
3247 order_by: Vec::new(),
3248 limit: None,
3249 }),
3250 Span::ZERO,
3251 );
3252 }
3253
3254 let (rendered, expr) = std::thread::Builder::new()
3255 .stack_size(1024 * 1024)
3256 .spawn(move || {
3257 let rendered = expr.to_string();
3258 (rendered, expr)
3259 })
3260 .expect("1 MiB formatter thread must spawn")
3261 .join()
3262 .expect("height-1000 scalar subquery formatting must not overflow");
3263 drop_scalar_subquery_chain_iteratively(expr);
3264 assert_eq!(rendered.matches("(SELECT ").count(), 999);
3265 assert!(rendered.ends_with(&")".repeat(999)));
3266 }
3267
3268 #[test]
3269 fn negative_literal_operands_cannot_merge_into_sql_comments() {
3270 let integer = Expr::UnaryOp {
3271 op: UnaryOp::Negate,
3272 expr: Box::new(Expr::Literal(Literal::Integer(i64::MIN), Span::ZERO)),
3273 span: Span::ZERO,
3274 };
3275 assert_eq!(integer.to_string(), "-(-9223372036854775808)");
3276
3277 let negative_zero = Expr::UnaryOp {
3278 op: UnaryOp::Negate,
3279 expr: Box::new(Expr::Literal(Literal::Float(-0.0), Span::ZERO)),
3280 span: Span::ZERO,
3281 };
3282 assert_eq!(negative_zero.to_string(), "-(-0.0)");
3283
3284 let bound = Expr::UnaryOp {
3285 op: UnaryOp::Negate,
3286 expr: Box::new(Expr::BoundOuterValue {
3287 value: SqliteValue::Integer(-1),
3288 collation: BoundCollation::Unspecified,
3289 affinity: None,
3290 span: Span::ZERO,
3291 }),
3292 span: Span::ZERO,
3293 };
3294 assert_eq!(bound.to_string(), "-(-1)");
3295 }
3296
3297 #[test]
3298 fn like_escape_operand_preserves_comparison_grouping() {
3299 let expr = Expr::Like {
3300 expr: Box::new(column("value")),
3301 pattern: Box::new(column("pattern")),
3302 escape: Some(Box::new(binary(
3303 column("lower"),
3304 BinaryOp::Lt,
3305 column("upper"),
3306 ))),
3307 op: LikeOp::Like,
3308 not: false,
3309 span: Span::ZERO,
3310 };
3311 assert_eq!(
3312 expr.to_string(),
3313 "value LIKE pattern ESCAPE (lower < upper)"
3314 );
3315 }
3316
3317 #[test]
3318 fn bound_outer_values_render_as_safe_literals_without_metadata() {
3319 let bound = |value| Expr::BoundOuterValue {
3320 value,
3321 collation: BoundCollation::Named("NOCASE".to_owned()),
3322 affinity: Some(fsqlite_types::TypeAffinity::Text),
3323 span: Span::new(7, 19),
3324 };
3325
3326 assert_eq!(bound(SqliteValue::Null).to_string(), "NULL");
3327 assert_eq!(bound(SqliteValue::Integer(-7)).to_string(), "-7");
3328 assert_eq!(bound(SqliteValue::Float(1.0)).to_string(), "1.0");
3329 assert_eq!(
3330 bound(SqliteValue::Text("O'Brien".into())).to_string(),
3331 "'O''Brien'"
3332 );
3333 assert_eq!(
3334 bound(SqliteValue::Blob(vec![0x00, 0xAB].into())).to_string(),
3335 "X'00AB'"
3336 );
3337 }
3338
3339 #[test]
3340 fn infinite_float_literals_render_as_numeric_sql() {
3341 assert_eq!(Literal::Float(f64::INFINITY).to_string(), "9e999");
3342 assert_eq!(Literal::Float(f64::NEG_INFINITY).to_string(), "-9e999");
3343 assert_eq!(Literal::Float(f64::NAN).to_string(), "NULL");
3344 assert_eq!(Literal::Float(-f64::NAN).to_string(), "NULL");
3345 }
3346
3347 #[test]
3348 fn collation_names_use_identifier_quoting() {
3349 let expr = Expr::Collate {
3350 expr: Box::new(column("value")),
3351 collation: "my col".to_owned(),
3352 span: Span::ZERO,
3353 };
3354 assert_eq!(expr.to_string(), "value COLLATE \"my col\"");
3355 }
3356
3357 #[test]
3358 fn window_reference_form_is_preserved_exactly() {
3359 let extended = Expr::FunctionCall {
3360 name: "sum".to_owned(),
3361 args: FunctionArgs::List(vec![column("x")]),
3362 distinct: false,
3363 order_by: Vec::new(),
3364 filter: None,
3365 over: Some(WindowSpec {
3366 window_ref: Some(WindowReference::Base("base".to_owned())),
3367 partition_by: vec![column("p")],
3368 order_by: vec![OrderingTerm {
3369 expr: column("y"),
3370 direction: None,
3371 nulls: None,
3372 }],
3373 frame: Some(FrameSpec {
3374 frame_type: FrameType::Rows,
3375 start: FrameBound::Preceding(Box::new(column("z"))),
3376 end: Some(FrameBound::CurrentRow),
3377 exclude: None,
3378 }),
3379 }),
3380 span: Span::ZERO,
3381 };
3382 assert_eq!(
3383 extended.to_string(),
3384 "sum(x) OVER (base PARTITION BY p ORDER BY y ROWS BETWEEN z PRECEDING AND CURRENT ROW)"
3385 );
3386 let task_stats = LAST_EXPR_WRITE_TASK_STACK_STATS.with(std::cell::Cell::get);
3387 assert!(
3388 !task_stats.spilled,
3389 "representative window expression should remain in the inline task stack"
3390 );
3391 assert!(task_stats.peak_len <= INLINE_EXPR_WRITE_TASKS);
3392
3393 let bare = Expr::FunctionCall {
3394 name: "sum".to_owned(),
3395 args: FunctionArgs::List(vec![column("x")]),
3396 distinct: false,
3397 order_by: Vec::new(),
3398 filter: None,
3399 over: Some(WindowSpec {
3400 window_ref: Some(WindowReference::Direct("base".to_owned())),
3401 partition_by: Vec::new(),
3402 order_by: Vec::new(),
3403 frame: None,
3404 }),
3405 span: Span::ZERO,
3406 };
3407 assert_eq!(bare.to_string(), "sum(x) OVER base");
3408 let Expr::FunctionCall {
3409 over: Some(bare_window),
3410 ..
3411 } = &bare
3412 else {
3413 panic!("bare window function should carry a window");
3414 };
3415 assert_eq!(bare_window.to_string(), "base");
3416
3417 let parenthesized = Expr::FunctionCall {
3418 name: "sum".to_owned(),
3419 args: FunctionArgs::List(vec![column("x")]),
3420 distinct: false,
3421 order_by: Vec::new(),
3422 filter: None,
3423 over: Some(WindowSpec {
3424 window_ref: Some(WindowReference::Base("base".to_owned())),
3425 partition_by: Vec::new(),
3426 order_by: Vec::new(),
3427 frame: None,
3428 }),
3429 span: Span::ZERO,
3430 };
3431 assert_eq!(parenthesized.to_string(), "sum(x) OVER (base)");
3432 let Expr::FunctionCall {
3433 over: Some(parenthesized_window),
3434 ..
3435 } = &parenthesized
3436 else {
3437 panic!("parenthesized window function should carry a window");
3438 };
3439 assert_eq!(parenthesized_window.to_string(), "(base)");
3440 }
3441}