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