Skip to main content

tank_core/writer/
sql_writer.rs

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