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(" (")?;
2197 comma_list_fn(f, &self.columns, |col, f| write_ident(f, col))?;
2198 f.write_str(")")?;
2199 }
2200 for action in &self.actions {
2201 write!(f, " {action}")?;
2202 }
2203 if let Some(ref def) = self.deferrable {
2204 write!(f, " {def}")?;
2205 }
2206 Ok(())
2207 }
2208}
2209
2210impl fmt::Display for ForeignKeyAction {
2211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2212 write!(f, "{} {}", self.trigger, self.action)
2213 }
2214}
2215
2216impl fmt::Display for ForeignKeyTrigger {
2217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2218 f.write_str(match self {
2219 Self::OnDelete => "ON DELETE",
2220 Self::OnUpdate => "ON UPDATE",
2221 })
2222 }
2223}
2224
2225impl fmt::Display for ForeignKeyActionType {
2226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2227 f.write_str(match self {
2228 Self::SetNull => "SET NULL",
2229 Self::SetDefault => "SET DEFAULT",
2230 Self::Cascade => "CASCADE",
2231 Self::Restrict => "RESTRICT",
2232 Self::NoAction => "NO ACTION",
2233 })
2234 }
2235}
2236
2237impl fmt::Display for Deferrable {
2238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2239 if self.not {
2240 f.write_str("NOT ")?;
2241 }
2242 f.write_str("DEFERRABLE")?;
2243 if let Some(init) = self.initially {
2244 write!(f, " {init}")?;
2245 }
2246 Ok(())
2247 }
2248}
2249
2250impl fmt::Display for DeferrableInitially {
2251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2252 f.write_str(match self {
2253 Self::Deferred => "INITIALLY DEFERRED",
2254 Self::Immediate => "INITIALLY IMMEDIATE",
2255 })
2256 }
2257}
2258
2259impl fmt::Display for CreateIndexStatement {
2264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2265 f.write_str("CREATE ")?;
2266 if self.unique {
2267 f.write_str("UNIQUE ")?;
2268 }
2269 f.write_str("INDEX ")?;
2270 if self.if_not_exists {
2271 f.write_str("IF NOT EXISTS ")?;
2272 }
2273 write!(f, "{} ON ", self.name)?;
2274 write_ident(f, &self.table)?;
2275 f.write_str("(")?;
2276 comma_list(f, &self.columns)?;
2277 f.write_str(")")?;
2278 if let Some(ref w) = self.where_clause {
2279 write!(f, " WHERE {w}")?;
2280 }
2281 Ok(())
2282 }
2283}
2284
2285impl fmt::Display for CreateViewStatement {
2290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2291 f.write_str("CREATE ")?;
2292 if self.temporary {
2293 f.write_str("TEMP ")?;
2294 }
2295 f.write_str("VIEW ")?;
2296 if self.if_not_exists {
2297 f.write_str("IF NOT EXISTS ")?;
2298 }
2299 write!(f, "{}", self.name)?;
2300 if !self.columns.is_empty() {
2301 f.write_str(" (")?;
2302 comma_list_fn(f, &self.columns, |col, f| write_ident(f, col))?;
2303 f.write_str(")")?;
2304 }
2305 write!(f, " AS {}", self.query)
2306 }
2307}
2308
2309impl fmt::Display for CreateTriggerStatement {
2314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2315 write_create_trigger(f, self)
2316 }
2317}
2318
2319impl fmt::Display for TriggerTiming {
2320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2321 f.write_str(match self {
2322 Self::Before => "BEFORE",
2323 Self::After => "AFTER",
2324 Self::InsteadOf => "INSTEAD OF",
2325 })
2326 }
2327}
2328
2329impl fmt::Display for TriggerEvent {
2330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2331 match self {
2332 Self::Insert => f.write_str("INSERT"),
2333 Self::Delete => f.write_str("DELETE"),
2334 Self::Update(cols) => {
2335 f.write_str("UPDATE")?;
2336 if !cols.is_empty() {
2337 f.write_str(" OF ")?;
2338 comma_list_fn(f, cols, |col, f| write_ident(f, col))?;
2339 }
2340 Ok(())
2341 }
2342 }
2343 }
2344}
2345
2346impl fmt::Display for CreateVirtualTableStatement {
2351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2352 f.write_str("CREATE VIRTUAL TABLE ")?;
2353 if self.if_not_exists {
2354 f.write_str("IF NOT EXISTS ")?;
2355 }
2356 write!(f, "{} USING ", self.name)?;
2357 write_ident(f, &self.module)?;
2358 if !self.args.is_empty() {
2359 f.write_str("(")?;
2360 for (i, arg) in self.args.iter().enumerate() {
2361 if i > 0 {
2362 f.write_str(", ")?;
2363 }
2364 f.write_str(arg)?;
2365 }
2366 f.write_str(")")?;
2367 }
2368 Ok(())
2369 }
2370}
2371
2372impl fmt::Display for DropStatement {
2377 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2378 write!(f, "DROP {}", self.object_type)?;
2379 if self.if_exists {
2380 f.write_str(" IF EXISTS")?;
2381 }
2382 write!(f, " {}", self.name)
2383 }
2384}
2385
2386impl fmt::Display for DropObjectType {
2387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2388 f.write_str(match self {
2389 Self::Table => "TABLE",
2390 Self::View => "VIEW",
2391 Self::Index => "INDEX",
2392 Self::Trigger => "TRIGGER",
2393 })
2394 }
2395}
2396
2397impl fmt::Display for AlterTableStatement {
2402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2403 write!(f, "ALTER TABLE {} {}", self.table, self.action)
2404 }
2405}
2406
2407impl fmt::Display for AlterTableAction {
2408 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2409 match self {
2410 Self::RenameTo(name) => {
2411 f.write_str("RENAME TO ")?;
2412 write_ident(f, name)
2413 }
2414 Self::RenameColumn { old, new } => {
2415 f.write_str("RENAME COLUMN ")?;
2416 write_ident(f, old)?;
2417 f.write_str(" TO ")?;
2418 write_ident(f, new)
2419 }
2420 Self::AddColumn(col) => write!(f, "ADD COLUMN {col}"),
2421 Self::DropColumn(name) => {
2422 f.write_str("DROP COLUMN ")?;
2423 write_ident(f, name)
2424 }
2425 }
2426 }
2427}
2428
2429impl fmt::Display for BeginStatement {
2434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2435 f.write_str("BEGIN")?;
2436 if let Some(mode) = self.mode {
2437 write!(f, " {mode}")?;
2438 }
2439 Ok(())
2440 }
2441}
2442
2443impl fmt::Display for TransactionMode {
2444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2445 f.write_str(match self {
2446 Self::Deferred => "DEFERRED",
2447 Self::Immediate => "IMMEDIATE",
2448 Self::Exclusive => "EXCLUSIVE",
2449 Self::Concurrent => "CONCURRENT",
2450 })
2451 }
2452}
2453
2454impl fmt::Display for RollbackStatement {
2455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2456 f.write_str("ROLLBACK")?;
2457 if let Some(ref sp) = self.to_savepoint {
2458 f.write_str(" TO SAVEPOINT ")?;
2459 write_ident(f, sp)?;
2460 }
2461 Ok(())
2462 }
2463}
2464
2465impl fmt::Display for AttachStatement {
2470 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2471 write!(f, "ATTACH {} AS ", self.expr)?;
2472 write_ident(f, &self.schema)
2473 }
2474}
2475
2476impl fmt::Display for PragmaStatement {
2477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2478 write!(f, "PRAGMA {}", self.name)?;
2479 if let Some(ref val) = self.value {
2480 match val {
2481 PragmaValue::Assign(e) => write!(f, " = {e}")?,
2482 PragmaValue::Call(e) => write!(f, "({e})")?,
2483 }
2484 }
2485 Ok(())
2486 }
2487}
2488
2489impl fmt::Display for VacuumStatement {
2490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2491 f.write_str("VACUUM")?;
2492 if let Some(ref s) = self.schema {
2493 f.write_str(" ")?;
2494 write_ident(f, s)?;
2495 }
2496 if let Some(ref expr) = self.into {
2497 write!(f, " INTO {expr}")?;
2498 }
2499 Ok(())
2500 }
2501}
2502
2503impl fmt::Display for Statement {
2508 #[allow(clippy::too_many_lines)]
2509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2510 write_statement(f, self)
2511 }
2512}
2513
2514#[cfg(test)]
2515mod expr_display_tests {
2516 use super::*;
2517 use std::fmt::Write as _;
2518
2519 fn column(name: &str) -> Expr {
2520 Expr::Column(ColumnRef::bare(name), Span::ZERO)
2521 }
2522
2523 fn integer(value: i64) -> Expr {
2524 Expr::Literal(Literal::Integer(value), Span::ZERO)
2525 }
2526
2527 fn binary(left: Expr, op: BinaryOp, right: Expr) -> Expr {
2528 Expr::BinaryOp {
2529 left: Box::new(left),
2530 op,
2531 right: Box::new(right),
2532 span: Span::ZERO,
2533 }
2534 }
2535
2536 fn table_source(name: &str) -> TableOrSubquery {
2537 TableOrSubquery::Table {
2538 name: QualifiedName::bare(name),
2539 alias: None,
2540 index_hint: None,
2541 time_travel: None,
2542 }
2543 }
2544
2545 fn nested_from(height: usize) -> FromClause {
2546 let mut from = FromClause {
2547 source: table_source("leaf"),
2548 joins: Vec::new(),
2549 };
2550 for _ in 0..height {
2551 from = FromClause {
2552 source: TableOrSubquery::ParenJoin(Box::new(from)),
2553 joins: Vec::new(),
2554 };
2555 }
2556 from
2557 }
2558
2559 fn update_with_from(from: FromClause) -> UpdateStatement {
2560 UpdateStatement {
2561 with: None,
2562 or_conflict: None,
2563 table: QualifiedTableRef {
2564 name: QualifiedName::bare("target"),
2565 alias: None,
2566 index_hint: None,
2567 time_travel: None,
2568 },
2569 assignments: vec![Assignment {
2570 target: AssignmentTarget::Column("x".to_owned()),
2571 value: integer(1),
2572 }],
2573 from: Some(from),
2574 where_clause: None,
2575 returning: Vec::new(),
2576 order_by: Vec::new(),
2577 limit: None,
2578 }
2579 }
2580
2581 fn drop_table(name: &str) -> Statement {
2582 Statement::Drop(DropStatement {
2583 object_type: DropObjectType::Table,
2584 if_exists: false,
2585 name: QualifiedName::bare(name),
2586 })
2587 }
2588
2589 fn drop_from_iteratively(root: FromClause) {
2590 let mut from_clauses = vec![root];
2591 let mut tables = Vec::new();
2592 while let Some(FromClause { source, joins }) = from_clauses.pop() {
2593 tables.push(source);
2594 for join in joins {
2595 tables.push(join.table);
2596 drop(join.constraint);
2597 }
2598 while let Some(table) = tables.pop() {
2599 match table {
2600 TableOrSubquery::ParenJoin(inner) => from_clauses.push(*inner),
2601 leaf => drop(leaf),
2602 }
2603 }
2604 }
2605 }
2606
2607 fn drop_table_iteratively(table: TableOrSubquery) {
2608 match table {
2609 TableOrSubquery::ParenJoin(inner) => drop_from_iteratively(*inner),
2610 leaf => drop(leaf),
2611 }
2612 }
2613
2614 fn drop_select_core_iteratively(core: SelectCore) {
2615 match core {
2616 SelectCore::Select { from, .. } => {
2617 if let Some(from) = from {
2618 drop_from_iteratively(from);
2619 }
2620 }
2621 SelectCore::Values(_) => {}
2622 }
2623 }
2624
2625 fn drop_select_body_iteratively(body: SelectBody) {
2626 drop_select_core_iteratively(body.select);
2627 for (_, core) in body.compounds {
2628 drop_select_core_iteratively(core);
2629 }
2630 }
2631
2632 fn drop_statement_iteratively(mut statement: Statement) {
2633 loop {
2634 match statement {
2635 Statement::Explain { stmt, .. } => statement = *stmt,
2636 Statement::CreateTrigger(mut trigger) if trigger.body.len() == 1 => {
2637 statement = trigger
2638 .body
2639 .pop()
2640 .expect("single trigger body statement must exist");
2641 }
2642 Statement::Update(mut update) => {
2643 if let Some(from) = update.from.take() {
2644 drop_from_iteratively(from);
2645 }
2646 break;
2647 }
2648 leaf => {
2649 drop(leaf);
2650 break;
2651 }
2652 }
2653 }
2654 }
2655
2656 fn drop_scalar_subquery_chain_iteratively(mut expr: Expr) {
2657 loop {
2658 match expr {
2659 Expr::Subquery(select, _) => {
2660 let SelectStatement {
2661 with,
2662 body,
2663 order_by,
2664 limit,
2665 } = *select;
2666 assert!(with.is_none());
2667 assert!(order_by.is_empty());
2668 assert!(limit.is_none());
2669 let SelectBody { select, compounds } = body;
2670 assert!(compounds.is_empty());
2671 let SelectCore::Select {
2672 mut columns,
2673 from,
2674 where_clause,
2675 group_by,
2676 having,
2677 windows,
2678 ..
2679 } = select
2680 else {
2681 panic!("scalar-subquery chain must contain SELECT cores");
2682 };
2683 assert!(from.is_none());
2684 assert!(where_clause.is_none());
2685 assert!(group_by.is_empty());
2686 assert!(having.is_none());
2687 assert!(windows.is_empty());
2688 assert_eq!(columns.len(), 1);
2689 let ResultColumn::Expr { expr: child, alias } = columns
2690 .pop()
2691 .expect("scalar-subquery SELECT must contain one column")
2692 else {
2693 panic!("scalar-subquery SELECT column must be an expression");
2694 };
2695 assert!(alias.is_none());
2696 expr = child;
2697 }
2698 leaf => {
2699 drop(leaf);
2700 break;
2701 }
2702 }
2703 }
2704 }
2705
2706 fn format_statement_on_one_mib_stack(statement: Statement) -> String {
2707 let (rendered, statement) = std::thread::Builder::new()
2708 .stack_size(1024 * 1024)
2709 .spawn(move || (statement.to_string(), statement))
2710 .expect("1 MiB formatter thread must spawn")
2711 .join()
2712 .expect("formatting on a 1 MiB stack must not overflow");
2713 drop_statement_iteratively(statement);
2714 rendered
2715 }
2716
2717 #[test]
2718 fn expression_task_stack_uses_inline_boundary_and_preserves_lifo() {
2719 let mut stack = ExprWriteTaskStack::new(ExprWriteTask::Text("first"));
2720 stack.push(ExprWriteTask::Text("second"));
2721 stack.push(ExprWriteTask::Text("third"));
2722 assert!(matches!(stack.pop(), Some(ExprWriteTask::Text("third"))));
2723 assert!(matches!(stack.pop(), Some(ExprWriteTask::Text("second"))));
2724 assert!(matches!(stack.pop(), Some(ExprWriteTask::Text("first"))));
2725
2726 let mut boundary = ExprWriteTaskStack::new(ExprWriteTask::Text("inline"));
2727 for _ in 1..INLINE_EXPR_WRITE_TASKS {
2728 boundary.push(ExprWriteTask::Text("inline"));
2729 }
2730 assert!(!boundary.tasks.spilled());
2731 boundary.push(ExprWriteTask::Text("spill"));
2732 assert!(boundary.tasks.spilled());
2733 while boundary.pop().is_some() {}
2734 assert!(
2735 boundary.stats().spilled,
2736 "spill history must survive draining the task stack"
2737 );
2738 }
2739
2740 #[test]
2741 fn formatter_error_resets_task_stack_stats() {
2742 struct FailingWriter;
2743
2744 impl fmt::Write for FailingWriter {
2745 fn write_str(&mut self, _: &str) -> fmt::Result {
2746 Err(fmt::Error)
2747 }
2748 }
2749
2750 let mut deep = integer(1);
2751 for _ in 1..100 {
2752 deep = binary(deep, BinaryOp::Add, integer(1));
2753 }
2754 let _ = deep.to_string();
2755 assert!(
2756 LAST_EXPR_WRITE_TASK_STACK_STATS
2757 .with(std::cell::Cell::get)
2758 .spilled
2759 );
2760
2761 let mut writer = FailingWriter;
2762 write!(&mut writer, "{}", column("value"))
2763 .expect_err("the test writer must reject formatter output");
2764 assert_eq!(
2765 LAST_EXPR_WRITE_TASK_STACK_STATS.with(std::cell::Cell::get),
2766 ExprWriteTaskStackStats::default(),
2767 "an early formatter error must not expose the previous call's task stats"
2768 );
2769 }
2770
2771 #[test]
2772 fn public_from_roots_format_height_1000_and_1001_on_one_mib_stack() {
2773 for height in [1000, 1001] {
2774 let from_root = nested_from(height);
2775 let table_root = TableOrSubquery::ParenJoin(Box::new(nested_from(height)));
2776 let join_root = JoinClause {
2777 join_type: JoinType {
2778 natural: false,
2779 kind: JoinKind::Inner,
2780 },
2781 table: TableOrSubquery::ParenJoin(Box::new(nested_from(height))),
2782 constraint: None,
2783 };
2784 let select_core_root = SelectCore::Select {
2785 distinct: Distinctness::All,
2786 columns: vec![ResultColumn::Star],
2787 from: Some(nested_from(height)),
2788 where_clause: None,
2789 group_by: Vec::new(),
2790 having: None,
2791 windows: Vec::new(),
2792 };
2793 let select_body_root = SelectBody {
2794 select: SelectCore::Select {
2795 distinct: Distinctness::All,
2796 columns: vec![ResultColumn::Star],
2797 from: Some(nested_from(height)),
2798 where_clause: None,
2799 group_by: Vec::new(),
2800 having: None,
2801 windows: Vec::new(),
2802 },
2803 compounds: Vec::new(),
2804 };
2805 let update_root = Statement::Update(update_with_from(nested_from(height)));
2806
2807 let (rendered, roots) = std::thread::Builder::new()
2808 .stack_size(1024 * 1024)
2809 .spawn(move || {
2810 let rendered = [
2811 from_root.to_string(),
2812 table_root.to_string(),
2813 join_root.to_string(),
2814 select_core_root.to_string(),
2815 select_body_root.to_string(),
2816 update_root.to_string(),
2817 ];
2818 (
2819 rendered,
2820 (
2821 from_root,
2822 table_root,
2823 join_root,
2824 select_core_root,
2825 select_body_root,
2826 update_root,
2827 ),
2828 )
2829 })
2830 .expect("1 MiB formatter thread must spawn")
2831 .join()
2832 .expect("all public FROM roots must format without stack overflow");
2833 let (
2834 from_root,
2835 table_root,
2836 JoinClause {
2837 table: join_table, ..
2838 },
2839 select_core_root,
2840 select_body_root,
2841 update_root,
2842 ) = roots;
2843 drop_from_iteratively(from_root);
2844 drop_table_iteratively(table_root);
2845 drop_table_iteratively(join_table);
2846 drop_select_core_iteratively(select_core_root);
2847 drop_select_body_iteratively(select_body_root);
2848 drop_statement_iteratively(update_root);
2849
2850 let expected_parentheses = [height, height + 1, height + 1, height, height, height];
2851 for (sql, expected) in rendered.iter().zip(expected_parentheses) {
2852 assert_eq!(sql.matches('(').count(), expected);
2853 assert_eq!(sql.matches(')').count(), expected);
2854 assert!(sql.contains("leaf"));
2855 }
2856 assert!(rendered[2].starts_with("INNER JOIN "));
2857 assert!(rendered[3].starts_with("SELECT * FROM "));
2858 assert!(rendered[4].starts_with("SELECT * FROM "));
2859 assert!(rendered[5].starts_with("UPDATE target SET x = 1 FROM "));
2860 }
2861 }
2862
2863 #[test]
2864 fn nested_explain_height_1000_and_1001_formats_on_one_mib_stack() {
2865 for height in [1000, 1001] {
2866 let mut statement = drop_table("leaf");
2867 for level in 0..height {
2868 statement = Statement::Explain {
2869 query_plan: level % 2 == 0,
2870 stmt: Box::new(statement),
2871 };
2872 }
2873
2874 let rendered = format_statement_on_one_mib_stack(statement);
2875 let mut tail = rendered.as_str();
2876 for level in (0..height).rev() {
2877 let prefix = if level % 2 == 0 {
2878 "EXPLAIN QUERY PLAN "
2879 } else {
2880 "EXPLAIN "
2881 };
2882 tail = tail
2883 .strip_prefix(prefix)
2884 .expect("EXPLAIN wrappers must retain their exact order");
2885 }
2886 assert_eq!(tail, "DROP TABLE leaf");
2887 }
2888 }
2889
2890 #[test]
2891 fn nested_trigger_body_height_1000_and_1001_formats_on_one_mib_stack() {
2892 for height in [1000, 1001] {
2893 let mut statement = drop_table("leaf");
2894 for level in 0..height {
2895 statement = Statement::CreateTrigger(CreateTriggerStatement {
2896 if_not_exists: false,
2897 temporary: false,
2898 name: QualifiedName::bare(format!("trigger_{level}")),
2899 timing: TriggerTiming::After,
2900 event: TriggerEvent::Insert,
2901 table: "target".to_owned(),
2902 for_each_row: false,
2903 when: None,
2904 body: vec![statement],
2905 });
2906 }
2907
2908 let rendered = format_statement_on_one_mib_stack(statement);
2909 assert_eq!(rendered.matches("CREATE TRIGGER ").count(), height);
2910 assert_eq!(rendered.matches("; END").count(), height);
2911 assert!(rendered.contains("DROP TABLE leaf"));
2912 assert!(rendered.ends_with("END"));
2913 }
2914 }
2915
2916 #[test]
2917 fn iterative_public_roots_preserve_shallow_sql() {
2918 let joined = FromClause {
2919 source: table_source("a"),
2920 joins: vec![JoinClause {
2921 join_type: JoinType {
2922 natural: false,
2923 kind: JoinKind::Inner,
2924 },
2925 table: table_source("b"),
2926 constraint: Some(JoinConstraint::On(binary(
2927 column("a_id"),
2928 BinaryOp::Eq,
2929 column("b_id"),
2930 ))),
2931 }],
2932 };
2933 assert_eq!(joined.to_string(), "a INNER JOIN b ON a_id = b_id");
2934
2935 let body = SelectBody {
2936 select: SelectCore::Select {
2937 distinct: Distinctness::All,
2938 columns: vec![ResultColumn::Star],
2939 from: Some(joined),
2940 where_clause: None,
2941 group_by: Vec::new(),
2942 having: None,
2943 windows: Vec::new(),
2944 },
2945 compounds: Vec::new(),
2946 };
2947 assert_eq!(
2948 body.to_string(),
2949 "SELECT * FROM a INNER JOIN b ON a_id = b_id"
2950 );
2951
2952 let compounds = SelectBody {
2953 select: SelectCore::Values(vec![vec![integer(1)]].into()),
2954 compounds: vec![
2955 (
2956 CompoundOp::UnionAll,
2957 SelectCore::Values(vec![vec![integer(2)]].into()),
2958 ),
2959 (
2960 CompoundOp::Except,
2961 SelectCore::Values(vec![vec![integer(3)]].into()),
2962 ),
2963 ],
2964 };
2965 assert_eq!(
2966 compounds.to_string(),
2967 "VALUES (1) UNION ALL VALUES (2) EXCEPT VALUES (3)"
2968 );
2969
2970 let update = UpdateStatement {
2971 with: None,
2972 or_conflict: None,
2973 table: QualifiedTableRef {
2974 name: QualifiedName::bare("target"),
2975 alias: None,
2976 index_hint: None,
2977 time_travel: None,
2978 },
2979 assignments: vec![Assignment {
2980 target: AssignmentTarget::Column("x".to_owned()),
2981 value: integer(1),
2982 }],
2983 from: Some(FromClause {
2984 source: table_source("source"),
2985 joins: Vec::new(),
2986 }),
2987 where_clause: Some(binary(column("id"), BinaryOp::Eq, integer(7))),
2988 returning: vec![ResultColumn::Expr {
2989 expr: column("x"),
2990 alias: Some("updated".to_owned()),
2991 }],
2992 order_by: vec![OrderingTerm {
2993 expr: column("id"),
2994 direction: Some(SortDirection::Desc),
2995 nulls: Some(NullsOrder::Last),
2996 }],
2997 limit: Some(LimitClause {
2998 limit: integer(10),
2999 offset: Some(integer(2)),
3000 }),
3001 };
3002 assert_eq!(
3003 update.to_string(),
3004 "UPDATE target SET x = 1 FROM source WHERE id = 7 RETURNING x AS updated \
3005 ORDER BY id DESC NULLS LAST LIMIT 10 OFFSET 2"
3006 );
3007
3008 let explained = Statement::Explain {
3009 query_plan: true,
3010 stmt: Box::new(drop_table("old")),
3011 };
3012 assert_eq!(explained.to_string(), "EXPLAIN QUERY PLAN DROP TABLE old");
3013
3014 let trigger = CreateTriggerStatement {
3015 if_not_exists: false,
3016 temporary: false,
3017 name: QualifiedName::bare("tr"),
3018 timing: TriggerTiming::After,
3019 event: TriggerEvent::Insert,
3020 table: "target".to_owned(),
3021 for_each_row: false,
3022 when: None,
3023 body: vec![drop_table("old"), drop_table("older")],
3024 };
3025 assert_eq!(
3026 trigger.to_string(),
3027 "CREATE TRIGGER tr AFTER INSERT ON target BEGIN \
3028 DROP TABLE old; DROP TABLE older; END"
3029 );
3030 }
3031
3032 #[test]
3033 fn representative_rich_select_stays_in_inline_task_stack() {
3034 let cte_query = SelectStatement {
3035 with: None,
3036 body: SelectBody {
3037 select: SelectCore::Values(vec![vec![integer(1)]].into()),
3038 compounds: Vec::new(),
3039 },
3040 order_by: Vec::new(),
3041 limit: None,
3042 };
3043 let select = SelectStatement {
3044 with: Some(WithClause {
3045 recursive: false,
3046 ctes: vec![Cte {
3047 name: "seed".to_owned(),
3048 columns: vec!["id".to_owned()],
3049 materialized: Some(CteMaterialized::NotMaterialized),
3050 query: cte_query,
3051 }],
3052 }),
3053 body: SelectBody {
3054 select: SelectCore::Select {
3055 distinct: Distinctness::Distinct,
3056 columns: vec![
3057 ResultColumn::Expr {
3058 expr: column("a_id"),
3059 alias: Some("id".to_owned()),
3060 },
3061 ResultColumn::Expr {
3062 expr: column("b_value"),
3063 alias: None,
3064 },
3065 ],
3066 from: Some(FromClause {
3067 source: table_source("a"),
3068 joins: vec![JoinClause {
3069 join_type: JoinType {
3070 natural: false,
3071 kind: JoinKind::Left,
3072 },
3073 table: table_source("b"),
3074 constraint: Some(JoinConstraint::On(binary(
3075 column("a_id"),
3076 BinaryOp::Eq,
3077 column("b_id"),
3078 ))),
3079 }],
3080 }),
3081 where_clause: Some(Box::new(binary(column("a_id"), BinaryOp::Gt, integer(0)))),
3082 group_by: vec![column("a_id")],
3083 having: Some(Box::new(binary(
3084 column("b_value"),
3085 BinaryOp::IsNot,
3086 Expr::Literal(Literal::Null, Span::ZERO),
3087 ))),
3088 windows: vec![WindowDef {
3089 name: "w".to_owned(),
3090 spec: WindowSpec {
3091 window_ref: None,
3092 partition_by: vec![column("a_id")],
3093 order_by: vec![OrderingTerm {
3094 expr: column("b_value"),
3095 direction: Some(SortDirection::Desc),
3096 nulls: None,
3097 }],
3098 frame: None,
3099 },
3100 }],
3101 },
3102 compounds: Vec::new(),
3103 },
3104 order_by: vec![OrderingTerm {
3105 expr: column("a_id"),
3106 direction: Some(SortDirection::Asc),
3107 nulls: Some(NullsOrder::First),
3108 }],
3109 limit: Some(LimitClause {
3110 limit: integer(25),
3111 offset: Some(integer(5)),
3112 }),
3113 };
3114
3115 assert_eq!(
3116 select.to_string(),
3117 "WITH seed(id) AS NOT MATERIALIZED (VALUES (1)) \
3118 SELECT DISTINCT a_id AS id, b_value FROM a \
3119 LEFT JOIN b ON a_id = b_id WHERE a_id > 0 GROUP BY a_id \
3120 HAVING b_value IS NOT NULL WINDOW w AS \
3121 (PARTITION BY a_id ORDER BY b_value DESC) \
3122 ORDER BY a_id ASC NULLS FIRST LIMIT 25 OFFSET 5"
3123 );
3124 let task_stats = LAST_EXPR_WRITE_TASK_STACK_STATS.with(std::cell::Cell::get);
3125 assert!(
3126 !task_stats.spilled,
3127 "representative rich SELECT should remain in the inline task stack"
3128 );
3129 assert!(task_stats.peak_len <= INLINE_EXPR_WRITE_TASKS);
3130 }
3131
3132 #[test]
3133 fn binary_operands_use_minimal_semantics_preserving_parentheses() {
3134 let tighter_right = binary(
3135 column("a"),
3136 BinaryOp::Add,
3137 binary(column("b"), BinaryOp::Multiply, integer(2)),
3138 );
3139 assert_eq!(tighter_right.to_string(), "a + b * 2");
3140
3141 let looser_right = binary(
3142 column("a"),
3143 BinaryOp::Multiply,
3144 binary(column("b"), BinaryOp::Add, column("c")),
3145 );
3146 assert_eq!(looser_right.to_string(), "a * (b + c)");
3147
3148 let left_associative = binary(
3149 binary(column("a"), BinaryOp::Subtract, column("b")),
3150 BinaryOp::Subtract,
3151 column("c"),
3152 );
3153 assert_eq!(left_associative.to_string(), "a - b - c");
3154
3155 let right_subtract = binary(
3156 column("a"),
3157 BinaryOp::Subtract,
3158 binary(column("b"), BinaryOp::Subtract, column("c")),
3159 );
3160 assert_eq!(right_subtract.to_string(), "a - (b - c)");
3161
3162 let right_divide = binary(
3163 column("a"),
3164 BinaryOp::Divide,
3165 binary(column("b"), BinaryOp::Divide, column("c")),
3166 );
3167 assert_eq!(right_divide.to_string(), "a / (b / c)");
3168
3169 let and_chain = binary(
3170 column("a"),
3171 BinaryOp::And,
3172 binary(column("b"), BinaryOp::And, column("c")),
3173 );
3174 assert_eq!(and_chain.to_string(), "a AND b AND c");
3175
3176 let or_chain = binary(
3177 column("a"),
3178 BinaryOp::Or,
3179 binary(column("b"), BinaryOp::Or, column("c")),
3180 );
3181 assert_eq!(or_chain.to_string(), "a OR b OR c");
3182 }
3183
3184 #[test]
3185 fn expression_display_height_1000_uses_bounded_work_stack() {
3186 let mut expr = Expr::Literal(Literal::Integer(1), Span::ZERO);
3187 for _ in 1..1000 {
3188 expr = Expr::BinaryOp {
3189 left: Box::new(expr),
3190 op: BinaryOp::Add,
3191 right: Box::new(Expr::Literal(Literal::Integer(1), Span::ZERO)),
3192 span: Span::ZERO,
3193 };
3194 }
3195
3196 let rendered = expr.to_string();
3197 let task_stats = LAST_EXPR_WRITE_TASK_STACK_STATS.with(std::cell::Cell::get);
3198 assert!(
3199 task_stats.spilled,
3200 "height-1000 expression should exercise the heap spill path"
3201 );
3202 assert!(task_stats.peak_len > INLINE_EXPR_WRITE_TASKS);
3203 assert_eq!(rendered.matches('+').count(), 999);
3204 assert!(rendered.ends_with(" + 1"));
3205 }
3206
3207 #[test]
3208 fn scalar_subquery_display_height_1000_uses_one_mib_stack() {
3209 let mut expr = Expr::Literal(Literal::Integer(1), Span::ZERO);
3210 for _ in 1..1000 {
3211 expr = Expr::Subquery(
3212 Box::new(SelectStatement {
3213 with: None,
3214 body: SelectBody {
3215 select: SelectCore::Select {
3216 distinct: Distinctness::All,
3217 columns: vec![ResultColumn::Expr { expr, alias: None }],
3218 from: None,
3219 where_clause: None,
3220 group_by: Vec::new(),
3221 having: None,
3222 windows: Vec::new(),
3223 },
3224 compounds: Vec::new(),
3225 },
3226 order_by: Vec::new(),
3227 limit: None,
3228 }),
3229 Span::ZERO,
3230 );
3231 }
3232
3233 let (rendered, expr) = std::thread::Builder::new()
3234 .stack_size(1024 * 1024)
3235 .spawn(move || {
3236 let rendered = expr.to_string();
3237 (rendered, expr)
3238 })
3239 .expect("1 MiB formatter thread must spawn")
3240 .join()
3241 .expect("height-1000 scalar subquery formatting must not overflow");
3242 drop_scalar_subquery_chain_iteratively(expr);
3243 assert_eq!(rendered.matches("(SELECT ").count(), 999);
3244 assert!(rendered.ends_with(&")".repeat(999)));
3245 }
3246
3247 #[test]
3248 fn negative_literal_operands_cannot_merge_into_sql_comments() {
3249 let integer = Expr::UnaryOp {
3250 op: UnaryOp::Negate,
3251 expr: Box::new(Expr::Literal(Literal::Integer(i64::MIN), Span::ZERO)),
3252 span: Span::ZERO,
3253 };
3254 assert_eq!(integer.to_string(), "-(-9223372036854775808)");
3255
3256 let negative_zero = Expr::UnaryOp {
3257 op: UnaryOp::Negate,
3258 expr: Box::new(Expr::Literal(Literal::Float(-0.0), Span::ZERO)),
3259 span: Span::ZERO,
3260 };
3261 assert_eq!(negative_zero.to_string(), "-(-0.0)");
3262
3263 let bound = Expr::UnaryOp {
3264 op: UnaryOp::Negate,
3265 expr: Box::new(Expr::BoundOuterValue {
3266 value: SqliteValue::Integer(-1),
3267 collation: BoundCollation::Unspecified,
3268 affinity: None,
3269 span: Span::ZERO,
3270 }),
3271 span: Span::ZERO,
3272 };
3273 assert_eq!(bound.to_string(), "-(-1)");
3274 }
3275
3276 #[test]
3277 fn like_escape_operand_preserves_comparison_grouping() {
3278 let expr = Expr::Like {
3279 expr: Box::new(column("value")),
3280 pattern: Box::new(column("pattern")),
3281 escape: Some(Box::new(binary(
3282 column("lower"),
3283 BinaryOp::Lt,
3284 column("upper"),
3285 ))),
3286 op: LikeOp::Like,
3287 not: false,
3288 span: Span::ZERO,
3289 };
3290 assert_eq!(
3291 expr.to_string(),
3292 "value LIKE pattern ESCAPE (lower < upper)"
3293 );
3294 }
3295
3296 #[test]
3297 fn bound_outer_values_render_as_safe_literals_without_metadata() {
3298 let bound = |value| Expr::BoundOuterValue {
3299 value,
3300 collation: BoundCollation::Named("NOCASE".to_owned()),
3301 affinity: Some(fsqlite_types::TypeAffinity::Text),
3302 span: Span::new(7, 19),
3303 };
3304
3305 assert_eq!(bound(SqliteValue::Null).to_string(), "NULL");
3306 assert_eq!(bound(SqliteValue::Integer(-7)).to_string(), "-7");
3307 assert_eq!(bound(SqliteValue::Float(1.0)).to_string(), "1.0");
3308 assert_eq!(
3309 bound(SqliteValue::Text("O'Brien".into())).to_string(),
3310 "'O''Brien'"
3311 );
3312 assert_eq!(
3313 bound(SqliteValue::Blob(vec![0x00, 0xAB].into())).to_string(),
3314 "X'00AB'"
3315 );
3316 }
3317
3318 #[test]
3319 fn infinite_float_literals_render_as_numeric_sql() {
3320 assert_eq!(Literal::Float(f64::INFINITY).to_string(), "9e999");
3321 assert_eq!(Literal::Float(f64::NEG_INFINITY).to_string(), "-9e999");
3322 assert_eq!(Literal::Float(f64::NAN).to_string(), "NULL");
3323 assert_eq!(Literal::Float(-f64::NAN).to_string(), "NULL");
3324 }
3325
3326 #[test]
3327 fn collation_names_use_identifier_quoting() {
3328 let expr = Expr::Collate {
3329 expr: Box::new(column("value")),
3330 collation: "my col".to_owned(),
3331 span: Span::ZERO,
3332 };
3333 assert_eq!(expr.to_string(), "value COLLATE \"my col\"");
3334 }
3335
3336 #[test]
3337 fn window_reference_form_is_preserved_exactly() {
3338 let extended = Expr::FunctionCall {
3339 name: "sum".to_owned(),
3340 args: FunctionArgs::List(vec![column("x")]),
3341 distinct: false,
3342 order_by: Vec::new(),
3343 filter: None,
3344 over: Some(WindowSpec {
3345 window_ref: Some(WindowReference::Base("base".to_owned())),
3346 partition_by: vec![column("p")],
3347 order_by: vec![OrderingTerm {
3348 expr: column("y"),
3349 direction: None,
3350 nulls: None,
3351 }],
3352 frame: Some(FrameSpec {
3353 frame_type: FrameType::Rows,
3354 start: FrameBound::Preceding(Box::new(column("z"))),
3355 end: Some(FrameBound::CurrentRow),
3356 exclude: None,
3357 }),
3358 }),
3359 span: Span::ZERO,
3360 };
3361 assert_eq!(
3362 extended.to_string(),
3363 "sum(x) OVER (base PARTITION BY p ORDER BY y ROWS BETWEEN z PRECEDING AND CURRENT ROW)"
3364 );
3365 let task_stats = LAST_EXPR_WRITE_TASK_STACK_STATS.with(std::cell::Cell::get);
3366 assert!(
3367 !task_stats.spilled,
3368 "representative window expression should remain in the inline task stack"
3369 );
3370 assert!(task_stats.peak_len <= INLINE_EXPR_WRITE_TASKS);
3371
3372 let bare = Expr::FunctionCall {
3373 name: "sum".to_owned(),
3374 args: FunctionArgs::List(vec![column("x")]),
3375 distinct: false,
3376 order_by: Vec::new(),
3377 filter: None,
3378 over: Some(WindowSpec {
3379 window_ref: Some(WindowReference::Direct("base".to_owned())),
3380 partition_by: Vec::new(),
3381 order_by: Vec::new(),
3382 frame: None,
3383 }),
3384 span: Span::ZERO,
3385 };
3386 assert_eq!(bare.to_string(), "sum(x) OVER base");
3387 let Expr::FunctionCall {
3388 over: Some(bare_window),
3389 ..
3390 } = &bare
3391 else {
3392 panic!("bare window function should carry a window");
3393 };
3394 assert_eq!(bare_window.to_string(), "base");
3395
3396 let parenthesized = Expr::FunctionCall {
3397 name: "sum".to_owned(),
3398 args: FunctionArgs::List(vec![column("x")]),
3399 distinct: false,
3400 order_by: Vec::new(),
3401 filter: None,
3402 over: Some(WindowSpec {
3403 window_ref: Some(WindowReference::Base("base".to_owned())),
3404 partition_by: Vec::new(),
3405 order_by: Vec::new(),
3406 frame: None,
3407 }),
3408 span: Span::ZERO,
3409 };
3410 assert_eq!(parenthesized.to_string(), "sum(x) OVER (base)");
3411 let Expr::FunctionCall {
3412 over: Some(parenthesized_window),
3413 ..
3414 } = &parenthesized
3415 else {
3416 panic!("parenthesized window function should carry a window");
3417 };
3418 assert_eq!(parenthesized_window.to_string(), "(base)");
3419 }
3420}