tank_core/writer/
sql_writer.rs

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