tank_core/writer/
sql_writer.rs

1use crate::{
2    Action, BinaryOp, BinaryOpType, ColumnDef, ColumnRef, DataSet, EitherIterator, Entity,
3    Expression, Fragment, Interval, Join, JoinType, Operand, Order, Ordered, PrimaryKeyType,
4    TableRef, UnaryOp, UnaryOpType, Value, possibly_parenthesized, print_timer, separated_by,
5    writer::Context,
6};
7use core::f64;
8use futures::future::Either;
9use std::{
10    collections::{BTreeMap, HashMap},
11    fmt::Write,
12};
13use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
14use uuid::Uuid;
15
16macro_rules! write_integer {
17    ($out:ident, $value:expr, $delim:expr) => {{
18        $out.push_str($delim);
19        let mut buffer = itoa::Buffer::new();
20        $out.push_str(buffer.format($value));
21        $out.push_str($delim);
22    }};
23}
24macro_rules! write_float {
25    ($this:ident, $context:ident,$out:ident, $value:expr, $delim:expr) => {{
26        if $value.is_infinite() {
27            $this.write_value_infinity($context, $out, $value.is_sign_negative());
28        } else if $value.is_nan() {
29            $this.write_value_nan($context, $out);
30        } else {
31            $out.push_str($delim);
32            let mut buffer = ryu::Buffer::new();
33            $out.push_str(buffer.format($value));
34            $out.push_str($delim);
35        }
36    }};
37}
38
39/// Dialect printer converting semantic constructs into concrete SQL strings.
40pub trait SqlWriter: Send {
41    fn as_dyn(&self) -> &dyn SqlWriter;
42
43    /// Whether the current fragment context allows alias declaration.
44    fn alias_declaration(&self, context: &mut Context) -> bool {
45        match context.fragment {
46            Fragment::SqlSelectFrom | Fragment::SqlJoin => true,
47            _ => false,
48        }
49    }
50
51    /// Escape occurrences of `search` char with `replace` while copying into buffer.
52    fn write_escaped(
53        &self,
54        _context: &mut Context,
55        out: &mut String,
56        value: &str,
57        search: char,
58        replace: &str,
59    ) {
60        let mut position = 0;
61        for (i, c) in value.char_indices() {
62            if c == search {
63                out.push_str(&value[position..i]);
64                out.push_str(replace);
65                position = i + 1;
66            }
67        }
68        out.push_str(&value[position..]);
69    }
70
71    /// Quote identifiers ("name") doubling inner quotes.
72    fn write_identifier_quoted(&self, context: &mut Context, out: &mut String, value: &str) {
73        out.push('"');
74        self.write_escaped(context, out, value, '"', "\"\"");
75        out.push('"');
76    }
77
78    /// Render a table reference with optional alias.
79    fn write_table_ref(&self, context: &mut Context, out: &mut String, value: &TableRef) {
80        if self.alias_declaration(context) || value.alias.is_empty() {
81            if !value.schema.is_empty() {
82                self.write_identifier_quoted(context, out, &value.schema);
83                out.push('.');
84            }
85            self.write_identifier_quoted(context, out, &value.name);
86        }
87        if !value.alias.is_empty() {
88            let _ = write!(out, " {}", value.alias);
89        }
90    }
91
92    /// Render a column reference optionally qualifying with schema/table.
93    fn write_column_ref(&self, context: &mut Context, out: &mut String, value: &ColumnRef) {
94        if context.qualify_columns && !value.table.is_empty() {
95            if !value.schema.is_empty() {
96                self.write_identifier_quoted(context, out, &value.schema);
97                out.push('.');
98            }
99            self.write_identifier_quoted(context, out, &value.table);
100            out.push('.');
101        }
102        self.write_identifier_quoted(context, out, &value.name);
103    }
104
105    /// Render the SQL overridden type.
106    fn write_column_overridden_type(
107        &self,
108        _context: &mut Context,
109        out: &mut String,
110        _column: &ColumnDef,
111        types: &BTreeMap<&'static str, &'static str>,
112    ) {
113        if let Some(t) = types
114            .iter()
115            .find_map(|(k, v)| if *k == "" { Some(v) } else { None })
116        {
117            out.push_str(t);
118        }
119    }
120
121    /// Render the SQL type for a `Value` prototype.
122    fn write_column_type(&self, context: &mut Context, out: &mut String, value: &Value) {
123        match value {
124            Value::Boolean(..) => out.push_str("BOOLEAN"),
125            Value::Int8(..) => out.push_str("TINYINT"),
126            Value::Int16(..) => out.push_str("SMALLINT"),
127            Value::Int32(..) => out.push_str("INTEGER"),
128            Value::Int64(..) => out.push_str("BIGINT"),
129            Value::Int128(..) => out.push_str("HUGEINT"),
130            Value::UInt8(..) => out.push_str("UTINYINT"),
131            Value::UInt16(..) => out.push_str("USMALLINT"),
132            Value::UInt32(..) => out.push_str("UINTEGER"),
133            Value::UInt64(..) => out.push_str("UBIGINT"),
134            Value::UInt128(..) => out.push_str("UHUGEINT"),
135            Value::Float32(..) => out.push_str("FLOAT"),
136            Value::Float64(..) => out.push_str("DOUBLE"),
137            Value::Decimal(.., precision, scale) => {
138                out.push_str("DECIMAL");
139                if (precision, scale) != (&0, &0) {
140                    let _ = write!(out, "({precision},{scale})");
141                }
142            }
143            Value::Char(..) => out.push_str("CHAR(1)"),
144            Value::Varchar(..) => out.push_str("VARCHAR"),
145            Value::Blob(..) => out.push_str("BLOB"),
146            Value::Date(..) => out.push_str("DATE"),
147            Value::Time(..) => out.push_str("TIME"),
148            Value::Timestamp(..) => out.push_str("TIMESTAMP"),
149            Value::TimestampWithTimezone(..) => out.push_str("TIMESTAMPTZ"),
150            Value::Interval(..) => out.push_str("INTERVAL"),
151            Value::Uuid(..) => out.push_str("UUID"),
152            Value::Array(.., inner, size) => {
153                self.write_column_type(context, out, inner);
154                let _ = write!(out, "[{size}]");
155            }
156            Value::List(.., inner) => {
157                self.write_column_type(context, out, inner);
158                out.push_str("[]");
159            }
160            Value::Map(.., key, value) => {
161                out.push_str("MAP(");
162                self.write_column_type(context, out, key);
163                out.push(',');
164                self.write_column_type(context, out, value);
165                out.push(')');
166            }
167            Value::Json(..) => out.push_str("JSON"),
168            _ => log::error!(
169                "Unexpected tank::Value, variant {:?} is not supported",
170                value
171            ),
172        };
173    }
174
175    /// Render a concrete value (including proper quoting / escaping).
176    fn write_value(&self, context: &mut Context, out: &mut String, value: &Value) {
177        let delimiter = if context.fragment == Fragment::JsonKey {
178            "\""
179        } else {
180            ""
181        };
182        match value {
183            v if v.is_null() => self.write_value_none(context, out),
184            Value::Boolean(Some(v), ..) => self.write_value_bool(context, out, *v),
185            Value::Int8(Some(v), ..) => write_integer!(out, *v, delimiter),
186            Value::Int16(Some(v), ..) => write_integer!(out, *v, delimiter),
187            Value::Int32(Some(v), ..) => write_integer!(out, *v, delimiter),
188            Value::Int64(Some(v), ..) => write_integer!(out, *v, delimiter),
189            Value::Int128(Some(v), ..) => write_integer!(out, *v, delimiter),
190            Value::UInt8(Some(v), ..) => write_integer!(out, *v, delimiter),
191            Value::UInt16(Some(v), ..) => write_integer!(out, *v, delimiter),
192            Value::UInt32(Some(v), ..) => write_integer!(out, *v, delimiter),
193            Value::UInt64(Some(v), ..) => write_integer!(out, *v, delimiter),
194            Value::UInt128(Some(v), ..) => write_integer!(out, *v, delimiter),
195            Value::Float32(Some(v), ..) => write_float!(self, context, out, *v, delimiter),
196            Value::Float64(Some(v), ..) => write_float!(self, context, out, *v, delimiter),
197            Value::Decimal(Some(v), ..) => drop(write!(out, "{delimiter}{v}{delimiter}")),
198            Value::Char(Some(v), ..) => {
199                let mut buf = [0u8; 4];
200                self.write_value_string(context, out, v.encode_utf8(&mut buf));
201            }
202            Value::Varchar(Some(v), ..) => self.write_value_string(context, out, v),
203            Value::Blob(Some(v), ..) => self.write_value_blob(context, out, v.as_ref()),
204            Value::Date(Some(v), ..) => self.write_value_date(context, out, v, false),
205            Value::Time(Some(v), ..) => self.write_value_time(context, out, v, false),
206            Value::Timestamp(Some(v), ..) => self.write_value_timestamp(context, out, v),
207            Value::TimestampWithTimezone(Some(v), ..) => {
208                self.write_value_timestamptz(context, out, v)
209            }
210            Value::Interval(Some(v), ..) => self.write_value_interval(context, out, v),
211            Value::Uuid(Some(v), ..) => self.write_value_uuid(context, out, v),
212            Value::Array(Some(..), elem_ty, ..) | Value::List(Some(..), elem_ty, ..) => match value
213            {
214                Value::Array(Some(v), ..) => {
215                    self.write_value_list(context, out, Either::Left(v), value, &*elem_ty)
216                }
217                Value::List(Some(v), ..) => {
218                    self.write_value_list(context, out, Either::Right(v), value, &*elem_ty)
219                }
220                _ => unreachable!(),
221            },
222            Value::Map(Some(v), ..) => self.write_value_map(context, out, v),
223            Value::Struct(Some(v), ..) => self.write_value_struct(context, out, v),
224            _ => {
225                log::error!("Cannot write {:?}", value);
226            }
227        };
228    }
229
230    /// Render NULL literal.
231    fn write_value_none(&self, context: &mut Context, out: &mut String) {
232        out.push_str(if context.fragment == Fragment::Json {
233            "null"
234        } else {
235            "NULL"
236        });
237    }
238
239    /// Render boolean literal.
240    fn write_value_bool(&self, context: &mut Context, out: &mut String, value: bool) {
241        if context.fragment == Fragment::JsonKey {
242            out.push('"');
243        }
244        out.push_str(["false", "true"][value as usize]);
245        if context.fragment == Fragment::JsonKey {
246            out.push('"');
247        }
248    }
249
250    /// Render +/- INF via CAST for dialect portability.
251    fn write_value_infinity(&self, context: &mut Context, out: &mut String, negative: bool) {
252        let mut buffer = ryu::Buffer::new();
253        self.write_expression_binary_op(
254            context,
255            out,
256            &BinaryOp {
257                op: BinaryOpType::Cast,
258                lhs: &Operand::LitStr(buffer.format(if negative {
259                    f64::NEG_INFINITY
260                } else {
261                    f64::INFINITY
262                })),
263                rhs: &Operand::Type(Value::Float64(None)),
264            },
265        );
266    }
267
268    /// Render NaN via CAST for dialect portability.
269    fn write_value_nan(&self, context: &mut Context, out: &mut String) {
270        let mut buffer = ryu::Buffer::new();
271        self.write_expression_binary_op(
272            context,
273            out,
274            &BinaryOp {
275                op: BinaryOpType::Cast,
276                lhs: &Operand::LitStr(buffer.format(f64::NAN)),
277                rhs: &Operand::Type(Value::Float64(None)),
278            },
279        );
280    }
281
282    /// Render and escape a string literal using single quotes.
283    fn write_value_string(&self, context: &mut Context, out: &mut String, value: &str) {
284        let (delimiter, escaped) =
285            if context.fragment == Fragment::Json || context.fragment == Fragment::JsonKey {
286                ('"', r#"\""#)
287            } else {
288                ('\'', "''")
289            };
290        out.push(delimiter);
291        let mut pos = 0;
292        for (i, c) in value.char_indices() {
293            if c == delimiter {
294                out.push_str(&value[pos..i]);
295                out.push_str(escaped);
296                pos = i + 1;
297            } else if c == '\n' {
298                out.push_str(&value[pos..i]);
299                out.push_str("\\n");
300                pos = i + 1;
301            }
302        }
303        out.push_str(&value[pos..]);
304        out.push(delimiter);
305    }
306
307    /// Render a blob literal using hex escapes.
308    fn write_value_blob(&self, context: &mut Context, out: &mut String, value: &[u8]) {
309        let delimiter = if context.fragment == Fragment::Json {
310            '"'
311        } else {
312            '\''
313        };
314        out.push(delimiter);
315        for v in value {
316            let _ = write!(out, "\\x{:X}", v);
317        }
318        out.push(delimiter);
319    }
320
321    /// Render a DATE literal (optionally as part of TIMESTAMP composition).
322    fn write_value_date(
323        &self,
324        context: &mut Context,
325        out: &mut String,
326        value: &Date,
327        timestamp: bool,
328    ) {
329        let b = match context.fragment {
330            Fragment::Json if !timestamp => "\"",
331            _ if !timestamp => "'",
332            _ => "",
333        };
334        let _ = write!(
335            out,
336            "{b}{:04}-{:02}-{:02}{b}",
337            value.year(),
338            value.month() as u8,
339            value.day()
340        );
341    }
342
343    /// Render a TIME literal (optionally as part of TIMESTAMP composition).
344    fn write_value_time(
345        &self,
346        context: &mut Context,
347        out: &mut String,
348        value: &Time,
349        timestamp: bool,
350    ) {
351        print_timer(
352            out,
353            match context.fragment {
354                Fragment::Json if !timestamp => "\"",
355                _ if !timestamp => "'",
356                _ => "",
357            },
358            value.hour() as _,
359            value.minute(),
360            value.second(),
361            value.nanosecond(),
362        );
363    }
364
365    /// Render a TIMESTAMP literal.
366    fn write_value_timestamp(
367        &self,
368        context: &mut Context,
369        out: &mut String,
370        value: &PrimitiveDateTime,
371    ) {
372        let delimiter = match context.fragment {
373            Fragment::ParameterBinding => "",
374            Fragment::Json => "\"",
375            _ => "'",
376        };
377        out.push_str(delimiter);
378        self.write_value_date(context, out, &value.date(), true);
379        out.push('T');
380        self.write_value_time(context, out, &value.time(), true);
381        out.push_str(delimiter);
382    }
383
384    /// Render a TIMESTAMPTZ literal.
385    fn write_value_timestamptz(
386        &self,
387        context: &mut Context,
388        out: &mut String,
389        value: &OffsetDateTime,
390    ) {
391        let date_time = value.to_utc();
392        self.write_value_timestamp(
393            context,
394            out,
395            &PrimitiveDateTime::new(date_time.date(), date_time.time()),
396        );
397    }
398
399    /// Ordered units used to decompose intervals.
400    fn value_interval_units(&self) -> &[(&str, i128)] {
401        static UNITS: &[(&str, i128)] = &[
402            ("DAY", Interval::NANOS_IN_DAY),
403            ("HOUR", Interval::NANOS_IN_SEC * 3600),
404            ("MINUTE", Interval::NANOS_IN_SEC * 60),
405            ("SECOND", Interval::NANOS_IN_SEC),
406            ("MICROSECOND", 1_000),
407            ("NANOSECOND", 1),
408        ];
409        UNITS
410    }
411
412    /// Render INTERVAL literal using largest representative units.
413    fn write_value_interval(&self, context: &mut Context, out: &mut String, value: &Interval) {
414        out.push_str("INTERVAL ");
415        let delimiter = if context.fragment == Fragment::Json {
416            '"'
417        } else {
418            '\''
419        };
420        out.push(delimiter);
421        if value.is_zero() {
422            out.push_str("0 SECONDS");
423        }
424        macro_rules! write_unit {
425            ($out:ident, $len:ident, $val:expr, $unit:expr) => {
426                if $out.len() > $len {
427                    $out.push(' ');
428                    $len = $out.len();
429                }
430                let _ = write!(
431                    $out,
432                    "{} {}{}",
433                    $val,
434                    $unit,
435                    if $val != 1 { "S" } else { "" }
436                );
437            };
438        }
439        let mut months = value.months;
440        let mut nanos = value.nanos + value.days as i128 * Interval::NANOS_IN_DAY;
441        let mut len = out.len();
442        if months != 0 {
443            if months > 48 || months % 12 == 0 {
444                write_unit!(out, len, months / 12, "YEAR");
445                months = months % 12;
446            }
447            if months != 0 {
448                write_unit!(out, len, months, "MONTH");
449            }
450        }
451        for &(name, factor) in self.value_interval_units() {
452            let rem = nanos % factor;
453            if rem == 0 || factor / rem > 1_000_000 {
454                let value = nanos / factor;
455                if value != 0 {
456                    write_unit!(out, len, value, name);
457                    nanos = rem;
458                    if nanos == 0 {
459                        break;
460                    }
461                }
462            }
463        }
464        out.push(delimiter);
465    }
466
467    /// Render UUID literal.
468    fn write_value_uuid(&self, context: &mut Context, out: &mut String, value: &Uuid) {
469        let b = if context.is_inside_json() { '"' } else { '\'' };
470        let _ = write!(out, "{b}{value}{b}");
471    }
472
473    /// Render list/array literal.
474    fn write_value_list(
475        &self,
476        context: &mut Context,
477        out: &mut String,
478        value: Either<&Box<[Value]>, &Vec<Value>>,
479        _ty: &Value,
480        _elem_ty: &Value,
481    ) {
482        out.push('[');
483        separated_by(
484            out,
485            match value {
486                Either::Left(v) => v.iter(),
487                Either::Right(v) => v.iter(),
488            },
489            |out, v| {
490                self.write_value(context, out, v);
491            },
492            ",",
493        );
494        out.push(']');
495    }
496
497    /// Render map literal.
498    fn write_value_map(
499        &self,
500        context: &mut Context,
501        out: &mut String,
502        value: &HashMap<Value, Value>,
503    ) {
504        out.push('{');
505        separated_by(
506            out,
507            value,
508            |out, (k, v)| {
509                self.write_value(context, out, k);
510                out.push(':');
511                self.write_value(context, out, v);
512            },
513            ",",
514        );
515        out.push('}');
516    }
517
518    /// Render struct literal.
519    fn write_value_struct(
520        &self,
521        context: &mut Context,
522        out: &mut String,
523        value: &Vec<(String, Value)>,
524    ) {
525        out.push('{');
526        separated_by(
527            out,
528            value,
529            |out, (k, v)| {
530                self.write_value_string(context, out, k);
531                out.push(':');
532                self.write_value(context, out, v);
533            },
534            ",",
535        );
536        out.push('}');
537    }
538
539    /// Precedence table for unary operators.
540    fn expression_unary_op_precedence(&self, value: &UnaryOpType) -> i32 {
541        match value {
542            UnaryOpType::Negative => 1250,
543            UnaryOpType::Not => 250,
544        }
545    }
546
547    /// Precedence table for binary operators.
548    fn expression_binary_op_precedence(&self, value: &BinaryOpType) -> i32 {
549        match value {
550            BinaryOpType::Or => 100,
551            BinaryOpType::And => 200,
552            BinaryOpType::Equal => 300,
553            BinaryOpType::NotEqual => 300,
554            BinaryOpType::Less => 300,
555            BinaryOpType::Greater => 300,
556            BinaryOpType::LessEqual => 300,
557            BinaryOpType::GreaterEqual => 300,
558            BinaryOpType::In => 400,
559            BinaryOpType::NotIn => 400,
560            BinaryOpType::Is => 400,
561            BinaryOpType::IsNot => 400,
562            BinaryOpType::Like => 400,
563            BinaryOpType::NotLike => 400,
564            BinaryOpType::Regexp => 400,
565            BinaryOpType::NotRegexp => 400,
566            BinaryOpType::Glob => 400,
567            BinaryOpType::NotGlob => 400,
568            BinaryOpType::BitwiseOr => 500,
569            BinaryOpType::BitwiseAnd => 600,
570            BinaryOpType::ShiftLeft => 700,
571            BinaryOpType::ShiftRight => 700,
572            BinaryOpType::Subtraction => 800,
573            BinaryOpType::Addition => 800,
574            BinaryOpType::Multiplication => 900,
575            BinaryOpType::Division => 900,
576            BinaryOpType::Remainder => 900,
577            BinaryOpType::Indexing => 1000,
578            BinaryOpType::Cast => 1100,
579            BinaryOpType::Alias => 1200,
580        }
581    }
582
583    /// Render an operand (literal / variable / nested expression).
584    fn write_expression_operand(&self, context: &mut Context, out: &mut String, value: &Operand) {
585        let delimiter = if context.fragment == Fragment::JsonKey {
586            "\""
587        } else {
588            ""
589        };
590        match value {
591            Operand::LitBool(v) => self.write_value_bool(context, out, *v),
592            Operand::LitFloat(v) => write_float!(self, context, out, *v, delimiter),
593            Operand::LitIdent(v) => drop(out.push_str(v)),
594            Operand::LitField(v) => separated_by(out, *v, |out, v| out.push_str(v), "."),
595            Operand::LitInt(v) => write_integer!(out, *v, delimiter),
596            Operand::LitStr(v) => self.write_value_string(context, out, v),
597            Operand::LitArray(v) => {
598                out.push('[');
599                separated_by(
600                    out,
601                    *v,
602                    |out, op| {
603                        op.write_query(self.as_dyn(), context, out);
604                    },
605                    ", ",
606                );
607                out.push(']');
608            }
609            Operand::LitTuple(v) => {
610                out.push('(');
611                separated_by(
612                    out,
613                    *v,
614                    |out, op| {
615                        op.write_query(self.as_dyn(), context, out);
616                    },
617                    ", ",
618                );
619                out.push(')');
620            }
621            Operand::Null => drop(out.push_str("NULL")),
622            Operand::Type(v) => self.write_column_type(context, out, v),
623            Operand::Variable(v) => self.write_value(context, out, v),
624            Operand::Call(f, args) => {
625                out.push_str(f);
626                out.push('(');
627                separated_by(
628                    out,
629                    *args,
630                    |out, expr| {
631                        expr.write_query(self.as_dyn(), context, out);
632                    },
633                    ",",
634                );
635                out.push(')');
636            }
637            Operand::Asterisk => drop(out.push('*')),
638            Operand::QuestionMark => self.write_expression_operand_question_mark(context, out),
639        };
640    }
641
642    /// Render parameter placeholder (dialect may override).
643    fn write_expression_operand_question_mark(&self, _context: &mut Context, out: &mut String) {
644        out.push('?');
645    }
646
647    /// Render unary operator expression.
648    fn write_expression_unary_op(
649        &self,
650        context: &mut Context,
651        out: &mut String,
652        value: &UnaryOp<&dyn Expression>,
653    ) {
654        match value.op {
655            UnaryOpType::Negative => out.push('-'),
656            UnaryOpType::Not => out.push_str("NOT "),
657        };
658        possibly_parenthesized!(
659            out,
660            value.arg.precedence(self.as_dyn()) <= self.expression_unary_op_precedence(&value.op),
661            value.arg.write_query(self.as_dyn(), context, out)
662        );
663    }
664
665    /// Render binary operator expression handling precedence / parenthesis.
666    fn write_expression_binary_op(
667        &self,
668        context: &mut Context,
669        out: &mut String,
670        value: &BinaryOp<&dyn Expression, &dyn Expression>,
671    ) {
672        let (prefix, infix, suffix, lhs_parenthesized, rhs_parenthesized) = match value.op {
673            BinaryOpType::Indexing => ("", "[", "]", false, true),
674            BinaryOpType::Cast => ("CAST(", " AS ", ")", true, true),
675            BinaryOpType::Multiplication => ("", " * ", "", false, false),
676            BinaryOpType::Division => ("", " / ", "", false, false),
677            BinaryOpType::Remainder => ("", " % ", "", false, false),
678            BinaryOpType::Addition => ("", " + ", "", false, false),
679            BinaryOpType::Subtraction => ("", " - ", "", false, false),
680            BinaryOpType::ShiftLeft => ("", " << ", "", false, false),
681            BinaryOpType::ShiftRight => ("", " >> ", "", false, false),
682            BinaryOpType::BitwiseAnd => ("", " & ", "", false, false),
683            BinaryOpType::BitwiseOr => ("", " | ", "", false, false),
684            BinaryOpType::In => ("", " IN ", "", false, false),
685            BinaryOpType::NotIn => ("", " NOT IN ", "", false, false),
686            BinaryOpType::Is => ("", " IS ", "", false, false),
687            BinaryOpType::IsNot => ("", " IS NOT ", "", false, false),
688            BinaryOpType::Like => ("", " LIKE ", "", false, false),
689            BinaryOpType::NotLike => ("", " NOT LIKE ", "", false, false),
690            BinaryOpType::Regexp => ("", " REGEXP ", "", false, false),
691            BinaryOpType::NotRegexp => ("", " NOT REGEXP ", "", false, false),
692            BinaryOpType::Glob => ("", " GLOB ", "", false, false),
693            BinaryOpType::NotGlob => ("", " NOT GLOB ", "", false, false),
694            BinaryOpType::Equal => ("", " = ", "", false, false),
695            BinaryOpType::NotEqual => ("", " != ", "", false, false),
696            BinaryOpType::Less => ("", " < ", "", false, false),
697            BinaryOpType::LessEqual => ("", " <= ", "", false, false),
698            BinaryOpType::Greater => ("", " > ", "", false, false),
699            BinaryOpType::GreaterEqual => ("", " >= ", "", false, false),
700            BinaryOpType::And => ("", " AND ", "", false, false),
701            BinaryOpType::Or => ("", " OR ", "", false, false),
702            BinaryOpType::Alias => {
703                if context.fragment == Fragment::SqlSelectOrderBy {
704                    return value.lhs.write_query(self.as_dyn(), context, out);
705                } else {
706                    ("", " AS ", "", false, false)
707                }
708            }
709        };
710        let mut context = context.switch_fragment(if value.op == BinaryOpType::Cast {
711            Fragment::Casting
712        } else {
713            context.fragment
714        });
715        let precedence = self.expression_binary_op_precedence(&value.op);
716        out.push_str(prefix);
717        possibly_parenthesized!(
718            out,
719            !lhs_parenthesized && value.lhs.precedence(self.as_dyn()) < precedence,
720            value
721                .lhs
722                .write_query(self.as_dyn(), &mut context.current, out)
723        );
724        out.push_str(infix);
725        possibly_parenthesized!(
726            out,
727            !rhs_parenthesized && value.rhs.precedence(self.as_dyn()) <= precedence,
728            value
729                .rhs
730                .write_query(self.as_dyn(), &mut context.current, out)
731        );
732        out.push_str(suffix);
733    }
734
735    /// Render ordered expression inside ORDER BY.
736    fn write_expression_ordered(
737        &self,
738        context: &mut Context,
739        out: &mut String,
740        value: &Ordered<&dyn Expression>,
741    ) {
742        value.expression.write_query(self.as_dyn(), context, out);
743        if context.fragment == Fragment::SqlSelectOrderBy {
744            let _ = write!(
745                out,
746                " {}",
747                match value.order {
748                    Order::ASC => "ASC",
749                    Order::DESC => "DESC",
750                }
751            );
752        }
753    }
754
755    /// Render join keyword(s) for the given join type.
756    fn write_join_type(&self, _context: &mut Context, out: &mut String, join_type: &JoinType) {
757        out.push_str(match &join_type {
758            JoinType::Default => "JOIN",
759            JoinType::Inner => "INNER JOIN",
760            JoinType::Outer => "OUTER JOIN",
761            JoinType::Left => "LEFT JOIN",
762            JoinType::Right => "RIGHT JOIN",
763            JoinType::Cross => "CROSS",
764            JoinType::Natural => "NATURAL JOIN",
765        });
766    }
767
768    /// Render a JOIN clause.
769    fn write_join(
770        &self,
771        context: &mut Context,
772        out: &mut String,
773        join: &Join<&dyn DataSet, &dyn DataSet, &dyn Expression>,
774    ) {
775        let mut context = context.switch_fragment(Fragment::SqlJoin);
776        context.current.qualify_columns = true;
777        join.lhs
778            .write_query(self.as_dyn(), &mut context.current, out);
779        out.push(' ');
780        self.write_join_type(&mut context.current, out, &join.join);
781        out.push(' ');
782        join.rhs
783            .write_query(self.as_dyn(), &mut context.current, out);
784        if let Some(on) = &join.on {
785            out.push_str(" ON ");
786            on.write_query(self.as_dyn(), &mut context.current, out);
787        }
788    }
789
790    /// Emit BEGIN statement.
791    fn write_transaction_begin(&self, out: &mut String) {
792        out.push_str("BEGIN;");
793    }
794
795    /// Emit COMMIT statement.
796    fn write_transaction_commit(&self, out: &mut String) {
797        out.push_str("COMMIT;");
798    }
799
800    /// Emit ROLLBACK statement.
801    fn write_transaction_rollback(&self, out: &mut String) {
802        out.push_str("ROLLBACK;");
803    }
804
805    /// Emit CREATE SCHEMA.
806    fn write_create_schema<E>(&self, out: &mut String, if_not_exists: bool)
807    where
808        Self: Sized,
809        E: Entity,
810    {
811        out.reserve(32 + E::table().schema.len());
812        if !out.is_empty() {
813            out.push('\n');
814        }
815        out.push_str("CREATE SCHEMA ");
816        let mut context = Context::new(Fragment::SqlCreateSchema, E::qualified_columns());
817        if if_not_exists {
818            out.push_str("IF NOT EXISTS ");
819        }
820        self.write_identifier_quoted(&mut context, out, E::table().schema());
821        out.push(';');
822    }
823
824    /// Emit DROP SCHEMA.
825    fn write_drop_schema<E>(&self, out: &mut String, if_exists: bool)
826    where
827        Self: Sized,
828        E: Entity,
829    {
830        out.reserve(24 + E::table().schema.len());
831        if !out.is_empty() {
832            out.push('\n');
833        }
834        out.push_str("DROP SCHEMA ");
835        let mut context = Context::new(Fragment::SqlDropSchema, E::qualified_columns());
836        if if_exists {
837            out.push_str("IF EXISTS ");
838        }
839        self.write_identifier_quoted(&mut context, out, E::table().schema());
840        out.push(';');
841    }
842
843    /// Emit CREATE TABLE with columns, constraints & comments.
844    fn write_create_table<E>(&self, out: &mut String, if_not_exists: bool)
845    where
846        Self: Sized,
847        E: Entity,
848    {
849        let mut context = Context::new(Fragment::SqlCreateTable, E::qualified_columns());
850        let estimated = 128 + E::columns().len() * 64 + E::primary_key_def().len() * 24;
851        out.reserve(estimated);
852        if !out.is_empty() {
853            out.push('\n');
854        }
855        out.push_str("CREATE TABLE ");
856        if if_not_exists {
857            out.push_str("IF NOT EXISTS ");
858        }
859        self.write_table_ref(&mut context, out, E::table());
860        out.push_str(" (\n");
861        separated_by(
862            out,
863            E::columns(),
864            |out, col| {
865                self.write_create_table_column_fragment(&mut context, out, col);
866            },
867            ",\n",
868        );
869        let pk = E::primary_key_def();
870        if pk.len() > 1 {
871            self.write_create_table_primary_key_fragment(&mut context, out, pk.iter().map(|v| *v));
872        }
873        for unique in E::unique_defs() {
874            if unique.len() > 1 {
875                out.push_str(",\nUNIQUE (");
876                separated_by(
877                    out,
878                    unique,
879                    |out, col| {
880                        self.write_identifier_quoted(
881                            &mut context
882                                .switch_fragment(Fragment::SqlCreateTableUnique)
883                                .current,
884                            out,
885                            col.name(),
886                        );
887                    },
888                    ", ",
889                );
890                out.push(')');
891            }
892        }
893        let foreign_keys = E::columns().iter().filter(|c| c.references.is_some());
894        separated_by(
895            out,
896            foreign_keys,
897            |out, column| {
898                let references = column.references.unwrap();
899                out.push_str(",\nFOREIGN KEY (");
900                self.write_identifier_quoted(&mut context, out, &column.name());
901                out.push_str(") REFERENCES ");
902                self.write_table_ref(&mut context, out, &references.table());
903                out.push('(');
904                self.write_column_ref(&mut context, out, &references);
905                out.push(')');
906                if let Some(on_delete) = &column.on_delete {
907                    out.push_str(" ON DELETE ");
908                    self.write_create_table_references_action(&mut context, out, on_delete);
909                }
910                if let Some(on_update) = &column.on_update {
911                    out.push_str(" ON UPDATE ");
912                    self.write_create_table_references_action(&mut context, out, on_update);
913                }
914            },
915            "",
916        );
917        out.push_str(");");
918        self.write_column_comments_statements::<E>(&mut context, out);
919    }
920
921    /// Emit single column definition fragment.
922    fn write_create_table_column_fragment(
923        &self,
924        context: &mut Context,
925        out: &mut String,
926        column: &ColumnDef,
927    ) where
928        Self: Sized,
929    {
930        self.write_identifier_quoted(context, out, &column.name());
931        out.push(' ');
932        let len = out.len();
933        self.write_column_overridden_type(context, out, column, &column.column_type);
934        let didnt_write_type = out.len() == len;
935        if didnt_write_type {
936            SqlWriter::write_column_type(self, context, out, &column.value);
937        }
938        if !column.nullable && column.primary_key == PrimaryKeyType::None {
939            out.push_str(" NOT NULL");
940        }
941        if column.default.is_set() {
942            out.push_str(" DEFAULT ");
943            column.default.write_query(self.as_dyn(), context, out);
944        }
945        if column.primary_key == PrimaryKeyType::PrimaryKey {
946            // Composite primary key will be printed elsewhere
947            out.push_str(" PRIMARY KEY");
948        }
949        if column.unique && column.primary_key != PrimaryKeyType::PrimaryKey {
950            out.push_str(" UNIQUE");
951        }
952        if !column.comment.is_empty() {
953            self.write_column_comment_inline(context, out, column);
954        }
955    }
956
957    fn write_create_table_primary_key_fragment<'a, It>(
958        &self,
959        context: &mut Context,
960        out: &mut String,
961        primary_key: It,
962    ) where
963        Self: Sized,
964        It: IntoIterator<Item = &'a ColumnDef>,
965        It::IntoIter: Clone,
966    {
967        out.push_str(",\nPRIMARY KEY (");
968        separated_by(
969            out,
970            primary_key,
971            |out, col| {
972                self.write_identifier_quoted(
973                    &mut context
974                        .switch_fragment(Fragment::SqlCreateTablePrimaryKey)
975                        .current,
976                    out,
977                    col.name(),
978                );
979            },
980            ", ",
981        );
982        out.push(')');
983    }
984
985    /// Emit referential action keyword.
986    fn write_create_table_references_action(
987        &self,
988        _context: &mut Context,
989        out: &mut String,
990        action: &Action,
991    ) {
992        out.push_str(match action {
993            Action::NoAction => "NO ACTION",
994            Action::Restrict => "RESTRICT",
995            Action::Cascade => "CASCADE",
996            Action::SetNull => "SET NULL",
997            Action::SetDefault => "SET DEFAULT",
998        });
999    }
1000
1001    fn write_column_comment_inline(
1002        &self,
1003        _context: &mut Context,
1004        _out: &mut String,
1005        _column: &ColumnDef,
1006    ) where
1007        Self: Sized,
1008    {
1009    }
1010
1011    /// Emit COMMENT ON COLUMN statements for columns carrying comments.
1012    fn write_column_comments_statements<E>(&self, context: &mut Context, out: &mut String)
1013    where
1014        Self: Sized,
1015        E: Entity,
1016    {
1017        let mut context = context.switch_fragment(Fragment::SqlCommentOnColumn);
1018        context.current.qualify_columns = true;
1019        for c in E::columns().iter().filter(|c| !c.comment.is_empty()) {
1020            out.push_str("\nCOMMENT ON COLUMN ");
1021            self.write_column_ref(&mut context.current, out, c.into());
1022            out.push_str(" IS ");
1023            self.write_value_string(&mut context.current, out, c.comment);
1024            out.push(';');
1025        }
1026    }
1027
1028    /// Emit DROP TABLE statement.
1029    fn write_drop_table<E>(&self, out: &mut String, if_exists: bool)
1030    where
1031        Self: Sized,
1032        E: Entity,
1033    {
1034        out.reserve(24 + E::table().schema.len() + E::table().name.len());
1035        if !out.is_empty() {
1036            out.push('\n');
1037        }
1038        out.push_str("DROP TABLE ");
1039        let mut context = Context::new(Fragment::SqlDropTable, E::qualified_columns());
1040        if if_exists {
1041            out.push_str("IF EXISTS ");
1042        }
1043        self.write_table_ref(&mut context, out, E::table());
1044        out.push(';');
1045    }
1046
1047    /// Emit SELECT statement (projection, FROM, WHERE, ORDER, LIMIT).
1048    fn write_select<Item, Cols, Data, Expr>(
1049        &self,
1050        out: &mut String,
1051        columns: Cols,
1052        from: &Data,
1053        condition: Expr,
1054        limit: Option<u32>,
1055    ) where
1056        Self: Sized,
1057        Item: Expression,
1058        Cols: IntoIterator<Item = Item> + Clone,
1059        Data: DataSet,
1060        Expr: Expression,
1061    {
1062        let cols = columns.clone().into_iter().count();
1063        out.reserve(128 + cols * 32);
1064        if !out.is_empty() {
1065            out.push('\n');
1066        }
1067        out.push_str("SELECT ");
1068        let mut has_order_by = false;
1069        let mut context = Context::new(Fragment::SqlSelect, Data::qualified_columns());
1070        separated_by(
1071            out,
1072            columns.clone(),
1073            |out, col| {
1074                col.write_query(self, &mut context, out);
1075                has_order_by = has_order_by || col.is_ordered();
1076            },
1077            ", ",
1078        );
1079        out.push_str("\nFROM ");
1080        from.write_query(
1081            self,
1082            &mut context.switch_fragment(Fragment::SqlSelectFrom).current,
1083            out,
1084        );
1085        if !condition.is_true() {
1086            out.push_str("\nWHERE ");
1087            condition.write_query(
1088                self,
1089                &mut context.switch_fragment(Fragment::SqlSelectWhere).current,
1090                out,
1091            );
1092        }
1093        if has_order_by {
1094            out.push_str("\nORDER BY ");
1095            let mut order_context = context.switch_fragment(Fragment::SqlSelectOrderBy);
1096            separated_by(
1097                out,
1098                columns.into_iter().filter(Expression::is_ordered),
1099                |out, col| {
1100                    col.write_query(self, &mut order_context.current, out);
1101                },
1102                ", ",
1103            );
1104        }
1105        if let Some(limit) = limit {
1106            let _ = write!(out, "\nLIMIT {limit}");
1107        }
1108        out.push(';');
1109    }
1110
1111    /// Emit INSERT (single/multi-row) optionally with ON CONFLICT DO UPDATE.
1112    fn write_insert<'b, E>(
1113        &self,
1114        out: &mut String,
1115        entities: impl IntoIterator<Item = &'b E>,
1116        update: bool,
1117    ) where
1118        Self: Sized,
1119        E: Entity + 'b,
1120    {
1121        let mut rows = entities.into_iter().map(Entity::row_filtered).peekable();
1122        let Some(mut row) = rows.next() else {
1123            return;
1124        };
1125        let single = rows.peek().is_none();
1126        let cols = E::columns().len();
1127        out.reserve(128 + cols * 48);
1128        if !out.is_empty() {
1129            out.push('\n');
1130        }
1131        out.push_str("INSERT INTO ");
1132        let mut context = Context::new(Fragment::SqlInsertInto, E::qualified_columns());
1133        self.write_table_ref(&mut context, out, E::table());
1134        out.push_str(" (");
1135        let columns = E::columns().iter();
1136        if single {
1137            // Inserting a single row uses row_labeled to filter out Passive::NotSet columns
1138            separated_by(
1139                out,
1140                row.iter(),
1141                |out, (name, ..)| {
1142                    self.write_identifier_quoted(&mut context, out, name);
1143                },
1144                ", ",
1145            );
1146        } else {
1147            separated_by(
1148                out,
1149                columns.clone(),
1150                |out, col| {
1151                    self.write_identifier_quoted(&mut context, out, col.name());
1152                },
1153                ", ",
1154            );
1155        };
1156        out.push_str(") VALUES\n");
1157        let mut context = context.switch_fragment(Fragment::SqlInsertIntoValues);
1158        let mut first_row = None;
1159        let mut separate = false;
1160        loop {
1161            if separate {
1162                out.push_str(",\n");
1163            }
1164            out.push('(');
1165            let mut fields = row.iter();
1166            let mut field = fields.next();
1167            separated_by(
1168                out,
1169                E::columns(),
1170                |out, col| {
1171                    if Some(col.name()) == field.map(|v| v.0) {
1172                        self.write_value(
1173                            &mut context.current,
1174                            out,
1175                            field
1176                                .map(|v| &v.1)
1177                                .expect(&format!("Column {} does not have a value", col.name())),
1178                        );
1179                        field = fields.next();
1180                    } else if !single {
1181                        out.push_str("DEFAULT");
1182                    }
1183                },
1184                ", ",
1185            );
1186            out.push(')');
1187            separate = true;
1188            if first_row.is_none() {
1189                first_row = row.into();
1190            }
1191            if let Some(next) = rows.next() {
1192                row = next;
1193            } else {
1194                break;
1195            };
1196        }
1197        let first_row = first_row
1198            .expect("Should have at least one row")
1199            .into_iter()
1200            .map(|(v, _)| v);
1201        if update {
1202            self.write_insert_update_fragment::<E>(
1203                &mut context.current,
1204                out,
1205                if single {
1206                    EitherIterator::Left(
1207                        // If there is only one row to insert then list only the columns that appear
1208                        columns.filter(|c| first_row.clone().find(|n| *n == c.name()).is_some()),
1209                    )
1210                } else {
1211                    EitherIterator::Right(columns)
1212                },
1213            );
1214        }
1215        out.push(';');
1216    }
1217
1218    /// Emit ON CONFLICT DO UPDATE fragment for upsert.
1219    fn write_insert_update_fragment<'a, E>(
1220        &self,
1221        context: &mut Context,
1222        out: &mut String,
1223        columns: impl Iterator<Item = &'a ColumnDef>,
1224    ) where
1225        Self: Sized,
1226        E: Entity,
1227    {
1228        let pk = E::primary_key_def();
1229        if pk.len() == 0 {
1230            return;
1231        }
1232        out.push_str("\nON CONFLICT");
1233        context.fragment = Fragment::SqlInsertIntoOnConflict;
1234        if pk.len() > 0 {
1235            out.push_str(" (");
1236            separated_by(
1237                out,
1238                pk,
1239                |out, col| {
1240                    self.write_identifier_quoted(context, out, col.name());
1241                },
1242                ", ",
1243            );
1244            out.push(')');
1245        }
1246        out.push_str(" DO UPDATE SET\n");
1247        separated_by(
1248            out,
1249            columns.filter(|c| c.primary_key == PrimaryKeyType::None),
1250            |out, col| {
1251                self.write_identifier_quoted(context, out, col.name());
1252                out.push_str(" = EXCLUDED.");
1253                self.write_identifier_quoted(context, out, col.name());
1254            },
1255            ",\n",
1256        );
1257    }
1258
1259    /// Emit DELETE statement with WHERE clause.
1260    fn write_delete<E>(&self, out: &mut String, condition: &impl Expression)
1261    where
1262        Self: Sized,
1263        E: Entity,
1264    {
1265        out.reserve(128 + E::table().schema().len() + E::table().name().len());
1266        if !out.is_empty() {
1267            out.push('\n');
1268        }
1269        out.push_str("DELETE FROM ");
1270        let mut context = Context::new(Fragment::SqlDeleteFrom, E::qualified_columns());
1271        self.write_table_ref(&mut context, out, E::table());
1272        out.push_str("\nWHERE ");
1273        condition.write_query(
1274            self,
1275            &mut context
1276                .switch_fragment(Fragment::SqlDeleteFromWhere)
1277                .current,
1278            out,
1279        );
1280        out.push(';');
1281    }
1282}
1283
1284/// Fallback generic SQL writer (closest to PostgreSQL / DuckDB conventions).
1285pub struct GenericSqlWriter;
1286impl GenericSqlWriter {
1287    /// Construct a new generic writer.
1288    pub fn new() -> Self {
1289        Self {}
1290    }
1291}
1292impl SqlWriter for GenericSqlWriter {
1293    fn as_dyn(&self) -> &dyn SqlWriter {
1294        self
1295    }
1296}