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 = if timestamp { "" } else { "'" };
331        let _ = write!(
332            out,
333            "{b}{:04}-{:02}-{:02}{b}",
334            value.year(),
335            value.month() as u8,
336            value.day()
337        );
338    }
339
340    /// Render a TIME literal (optionally as part of TIMESTAMP composition).
341    fn write_value_time(
342        &self,
343        _context: &mut Context,
344        out: &mut String,
345        value: &Time,
346        timestamp: bool,
347    ) {
348        print_timer(
349            out,
350            if timestamp { "" } else { "'" },
351            value.hour() as _,
352            value.minute(),
353            value.second(),
354            value.nanosecond(),
355        );
356    }
357
358    /// Render a TIMESTAMP literal.
359    fn write_value_timestamp(
360        &self,
361        context: &mut Context,
362        out: &mut String,
363        value: &PrimitiveDateTime,
364    ) {
365        let delimiter = if context.fragment == Fragment::Json {
366            '"'
367        } else {
368            '\''
369        };
370        out.push(delimiter);
371        self.write_value_date(context, out, &value.date(), true);
372        out.push('T');
373        self.write_value_time(context, out, &value.time(), true);
374        out.push(delimiter);
375    }
376
377    /// Render a TIMESTAMPTZ literal.
378    fn write_value_timestamptz(
379        &self,
380        context: &mut Context,
381        out: &mut String,
382        value: &OffsetDateTime,
383    ) {
384        let date_time = value.to_utc();
385        self.write_value_timestamp(
386            context,
387            out,
388            &PrimitiveDateTime::new(date_time.date(), date_time.time()),
389        );
390    }
391
392    /// Ordered units used to decompose intervals.
393    fn value_interval_units(&self) -> &[(&str, i128)] {
394        static UNITS: &[(&str, i128)] = &[
395            ("DAY", Interval::NANOS_IN_DAY),
396            ("HOUR", Interval::NANOS_IN_SEC * 3600),
397            ("MINUTE", Interval::NANOS_IN_SEC * 60),
398            ("SECOND", Interval::NANOS_IN_SEC),
399            ("MICROSECOND", 1_000),
400            ("NANOSECOND", 1),
401        ];
402        UNITS
403    }
404
405    /// Render INTERVAL literal using largest representative units.
406    fn write_value_interval(&self, context: &mut Context, out: &mut String, value: &Interval) {
407        out.push_str("INTERVAL ");
408        let delimiter = if context.fragment == Fragment::Json {
409            '"'
410        } else {
411            '\''
412        };
413        out.push(delimiter);
414        if value.is_zero() {
415            out.push_str("0 SECONDS");
416        }
417        macro_rules! write_unit {
418            ($out:ident, $len:ident, $val:expr, $unit:expr) => {
419                if $out.len() > $len {
420                    $out.push(' ');
421                    $len = $out.len();
422                }
423                let _ = write!(
424                    $out,
425                    "{} {}{}",
426                    $val,
427                    $unit,
428                    if $val != 1 { "S" } else { "" }
429                );
430            };
431        }
432        let mut months = value.months;
433        let mut nanos = value.nanos + value.days as i128 * Interval::NANOS_IN_DAY;
434        let mut len = out.len();
435        if months != 0 {
436            if months > 48 || months % 12 == 0 {
437                write_unit!(out, len, months / 12, "YEAR");
438                months = months % 12;
439            }
440            if months != 0 {
441                write_unit!(out, len, months, "MONTH");
442            }
443        }
444        for &(name, factor) in self.value_interval_units() {
445            let rem = nanos % factor;
446            if rem == 0 || factor / rem > 1_000_000 {
447                let value = nanos / factor;
448                if value != 0 {
449                    write_unit!(out, len, value, name);
450                    nanos = rem;
451                    if nanos == 0 {
452                        break;
453                    }
454                }
455            }
456        }
457        out.push(delimiter);
458    }
459
460    /// Render list/array literal.
461    fn write_value_list(
462        &self,
463        context: &mut Context,
464        out: &mut String,
465        value: Either<&Box<[Value]>, &Vec<Value>>,
466        _ty: &Value,
467    ) {
468        out.push('[');
469        separated_by(
470            out,
471            match value {
472                Either::Left(v) => v.iter(),
473                Either::Right(v) => v.iter(),
474            },
475            |out, v| {
476                self.write_value(context, out, v);
477            },
478            ",",
479        );
480        out.push(']');
481    }
482
483    /// Render map literal.
484    fn write_value_map(
485        &self,
486        context: &mut Context,
487        out: &mut String,
488        value: &HashMap<Value, Value>,
489    ) {
490        out.push('{');
491        separated_by(
492            out,
493            value,
494            |out, (k, v)| {
495                self.write_value(context, out, k);
496                out.push(':');
497                self.write_value(context, out, v);
498            },
499            ",",
500        );
501        out.push('}');
502    }
503
504    /// Render struct literal.
505    fn write_value_struct(
506        &self,
507        context: &mut Context,
508        out: &mut String,
509        value: &Vec<(String, Value)>,
510    ) {
511        out.push('{');
512        separated_by(
513            out,
514            value,
515            |out, (k, v)| {
516                self.write_value_string(context, out, k);
517                out.push(':');
518                self.write_value(context, out, v);
519            },
520            ",",
521        );
522        out.push('}');
523    }
524
525    /// Precedence table for unary operators.
526    fn expression_unary_op_precedence(&self, value: &UnaryOpType) -> i32 {
527        match value {
528            UnaryOpType::Negative => 1250,
529            UnaryOpType::Not => 250,
530        }
531    }
532
533    /// Precedence table for binary operators.
534    fn expression_binary_op_precedence(&self, value: &BinaryOpType) -> i32 {
535        match value {
536            BinaryOpType::Or => 100,
537            BinaryOpType::And => 200,
538            BinaryOpType::Equal => 300,
539            BinaryOpType::NotEqual => 300,
540            BinaryOpType::Less => 300,
541            BinaryOpType::Greater => 300,
542            BinaryOpType::LessEqual => 300,
543            BinaryOpType::GreaterEqual => 300,
544            BinaryOpType::Is => 400,
545            BinaryOpType::IsNot => 400,
546            BinaryOpType::Like => 400,
547            BinaryOpType::NotLike => 400,
548            BinaryOpType::Regexp => 400,
549            BinaryOpType::NotRegexp => 400,
550            BinaryOpType::Glob => 400,
551            BinaryOpType::NotGlob => 400,
552            BinaryOpType::BitwiseOr => 500,
553            BinaryOpType::BitwiseAnd => 600,
554            BinaryOpType::ShiftLeft => 700,
555            BinaryOpType::ShiftRight => 700,
556            BinaryOpType::Subtraction => 800,
557            BinaryOpType::Addition => 800,
558            BinaryOpType::Multiplication => 900,
559            BinaryOpType::Division => 900,
560            BinaryOpType::Remainder => 900,
561            BinaryOpType::Indexing => 1000,
562            BinaryOpType::Cast => 1100,
563            BinaryOpType::Alias => 1200,
564        }
565    }
566
567    /// Render an operand (literal / variable / nested expression).
568    fn write_expression_operand(&self, context: &mut Context, out: &mut String, value: &Operand) {
569        let delimiter = if context.fragment == Fragment::JsonKey {
570            "\""
571        } else {
572            ""
573        };
574        match value {
575            Operand::LitBool(v) => self.write_value_bool(context, out, *v),
576            Operand::LitFloat(v) => write_float!(self, context, out, *v, delimiter),
577            Operand::LitIdent(v) => drop(out.push_str(v)),
578            Operand::LitField(v) => separated_by(out, *v, |out, v| out.push_str(v), "."),
579            Operand::LitInt(v) => write_integer!(out, *v, delimiter),
580            Operand::LitStr(v) => self.write_value_string(context, out, v),
581            Operand::LitArray(v) => {
582                out.push('[');
583                separated_by(
584                    out,
585                    *v,
586                    |out, v| {
587                        v.write_query(self.as_dyn(), context, out);
588                    },
589                    ", ",
590                );
591                out.push(']');
592            }
593            Operand::Null => drop(out.push_str("NULL")),
594            Operand::Type(v) => self.write_column_type(context, out, v),
595            Operand::Variable(v) => self.write_value(context, out, v),
596            Operand::Call(f, args) => {
597                out.push_str(f);
598                out.push('(');
599                separated_by(
600                    out,
601                    *args,
602                    |out, v| {
603                        v.write_query(self.as_dyn(), context, out);
604                    },
605                    ",",
606                );
607                out.push(')');
608            }
609            Operand::Asterisk => drop(out.push('*')),
610            Operand::QuestionMark => self.write_expression_operand_question_mark(context, out),
611        };
612    }
613
614    /// Render parameter placeholder (dialect may override).
615    fn write_expression_operand_question_mark(&self, _context: &mut Context, out: &mut String) {
616        out.push('?');
617    }
618
619    /// Render unary operator expression.
620    fn write_expression_unary_op(
621        &self,
622        context: &mut Context,
623        out: &mut String,
624        value: &UnaryOp<&dyn Expression>,
625    ) {
626        match value.op {
627            UnaryOpType::Negative => out.push('-'),
628            UnaryOpType::Not => out.push_str("NOT "),
629        };
630        possibly_parenthesized!(
631            out,
632            value.arg.precedence(self.as_dyn()) <= self.expression_unary_op_precedence(&value.op),
633            value.arg.write_query(self.as_dyn(), context, out)
634        );
635    }
636
637    /// Render binary operator expression handling precedence / parenthesis.
638    fn write_expression_binary_op(
639        &self,
640        context: &mut Context,
641        out: &mut String,
642        value: &BinaryOp<&dyn Expression, &dyn Expression>,
643    ) {
644        let (prefix, infix, suffix, lhs_parenthesized, rhs_parenthesized) = match value.op {
645            BinaryOpType::Indexing => ("", "[", "]", false, true),
646            BinaryOpType::Cast => ("CAST(", " AS ", ")", true, true),
647            BinaryOpType::Multiplication => ("", " * ", "", false, false),
648            BinaryOpType::Division => ("", " / ", "", false, false),
649            BinaryOpType::Remainder => ("", " % ", "", false, false),
650            BinaryOpType::Addition => ("", " + ", "", false, false),
651            BinaryOpType::Subtraction => ("", " - ", "", false, false),
652            BinaryOpType::ShiftLeft => ("", " << ", "", false, false),
653            BinaryOpType::ShiftRight => ("", " >> ", "", false, false),
654            BinaryOpType::BitwiseAnd => ("", " & ", "", false, false),
655            BinaryOpType::BitwiseOr => ("", " | ", "", false, false),
656            BinaryOpType::Is => ("", " IS ", "", false, false),
657            BinaryOpType::IsNot => ("", " IS NOT ", "", false, false),
658            BinaryOpType::Like => ("", " LIKE ", "", false, false),
659            BinaryOpType::NotLike => ("", " NOT LIKE ", "", false, false),
660            BinaryOpType::Regexp => ("", " REGEXP ", "", false, false),
661            BinaryOpType::NotRegexp => ("", " NOT REGEXP ", "", false, false),
662            BinaryOpType::Glob => ("", " GLOB ", "", false, false),
663            BinaryOpType::NotGlob => ("", " NOT GLOB ", "", false, false),
664            BinaryOpType::Equal => ("", " = ", "", false, false),
665            BinaryOpType::NotEqual => ("", " != ", "", false, false),
666            BinaryOpType::Less => ("", " < ", "", false, false),
667            BinaryOpType::LessEqual => ("", " <= ", "", false, false),
668            BinaryOpType::Greater => ("", " > ", "", false, false),
669            BinaryOpType::GreaterEqual => ("", " >= ", "", false, false),
670            BinaryOpType::And => ("", " AND ", "", false, false),
671            BinaryOpType::Or => ("", " OR ", "", false, false),
672            BinaryOpType::Alias => {
673                if context.fragment == Fragment::SqlSelectOrderBy {
674                    return value.lhs.write_query(self.as_dyn(), context, out);
675                } else {
676                    ("", " AS ", "", false, false)
677                }
678            }
679        };
680        let mut context = context.switch_fragment(if value.op == BinaryOpType::Cast {
681            Fragment::Casting
682        } else {
683            context.fragment
684        });
685        let precedence = self.expression_binary_op_precedence(&value.op);
686        out.push_str(prefix);
687        possibly_parenthesized!(
688            out,
689            !lhs_parenthesized && value.lhs.precedence(self.as_dyn()) < precedence,
690            value
691                .lhs
692                .write_query(self.as_dyn(), &mut context.current, out)
693        );
694        out.push_str(infix);
695        possibly_parenthesized!(
696            out,
697            !rhs_parenthesized && value.rhs.precedence(self.as_dyn()) <= precedence,
698            value
699                .rhs
700                .write_query(self.as_dyn(), &mut context.current, out)
701        );
702        out.push_str(suffix);
703    }
704
705    /// Render ordered expression inside ORDER BY.
706    fn write_expression_ordered(
707        &self,
708        context: &mut Context,
709        out: &mut String,
710        value: &Ordered<&dyn Expression>,
711    ) {
712        value.expression.write_query(self.as_dyn(), context, out);
713        if context.fragment == Fragment::SqlSelectOrderBy {
714            let _ = write!(
715                out,
716                " {}",
717                match value.order {
718                    Order::ASC => "ASC",
719                    Order::DESC => "DESC",
720                }
721            );
722        }
723    }
724
725    /// Render join keyword(s) for the given join type.
726    fn write_join_type(&self, _context: &mut Context, out: &mut String, join_type: &JoinType) {
727        out.push_str(match &join_type {
728            JoinType::Default => "JOIN",
729            JoinType::Inner => "INNER JOIN",
730            JoinType::Outer => "OUTER JOIN",
731            JoinType::Left => "LEFT JOIN",
732            JoinType::Right => "RIGHT JOIN",
733            JoinType::Cross => "CROSS",
734            JoinType::Natural => "NATURAL JOIN",
735        });
736    }
737
738    /// Render a JOIN clause.
739    fn write_join(
740        &self,
741        context: &mut Context,
742        out: &mut String,
743        join: &Join<&dyn DataSet, &dyn DataSet, &dyn Expression>,
744    ) {
745        let mut context = context.switch_fragment(Fragment::SqlJoin);
746        context.current.qualify_columns = true;
747        join.lhs
748            .write_query(self.as_dyn(), &mut context.current, out);
749        out.push(' ');
750        self.write_join_type(&mut context.current, out, &join.join);
751        out.push(' ');
752        join.rhs
753            .write_query(self.as_dyn(), &mut context.current, out);
754        if let Some(on) = &join.on {
755            out.push_str(" ON ");
756            on.write_query(self.as_dyn(), &mut context.current, out);
757        }
758    }
759
760    /// Emit BEGIN statement.
761    fn write_transaction_begin(&self, out: &mut String) {
762        out.push_str("BEGIN;");
763    }
764
765    /// Emit COMMIT statement.
766    fn write_transaction_commit(&self, out: &mut String) {
767        out.push_str("COMMIT;");
768    }
769
770    /// Emit ROLLBACK statement.
771    fn write_transaction_rollback(&self, out: &mut String) {
772        out.push_str("ROLLBACK;");
773    }
774
775    /// Emit CREATE SCHEMA.
776    fn write_create_schema<E>(&self, out: &mut String, if_not_exists: bool)
777    where
778        Self: Sized,
779        E: Entity,
780    {
781        out.reserve(32 + E::table().schema.len());
782        if !out.is_empty() {
783            out.push('\n');
784        }
785        out.push_str("CREATE SCHEMA ");
786        let mut context = Context::new(Fragment::SqlCreateSchema, E::qualified_columns());
787        if if_not_exists {
788            out.push_str("IF NOT EXISTS ");
789        }
790        self.write_identifier_quoted(&mut context, out, E::table().schema);
791        out.push(';');
792    }
793
794    /// Emit DROP SCHEMA.
795    fn write_drop_schema<E>(&self, out: &mut String, if_exists: bool)
796    where
797        Self: Sized,
798        E: Entity,
799    {
800        out.reserve(24 + E::table().schema.len());
801        if !out.is_empty() {
802            out.push('\n');
803        }
804        out.push_str("DROP SCHEMA ");
805        let mut context = Context::new(Fragment::SqlDropSchema, E::qualified_columns());
806        if if_exists {
807            out.push_str("IF EXISTS ");
808        }
809        self.write_identifier_quoted(&mut context, out, E::table().schema);
810        out.push(';');
811    }
812
813    /// Emit CREATE TABLE with columns, constraints & comments.
814    fn write_create_table<E>(&self, out: &mut String, if_not_exists: bool)
815    where
816        Self: Sized,
817        E: Entity,
818    {
819        let mut context = Context::new(Fragment::SqlCreateTable, E::qualified_columns());
820        let estimated = 128 + E::columns().len() * 64 + E::primary_key_def().len() * 24;
821        out.reserve(estimated);
822        if !out.is_empty() {
823            out.push('\n');
824        }
825        out.push_str("CREATE TABLE ");
826        if if_not_exists {
827            out.push_str("IF NOT EXISTS ");
828        }
829        self.write_table_ref(&mut context, out, E::table());
830        out.push_str(" (\n");
831        separated_by(
832            out,
833            E::columns(),
834            |out, v| {
835                self.write_create_table_column_fragment(&mut context, out, v);
836            },
837            ",\n",
838        );
839        let primary_key = E::primary_key_def();
840        if primary_key.len() > 1 {
841            out.push_str(",\nPRIMARY KEY (");
842            separated_by(
843                out,
844                primary_key,
845                |out, v| {
846                    self.write_identifier_quoted(
847                        &mut context
848                            .switch_fragment(Fragment::SqlCreateTablePrimaryKey)
849                            .current,
850                        out,
851                        v.name(),
852                    );
853                },
854                ", ",
855            );
856            out.push(')');
857        }
858        for unique in E::unique_defs() {
859            if unique.len() > 1 {
860                out.push_str(",\nUNIQUE (");
861                separated_by(
862                    out,
863                    unique,
864                    |out, v| {
865                        self.write_identifier_quoted(
866                            &mut context
867                                .switch_fragment(Fragment::SqlCreateTableUnique)
868                                .current,
869                            out,
870                            v.name(),
871                        );
872                    },
873                    ", ",
874                );
875                out.push(')');
876            }
877        }
878        let foreign_keys = E::columns().iter().filter(|c| c.references.is_some());
879        separated_by(
880            out,
881            foreign_keys,
882            |out, column| {
883                let references = column.references.unwrap();
884                out.push_str(",\nFOREIGN KEY (");
885                self.write_identifier_quoted(&mut context, out, &column.name());
886                out.push_str(") REFERENCES ");
887                self.write_table_ref(&mut context, out, &references.table());
888                out.push('(');
889                self.write_column_ref(&mut context, out, &references);
890                out.push(')');
891                if let Some(on_delete) = &column.on_delete {
892                    out.push_str(" ON DELETE ");
893                    self.write_create_table_references_action(&mut context, out, on_delete);
894                }
895                if let Some(on_update) = &column.on_update {
896                    out.push_str(" ON UPDATE ");
897                    self.write_create_table_references_action(&mut context, out, on_update);
898                }
899            },
900            "",
901        );
902        out.push_str(");");
903        self.write_column_comments_statements::<E>(&mut context, out);
904    }
905
906    /// Emit single column definition fragment.
907    fn write_create_table_column_fragment(
908        &self,
909        context: &mut Context,
910        out: &mut String,
911        column: &ColumnDef,
912    ) where
913        Self: Sized,
914    {
915        self.write_identifier_quoted(context, out, &column.name());
916        out.push(' ');
917        let len = out.len();
918        self.write_column_overridden_type(context, out, column, &column.column_type);
919        let didnt_write_type = out.len() == len;
920        if didnt_write_type {
921            SqlWriter::write_column_type(self, context, out, &column.value);
922        }
923        if !column.nullable && column.primary_key == PrimaryKeyType::None {
924            out.push_str(" NOT NULL");
925        }
926        if let Some(default) = &column.default {
927            out.push_str(" DEFAULT ");
928            default.write_query(self.as_dyn(), context, out);
929        }
930        if column.primary_key == PrimaryKeyType::PrimaryKey {
931            // Composite primary key will be printed elsewhere
932            out.push_str(" PRIMARY KEY");
933        }
934        if column.unique && column.primary_key != PrimaryKeyType::PrimaryKey {
935            out.push_str(" UNIQUE");
936        }
937        if !column.comment.is_empty() {
938            self.write_column_comment_inline(context, out, column);
939        }
940    }
941
942    /// Emit referential action keyword.
943    fn write_create_table_references_action(
944        &self,
945        _context: &mut Context,
946        out: &mut String,
947        action: &Action,
948    ) {
949        out.push_str(match action {
950            Action::NoAction => "NO ACTION",
951            Action::Restrict => "RESTRICT",
952            Action::Cascade => "CASCADE",
953            Action::SetNull => "SET NULL",
954            Action::SetDefault => "SET DEFAULT",
955        });
956    }
957
958    fn write_column_comment_inline(
959        &self,
960        _context: &mut Context,
961        _out: &mut String,
962        _column: &ColumnDef,
963    ) where
964        Self: Sized,
965    {
966    }
967
968    /// Emit COMMENT ON COLUMN statements for columns carrying comments.
969    fn write_column_comments_statements<E>(&self, context: &mut Context, out: &mut String)
970    where
971        Self: Sized,
972        E: Entity,
973    {
974        let mut context = context.switch_fragment(Fragment::SqlCommentOnColumn);
975        context.current.qualify_columns = true;
976        for c in E::columns().iter().filter(|c| !c.comment.is_empty()) {
977            out.push_str("\nCOMMENT ON COLUMN ");
978            self.write_column_ref(&mut context.current, out, c.into());
979            out.push_str(" IS ");
980            self.write_value_string(&mut context.current, out, c.comment);
981            out.push(';');
982        }
983    }
984
985    /// Emit DROP TABLE statement.
986    fn write_drop_table<E>(&self, out: &mut String, if_exists: bool)
987    where
988        Self: Sized,
989        E: Entity,
990    {
991        out.reserve(24 + E::table().schema.len() + E::table().name.len());
992        if !out.is_empty() {
993            out.push('\n');
994        }
995        out.push_str("DROP TABLE ");
996        let mut context = Context::new(Fragment::SqlDropTable, E::qualified_columns());
997        if if_exists {
998            out.push_str("IF EXISTS ");
999        }
1000        self.write_table_ref(&mut context, out, E::table());
1001        out.push(';');
1002    }
1003
1004    /// Emit SELECT statement (projection, FROM, WHERE, ORDER, LIMIT).
1005    fn write_select<Item, Cols, Data, Cond>(
1006        &self,
1007        out: &mut String,
1008        columns: Cols,
1009        from: &Data,
1010        condition: &Cond,
1011        limit: Option<u32>,
1012    ) where
1013        Self: Sized,
1014        Item: Expression,
1015        Cols: IntoIterator<Item = Item> + Clone,
1016        Data: DataSet,
1017        Cond: Expression,
1018    {
1019        let cols = columns.clone().into_iter().count();
1020        out.reserve(128 + cols * 32);
1021        if !out.is_empty() {
1022            out.push('\n');
1023        }
1024        out.push_str("SELECT ");
1025        let mut has_order_by = false;
1026        let mut context = Context::new(Fragment::SqlSelect, Data::qualified_columns());
1027        separated_by(
1028            out,
1029            columns.clone(),
1030            |out, col| {
1031                col.write_query(self, &mut context, out);
1032                has_order_by = has_order_by || col.is_ordered();
1033            },
1034            ", ",
1035        );
1036        out.push_str("\nFROM ");
1037        from.write_query(
1038            self,
1039            &mut context.switch_fragment(Fragment::SqlSelectFrom).current,
1040            out,
1041        );
1042        out.push_str("\nWHERE ");
1043        condition.write_query(
1044            self,
1045            &mut context.switch_fragment(Fragment::SqlSelectWhere).current,
1046            out,
1047        );
1048        if has_order_by {
1049            out.push_str("\nORDER BY ");
1050            let mut order_context = context.switch_fragment(Fragment::SqlSelectOrderBy);
1051            separated_by(
1052                out,
1053                columns.into_iter().filter(Expression::is_ordered),
1054                |out, col| {
1055                    col.write_query(self, &mut order_context.current, out);
1056                },
1057                ", ",
1058            );
1059        }
1060        if let Some(limit) = limit {
1061            let _ = write!(out, "\nLIMIT {}", limit);
1062        }
1063        out.push(';');
1064    }
1065
1066    /// Emit INSERT (single/multi-row) optionally with ON CONFLICT DO UPDATE.
1067    fn write_insert<'b, E>(
1068        &self,
1069        out: &mut String,
1070        entities: impl IntoIterator<Item = &'b E>,
1071        update: bool,
1072    ) where
1073        Self: Sized,
1074        E: Entity + 'b,
1075    {
1076        let mut rows = entities.into_iter().map(Entity::row_filtered).peekable();
1077        let Some(mut row) = rows.next() else {
1078            return;
1079        };
1080        let cols = E::columns().len();
1081        out.reserve(128 + cols * 48);
1082        if !out.is_empty() {
1083            out.push('\n');
1084        }
1085        out.push_str("INSERT INTO ");
1086        let mut context = Context::new(Fragment::SqlInsertInto, E::qualified_columns());
1087        self.write_table_ref(&mut context, out, E::table());
1088        out.push_str(" (");
1089        let columns = E::columns().iter();
1090        let single = rows.peek().is_none();
1091        if single {
1092            // Inserting a single row uses row_labeled to filter out Passive::NotSet columns
1093            separated_by(
1094                out,
1095                row.iter(),
1096                |out, v| {
1097                    self.write_identifier_quoted(&mut context, out, v.0);
1098                },
1099                ", ",
1100            );
1101        } else {
1102            separated_by(
1103                out,
1104                columns.clone(),
1105                |out, v| {
1106                    self.write_identifier_quoted(&mut context, out, v.name());
1107                },
1108                ", ",
1109            );
1110        };
1111        out.push_str(") VALUES\n");
1112        let mut context = context.switch_fragment(Fragment::SqlInsertIntoValues);
1113        let mut first_row = None;
1114        let mut separate = false;
1115        loop {
1116            if separate {
1117                out.push_str(",\n");
1118            }
1119            out.push('(');
1120            let mut fields = row.iter();
1121            let mut field = fields.next();
1122            separated_by(
1123                out,
1124                E::columns(),
1125                |out, col| {
1126                    if Some(col.name()) == field.map(|v| v.0) {
1127                        self.write_value(
1128                            &mut context.current,
1129                            out,
1130                            field
1131                                .map(|v| &v.1)
1132                                .expect(&format!("Column {} does not have a value", col.name())),
1133                        );
1134                        field = fields.next();
1135                    } else if !single {
1136                        out.push_str("DEFAULT");
1137                    }
1138                },
1139                ", ",
1140            );
1141            out.push(')');
1142            separate = true;
1143            if first_row.is_none() {
1144                first_row = row.into();
1145            }
1146            if let Some(next) = rows.next() {
1147                row = next;
1148            } else {
1149                break;
1150            };
1151        }
1152        let first_row = first_row
1153            .expect("Should have at least one row")
1154            .into_iter()
1155            .map(|(v, _)| v);
1156        if update {
1157            self.write_insert_update_fragment::<E>(
1158                &mut context.current,
1159                out,
1160                if single {
1161                    EitherIterator::Left(
1162                        // If there is only one row to insert then list only the columns that appear
1163                        columns.filter(|c| first_row.clone().find(|n| *n == c.name()).is_some()),
1164                    )
1165                } else {
1166                    EitherIterator::Right(columns)
1167                },
1168            );
1169        }
1170        out.push(';');
1171    }
1172
1173    /// Emit ON CONFLICT DO UPDATE fragment for upsert.
1174    fn write_insert_update_fragment<'a, E>(
1175        &self,
1176        context: &mut Context,
1177        out: &mut String,
1178        columns: impl Iterator<Item = &'a ColumnDef>,
1179    ) where
1180        Self: Sized,
1181        E: Entity,
1182    {
1183        let pk = E::primary_key_def();
1184        if pk.len() == 0 {
1185            return;
1186        }
1187        out.push_str("\nON CONFLICT");
1188        context.fragment = Fragment::SqlInsertIntoOnConflict;
1189        if pk.len() > 0 {
1190            out.push_str(" (");
1191            separated_by(
1192                out,
1193                pk,
1194                |out, v| {
1195                    self.write_identifier_quoted(context, out, v.name());
1196                },
1197                ", ",
1198            );
1199            out.push(')');
1200        }
1201        out.push_str(" DO UPDATE SET\n");
1202        separated_by(
1203            out,
1204            columns.filter(|c| c.primary_key == PrimaryKeyType::None),
1205            |out, v| {
1206                self.write_identifier_quoted(context, out, v.name());
1207                out.push_str(" = EXCLUDED.");
1208                self.write_identifier_quoted(context, out, v.name());
1209            },
1210            ",\n",
1211        );
1212    }
1213
1214    /// Emit DELETE statement with WHERE clause.
1215    fn write_delete<E>(&self, out: &mut String, condition: &impl Expression)
1216    where
1217        Self: Sized,
1218        E: Entity,
1219    {
1220        out.reserve(128 + E::table().schema.len() + E::table().name.len());
1221        if !out.is_empty() {
1222            out.push('\n');
1223        }
1224        out.push_str("DELETE FROM ");
1225        let mut context = Context::new(Fragment::SqlDeleteFrom, E::qualified_columns());
1226        self.write_table_ref(&mut context, out, E::table());
1227        out.push_str("\nWHERE ");
1228        condition.write_query(
1229            self,
1230            &mut context
1231                .switch_fragment(Fragment::SqlDeleteFromWhere)
1232                .current,
1233            out,
1234        );
1235        out.push(';');
1236    }
1237}
1238
1239/// Fallback generic SQL writer (closest to PostgreSQL / DuckDB conventions).
1240pub struct GenericSqlWriter;
1241impl GenericSqlWriter {
1242    /// Construct a new generic writer.
1243    pub fn new() -> Self {
1244        Self {}
1245    }
1246}
1247impl SqlWriter for GenericSqlWriter {
1248    fn as_dyn(&self) -> &dyn SqlWriter {
1249        self
1250    }
1251}