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("VARCHAR"),
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.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: &mut Context,
645        op_type: BinaryOpType,
646    ) -> (&str, &str, &str, bool, bool) {
647        match op_type {
648            BinaryOpType::Indexing => ("", "[", "]", false, true),
649            BinaryOpType::Cast => {
650                context.switch_fragment(Fragment::Casting);
651                ("CAST(", " AS ", ")", true, true)
652            }
653            BinaryOpType::Multiplication => ("", " * ", "", false, false),
654            BinaryOpType::Division => ("", " / ", "", false, false),
655            BinaryOpType::Remainder => ("", " % ", "", false, false),
656            BinaryOpType::Addition => ("", " + ", "", false, false),
657            BinaryOpType::Subtraction => ("", " - ", "", false, false),
658            BinaryOpType::ShiftLeft => ("", " << ", "", false, false),
659            BinaryOpType::ShiftRight => ("", " >> ", "", false, false),
660            BinaryOpType::BitwiseAnd => ("", " & ", "", false, false),
661            BinaryOpType::BitwiseOr => ("", " | ", "", false, false),
662            BinaryOpType::In => ("", " IN ", "", false, false),
663            BinaryOpType::NotIn => ("", " NOT IN ", "", false, false),
664            BinaryOpType::Is => ("", " IS ", "", false, false),
665            BinaryOpType::IsNot => ("", " IS NOT ", "", false, false),
666            BinaryOpType::Like => ("", " LIKE ", "", false, false),
667            BinaryOpType::NotLike => ("", " NOT LIKE ", "", false, false),
668            BinaryOpType::Regexp => ("", " REGEXP ", "", false, false),
669            BinaryOpType::NotRegexp => ("", " NOT REGEXP ", "", false, false),
670            BinaryOpType::Glob => ("", " GLOB ", "", false, false),
671            BinaryOpType::NotGlob => ("", " NOT GLOB ", "", false, false),
672            BinaryOpType::Equal => ("", " = ", "", false, false),
673            BinaryOpType::NotEqual => ("", " != ", "", false, false),
674            BinaryOpType::Less => ("", " < ", "", false, false),
675            BinaryOpType::LessEqual => ("", " <= ", "", false, false),
676            BinaryOpType::Greater => ("", " > ", "", false, false),
677            BinaryOpType::GreaterEqual => ("", " >= ", "", false, false),
678            BinaryOpType::And => ("", " AND ", "", false, false),
679            BinaryOpType::Or => ("", " OR ", "", false, false),
680            BinaryOpType::Alias => {
681                context.switch_fragment(Fragment::Aliasing);
682                ("", " AS ", "", false, false)
683            }
684        }
685    }
686
687    fn write_operand(&self, context: &mut Context, out: &mut DynQuery, value: &Operand) {
688        match value {
689            Operand::Null => self.write_null(context, out),
690            Operand::LitBool(v) => self.write_bool(context, out, *v),
691            Operand::LitInt(v) => self.write_value_i128(context, out, *v),
692            Operand::LitFloat(v) => self.write_value_f64(context, out, *v),
693            Operand::LitStr(v) => self.write_string(context, out, v),
694            Operand::LitIdent(v) => {
695                self.write_identifier(context, out, v, context.fragment == Fragment::Aliasing)
696            }
697            Operand::LitField(v) => {
698                let separator = self.separator();
699                for (i, part) in v.iter().enumerate() {
700                    if i > 0 {
701                        out.push_str(separator);
702                    }
703                    out.push_str(part);
704                }
705            }
706            Operand::LitList(v) => self.write_list(
707                context,
708                out,
709                &mut v.iter().map(|v| v as &dyn Expression),
710                None,
711                false,
712            ),
713            Operand::LitTuple(v) => {
714                self.write_tuple(context, out, &mut v.iter().map(|v| v as &dyn Expression))
715            }
716            Operand::Type(v) => self.write_column_type(context, out, v),
717            Operand::Variable(v) => self.write_value(context, out, v),
718            Operand::Value(v) => self.write_value(context, out, v),
719            Operand::Call(f, args) => self.write_function(context, out, f, args),
720            Operand::Asterisk => drop(out.push('*')),
721            Operand::QuestionMark => self.write_question_mark(context, out),
722            Operand::CurrentTimestampMs => self.write_current_timestamp_ms(context, out),
723        };
724    }
725
726    fn write_question_mark(&self, context: &mut Context, out: &mut DynQuery) {
727        context.counter += 1;
728        out.push('?');
729    }
730
731    fn write_current_timestamp_ms(&self, _context: &mut Context, out: &mut DynQuery) {
732        out.push_str("NOW()");
733    }
734
735    fn write_unary_op(
736        &self,
737        context: &mut Context,
738        out: &mut DynQuery,
739        value: &UnaryOp<&dyn Expression>,
740    ) {
741        match value.op {
742            UnaryOpType::Negative => out.push('-'),
743            UnaryOpType::Not => out.push_str("NOT "),
744        };
745        possibly_parenthesized!(
746            out,
747            value.arg.precedence(self.as_dyn()) <= self.expression_unary_op_precedence(&value.op),
748            value.arg.write_query(self.as_dyn(), context, out)
749        );
750    }
751
752    /// Render binary operator expression handling precedence and parenthesis.
753    fn write_binary_op(
754        &self,
755        context: &mut Context,
756        out: &mut DynQuery,
757        value: &BinaryOp<&dyn Expression, &dyn Expression>,
758    ) {
759        if value.op == BinaryOpType::Alias && context.fragment == Fragment::SqlSelectOrderBy {
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(if value.op == BinaryOpType::Alias {
773            Fragment::Aliasing
774        } else {
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        out.push_str("BEGIN;");
868    }
869
870    /// Emit COMMIT statement.
871    fn write_transaction_commit(&self, out: &mut DynQuery) {
872        out.push_str("COMMIT;");
873    }
874
875    /// Emit ROLLBACK statement.
876    fn write_transaction_rollback(&self, out: &mut DynQuery) {
877        out.push_str("ROLLBACK;");
878    }
879
880    /// Emit CREATE SCHEMA.
881    fn write_create_schema<E>(&self, out: &mut DynQuery, if_not_exists: bool)
882    where
883        Self: Sized,
884        E: Entity,
885    {
886        let table = E::table();
887        out.buffer().reserve(32 + table.schema.len());
888        if !out.is_empty() {
889            out.push('\n');
890        }
891        out.push_str("CREATE SCHEMA ");
892        let mut context = Context::new(Fragment::SqlCreateSchema, E::qualified_columns());
893        if if_not_exists {
894            out.push_str("IF NOT EXISTS ");
895        }
896        self.write_identifier(&mut context, out, &table.schema, true);
897        out.push(';');
898    }
899
900    /// Emit DROP SCHEMA.
901    fn write_drop_schema<E>(&self, out: &mut DynQuery, if_exists: bool)
902    where
903        Self: Sized,
904        E: Entity,
905    {
906        let mut context = Context::new(Fragment::SqlDropSchema, E::qualified_columns());
907        let table = E::table();
908        out.buffer().reserve(32 + table.schema.len());
909        if !out.is_empty() {
910            out.push('\n');
911        }
912        out.push_str("DROP SCHEMA ");
913        if if_exists {
914            out.push_str("IF EXISTS ");
915        }
916        self.write_identifier(&mut context, out, &table.schema, true);
917        out.push(';');
918    }
919
920    /// Emit CREATE TABLE with columns, constraints & comments.
921    fn write_create_table<E>(&self, out: &mut DynQuery, if_not_exists: bool)
922    where
923        Self: Sized,
924        E: Entity,
925    {
926        let mut context = Context::new(Fragment::SqlCreateTable, E::qualified_columns());
927        let table = E::table();
928        let estimated = 128 + E::columns().len() * 64 + E::primary_key_def().len() * 24;
929        out.buffer().reserve(estimated);
930        if !out.is_empty() {
931            out.push('\n');
932        }
933        out.push_str("CREATE TABLE ");
934        if if_not_exists {
935            out.push_str("IF NOT EXISTS ");
936        }
937        self.write_table_ref(&mut context, out, table);
938        out.push_str(" (\n");
939        separated_by(
940            out,
941            E::columns(),
942            |out, col| {
943                self.write_create_table_column_fragment(&mut context, out, col);
944            },
945            ",\n",
946        );
947        let pk = E::primary_key_def();
948        if pk.len() > 1 {
949            self.write_create_table_primary_key_fragment(&mut context, out, pk.iter().map(|v| *v));
950        }
951        for unique in E::unique_defs() {
952            if unique.len() > 1 {
953                out.push_str(",\nUNIQUE (");
954                separated_by(
955                    out,
956                    unique,
957                    |out, col| {
958                        self.write_identifier(
959                            &mut context
960                                .switch_fragment(Fragment::SqlCreateTableUnique)
961                                .current,
962                            out,
963                            col.name(),
964                            true,
965                        );
966                    },
967                    ", ",
968                );
969                out.push(')');
970            }
971        }
972        let foreign_keys = E::columns().iter().filter(|c| c.references.is_some());
973        separated_by(
974            out,
975            foreign_keys,
976            |out, column| {
977                let references = column.references.as_ref().unwrap();
978                out.push_str(",\nFOREIGN KEY (");
979                self.write_identifier(&mut context, out, &column.name(), true);
980                out.push_str(") REFERENCES ");
981                self.write_table_ref(&mut context, out, &references.table());
982                out.push('(');
983                self.write_column_ref(&mut context, out, references);
984                out.push(')');
985                if let Some(on_delete) = &column.on_delete {
986                    out.push_str(" ON DELETE ");
987                    self.write_create_table_references_action(&mut context, out, on_delete);
988                }
989                if let Some(on_update) = &column.on_update {
990                    out.push_str(" ON UPDATE ");
991                    self.write_create_table_references_action(&mut context, out, on_update);
992                }
993            },
994            "",
995        );
996        out.push_str(");");
997        self.write_column_comments_statements::<E>(&mut context, out);
998    }
999
1000    /// Emit single column definition fragment.
1001    fn write_create_table_column_fragment(
1002        &self,
1003        context: &mut Context,
1004        out: &mut DynQuery,
1005        column: &ColumnDef,
1006    ) where
1007        Self: Sized,
1008    {
1009        self.write_identifier(context, out, &column.name(), true);
1010        out.push(' ');
1011        let len = out.len();
1012        self.write_column_overridden_type(context, out, column, &column.column_type);
1013        let didnt_write_type = out.len() == len;
1014        if didnt_write_type {
1015            SqlWriter::write_column_type(self, context, out, &column.value);
1016        }
1017        if !column.nullable && column.primary_key == PrimaryKeyType::None {
1018            out.push_str(" NOT NULL");
1019        }
1020        if column.default.is_set() {
1021            out.push_str(" DEFAULT ");
1022            column.default.write_query(self.as_dyn(), context, out);
1023        }
1024        if column.primary_key == PrimaryKeyType::PrimaryKey {
1025            // Composite primary key will be printed elsewhere
1026            out.push_str(" PRIMARY KEY");
1027        }
1028        if column.unique && column.primary_key != PrimaryKeyType::PrimaryKey {
1029            out.push_str(" UNIQUE");
1030        }
1031        if !column.comment.is_empty() {
1032            self.write_column_comment_inline(context, out, column);
1033        }
1034    }
1035
1036    /// Write PRIMARY KEY constraint.
1037    fn write_create_table_primary_key_fragment<'a, It>(
1038        &self,
1039        context: &mut Context,
1040        out: &mut DynQuery,
1041        primary_key: It,
1042    ) where
1043        Self: Sized,
1044        It: IntoIterator<Item = &'a ColumnDef>,
1045        It::IntoIter: Clone,
1046    {
1047        out.push_str(",\nPRIMARY KEY (");
1048        separated_by(
1049            out,
1050            primary_key,
1051            |out, col| {
1052                self.write_identifier(
1053                    &mut context
1054                        .switch_fragment(Fragment::SqlCreateTablePrimaryKey)
1055                        .current,
1056                    out,
1057                    col.name(),
1058                    true,
1059                );
1060            },
1061            ", ",
1062        );
1063        out.push(')');
1064    }
1065
1066    /// Write referential action.
1067    fn write_create_table_references_action(
1068        &self,
1069        _context: &mut Context,
1070        out: &mut DynQuery,
1071        action: &Action,
1072    ) {
1073        out.push_str(match action {
1074            Action::NoAction => "NO ACTION",
1075            Action::Restrict => "RESTRICT",
1076            Action::Cascade => "CASCADE",
1077            Action::SetNull => "SET NULL",
1078            Action::SetDefault => "SET DEFAULT",
1079        });
1080    }
1081
1082    fn write_column_comment_inline(
1083        &self,
1084        _context: &mut Context,
1085        _out: &mut DynQuery,
1086        _column: &ColumnDef,
1087    ) where
1088        Self: Sized,
1089    {
1090    }
1091
1092    /// Write column comments.
1093    fn write_column_comments_statements<E>(&self, context: &mut Context, out: &mut DynQuery)
1094    where
1095        Self: Sized,
1096        E: Entity,
1097    {
1098        let mut context = context.switch_fragment(Fragment::SqlCommentOnColumn);
1099        context.current.qualify_columns = true;
1100        for c in E::columns().iter().filter(|c| !c.comment.is_empty()) {
1101            out.push_str("\nCOMMENT ON COLUMN ");
1102            self.write_column_ref(&mut context.current, out, c.into());
1103            out.push_str(" IS ");
1104            self.write_string(&mut context.current, out, c.comment);
1105            out.push(';');
1106        }
1107    }
1108
1109    /// Write DROP TABLE statement.
1110    fn write_drop_table<E>(&self, out: &mut DynQuery, if_exists: bool)
1111    where
1112        Self: Sized,
1113        E: Entity,
1114    {
1115        let table = E::table();
1116        out.buffer()
1117            .reserve(24 + table.schema.len() + table.name.len());
1118        if !out.is_empty() {
1119            out.push('\n');
1120        }
1121        out.push_str("DROP TABLE ");
1122        let mut context = Context::new(Fragment::SqlDropTable, E::qualified_columns());
1123        if if_exists {
1124            out.push_str("IF EXISTS ");
1125        }
1126        self.write_table_ref(&mut context, out, table);
1127        out.push(';');
1128    }
1129
1130    /// Write SELECT statement.
1131    fn write_select<'a, Data>(&self, out: &mut DynQuery, query: &impl SelectQuery<Data>)
1132    where
1133        Self: Sized,
1134        Data: Dataset + 'a,
1135    {
1136        let Some(from) = query.get_from() else {
1137            log::error!("The query does not have the FROM clause");
1138            return;
1139        };
1140        let columns = query.get_select();
1141        let columns_count = columns.clone().into_iter().count();
1142        out.buffer().reserve(128 + columns_count * 32);
1143        if !out.is_empty() {
1144            out.push('\n');
1145        }
1146        out.push_str("SELECT ");
1147        let mut context = Context::new(Fragment::SqlSelect, Data::qualified_columns());
1148        if columns_count != 0 {
1149            separated_by(
1150                out,
1151                columns.clone(),
1152                |out, col| {
1153                    col.write_query(self, &mut context, out);
1154                },
1155                ", ",
1156            );
1157        } else {
1158            out.push('*');
1159        }
1160        out.push_str("\nFROM ");
1161        from.write_table_name(
1162            self,
1163            &mut context.switch_fragment(Fragment::SqlSelectFrom).current,
1164            out,
1165        );
1166        if let Some(condition) = query.get_where()
1167            && !condition.accept_visitor(&mut IsTrue, self, &mut context, out)
1168        {
1169            out.push_str("\nWHERE ");
1170            condition.write_query(
1171                self,
1172                &mut context.switch_fragment(Fragment::SqlSelectWhere).current,
1173                out,
1174            );
1175        }
1176        let mut group_by = query.get_group_by().peekable();
1177        if group_by.peek().is_some() {
1178            out.push_str("\nGROUP BY ");
1179            let mut context = context.switch_fragment(Fragment::SqlSelectGroupBy);
1180            separated_by(
1181                out,
1182                group_by,
1183                |out, col| {
1184                    col.write_query(self, &mut context.current, out);
1185                },
1186                ", ",
1187            );
1188        }
1189        if let Some(having) = query.get_having() {
1190            out.push_str("\nHAVING ");
1191            having.write_query(
1192                self,
1193                &mut context.switch_fragment(Fragment::SqlSelectHaving).current,
1194                out,
1195            );
1196        }
1197        let mut order_by = query.get_order_by().peekable();
1198        if order_by.peek().is_some() {
1199            out.push_str("\nORDER BY ");
1200            let mut context = context.switch_fragment(Fragment::SqlSelectOrderBy);
1201            separated_by(
1202                out,
1203                order_by,
1204                |out, col| {
1205                    col.write_query(self, &mut context.current, out);
1206                },
1207                ", ",
1208            );
1209        }
1210        if let Some(limit) = query.get_limit() {
1211            let _ = write!(out, "\nLIMIT {limit}");
1212        }
1213        out.push(';');
1214    }
1215
1216    /// Write INSERT statement.
1217    fn write_insert<It>(&self, out: &mut DynQuery, entities: It, update: bool)
1218    where
1219        Self: Sized,
1220        It: IntoIterator,
1221        It::Item: AsEntity,
1222    {
1223        type E<It> = <<It as IntoIterator>::Item as AsEntity>::Entity;
1224        let table = E::<It>::table();
1225        let mut entities = entities.into_iter().peekable();
1226        if entities.peek().is_none() {
1227            return;
1228        };
1229        let cols = E::<It>::columns().len();
1230        let rows = entities.size_hint().0.max(1);
1231        out.buffer().reserve(128 + (cols + rows * cols) * 16);
1232        if !out.is_empty() {
1233            out.push('\n');
1234        }
1235        out.push_str("INSERT INTO ");
1236        let mut context = Context::new(Fragment::SqlInsertInto, E::<It>::qualified_columns());
1237        self.write_table_ref(&mut context, out, table);
1238        out.push_str(" (");
1239        separated_by(
1240            out,
1241            E::<It>::columns().iter(),
1242            |out, col| {
1243                self.write_identifier(&mut context, out, col.name(), true);
1244            },
1245            ", ",
1246        );
1247        out.push_str(") VALUES");
1248        let mut context = context.switch_fragment(Fragment::SqlInsertIntoValues);
1249        separated_by(
1250            out,
1251            entities,
1252            |out, entity| {
1253                out.push_str("\n(");
1254                entity
1255                    .as_entity()
1256                    .write_query(self.as_dyn(), &mut context.current, out);
1257                out.push(')');
1258            },
1259            ",",
1260        );
1261        if update {
1262            self.write_insert_update_fragment::<E<It>>(
1263                &mut context.current,
1264                out,
1265                E::<It>::columns().iter(),
1266            );
1267        }
1268        out.push(';');
1269    }
1270
1271    /// Write ON CONFLICT DO UPDATE fragment for upsert.
1272    fn write_insert_update_fragment<'a, E>(
1273        &self,
1274        context: &mut Context,
1275        out: &mut DynQuery,
1276        columns: impl Iterator<Item = &'a ColumnDef> + Clone,
1277    ) where
1278        Self: Sized,
1279        E: Entity,
1280    {
1281        let pk = E::primary_key_def();
1282        if pk.is_empty() {
1283            return;
1284        }
1285        out.push_str("\nON CONFLICT");
1286        context.fragment = Fragment::SqlInsertIntoOnConflict;
1287        out.push_str(" (");
1288        separated_by(
1289            out,
1290            pk,
1291            |out, col| {
1292                self.write_identifier(context, out, col.name(), true);
1293            },
1294            ", ",
1295        );
1296        out.push(')');
1297        let mut update_cols = columns
1298            .filter(|c| c.primary_key == PrimaryKeyType::None)
1299            .peekable();
1300        if update_cols.peek().is_some() {
1301            out.push_str(" DO UPDATE SET\n");
1302            separated_by(
1303                out,
1304                update_cols,
1305                |out, col| {
1306                    self.write_identifier(context, out, col.name(), true);
1307                    out.push_str(" = EXCLUDED.");
1308                    self.write_identifier(context, out, col.name(), true);
1309                },
1310                ",\n",
1311            );
1312        } else {
1313            out.push_str(" DO NOTHING");
1314        }
1315    }
1316
1317    /// Write DELETE statement.
1318    fn write_delete<E>(&self, out: &mut DynQuery, condition: impl Expression)
1319    where
1320        Self: Sized,
1321        E: Entity,
1322    {
1323        let table = E::table();
1324        out.buffer().reserve(128);
1325        if !out.is_empty() {
1326            out.push('\n');
1327        }
1328        out.push_str("DELETE FROM ");
1329        let mut context = Context::new(Fragment::SqlDeleteFrom, E::qualified_columns());
1330        self.write_table_ref(&mut context, out, table);
1331        out.push_str("\nWHERE ");
1332        condition.write_query(
1333            self,
1334            &mut context
1335                .switch_fragment(Fragment::SqlDeleteFromWhere)
1336                .current,
1337            out,
1338        );
1339        out.push(';');
1340    }
1341}
1342
1343/// Generic SQL writer.
1344pub struct GenericSqlWriter;
1345impl GenericSqlWriter {
1346    /// New generic writer.
1347    pub fn new() -> Self {
1348        Self {}
1349    }
1350}
1351impl SqlWriter for GenericSqlWriter {
1352    fn as_dyn(&self) -> &dyn SqlWriter {
1353        self
1354    }
1355}