tank_core/writer/
sql_writer.rs

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