tank_core/writer/
sql_writer.rs

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