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