Skip to main content

tank_core/writer/
sql_writer.rs

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