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