tank_postgres/
sql_writer.rs

1use std::fmt::Write;
2use tank_core::{Context, SqlWriter, Value, future::Either, separated_by};
3use time::{Date, PrimitiveDateTime, Time};
4
5pub struct PostgresSqlWriter {}
6
7impl SqlWriter for PostgresSqlWriter {
8    fn as_dyn(&self) -> &dyn SqlWriter {
9        self
10    }
11
12    fn write_column_type(&self, context: &mut Context, buff: &mut String, value: &Value) {
13        match value {
14            Value::Boolean(..) => buff.push_str("BOOLEAN"),
15            Value::Int8(..) => buff.push_str("SMALLINT"),
16            Value::Int16(..) => buff.push_str("SMALLINT"),
17            Value::Int32(..) => buff.push_str("INTEGER"),
18            Value::Int64(..) => buff.push_str("BIGINT"),
19            Value::Int128(..) => buff.push_str("NUMERIC(38)"),
20            Value::UInt8(..) => buff.push_str("SMALLINT"),
21            Value::UInt16(..) => buff.push_str("INTEGER"),
22            Value::UInt32(..) => buff.push_str("BIGINT"),
23            Value::UInt64(..) => buff.push_str("NUMERIC(19)"),
24            Value::UInt128(..) => buff.push_str("NUMERIC(38)"),
25            Value::Float32(..) => buff.push_str("FLOAT4"),
26            Value::Float64(..) => buff.push_str("FLOAT8"),
27            Value::Decimal(.., precision, scale) => {
28                buff.push_str("NUMERIC");
29                if (precision, scale) != (&0, &0) {
30                    let _ = write!(buff, "({},{})", precision, scale);
31                }
32            }
33            Value::Char(..) => buff.push_str("CHARACTER(1)"),
34            Value::Varchar(..) => buff.push_str("TEXT"),
35            Value::Blob(..) => buff.push_str("BYTEA"),
36            Value::Date(..) => buff.push_str("DATE"),
37            Value::Time(..) => buff.push_str("TIME"),
38            Value::Timestamp(..) => buff.push_str("TIMESTAMP"),
39            Value::TimestampWithTimezone(..) => buff.push_str("TIMESTAMP WITH TIME ZONE"),
40            Value::Interval(..) => buff.push_str("INTERVAL"),
41            Value::Uuid(..) => buff.push_str("UUID"),
42            Value::Array(.., inner, size) => {
43                self.write_column_type(context, buff, inner);
44                let _ = write!(buff, "[{}]", size);
45            }
46            Value::List(.., inner) => {
47                self.write_column_type(context, buff, inner);
48                buff.push_str("[]");
49            }
50            _ => log::error!(
51                "Unexpected tank::Value, variant {:?} is not supported",
52                value
53            ),
54        };
55    }
56
57    fn write_value_blob(&self, _context: &mut Context, buff: &mut String, value: &[u8]) {
58        buff.push_str("'\\x");
59        for b in value {
60            let _ = write!(buff, "{:X}", b);
61        }
62        buff.push('\'');
63    }
64
65    fn write_value_date(
66        &self,
67        _context: &mut Context,
68        buff: &mut String,
69        value: &Date,
70        timestamp: bool,
71    ) {
72        let (l, r) = if timestamp {
73            ("", "")
74        } else {
75            ("'", "'::DATE")
76        };
77        let (year, suffix) = if !timestamp && value.year() <= 0 {
78            // Year 0 in Postgres is 1 BC
79            (value.year().abs() + 1, " BC")
80        } else {
81            (value.year(), "")
82        };
83        let _ = write!(
84            buff,
85            "{l}{:04}-{:02}-{:02}{suffix}{r}",
86            year,
87            value.month() as u8,
88            value.day()
89        );
90    }
91
92    fn write_value_time(
93        &self,
94        _context: &mut Context,
95        buff: &mut String,
96        value: &Time,
97        timestamp: bool,
98    ) {
99        let mut subsecond = value.nanosecond();
100        let mut width = 9;
101        while width > 1 && subsecond % 10 == 0 {
102            subsecond /= 10;
103            width -= 1;
104        }
105        let (l, r) = if timestamp {
106            ("", "")
107        } else {
108            ("'", "'::TIME")
109        };
110        let _ = write!(
111            buff,
112            "{l}{:02}:{:02}:{:02}.{:0width$}{r}",
113            value.hour(),
114            value.minute(),
115            value.second(),
116            subsecond
117        );
118    }
119
120    fn write_value_timestamp(
121        &self,
122        context: &mut Context,
123        buff: &mut String,
124        value: &PrimitiveDateTime,
125    ) {
126        buff.push('\'');
127        self.write_value_date(context, buff, &value.date(), true);
128        buff.push('T');
129        self.write_value_time(context, buff, &value.time(), true);
130        if value.date().year() <= 0 {
131            buff.push_str(" BC");
132        }
133        buff.push_str("'::TIMESTAMP");
134    }
135
136    fn write_value_list<'a>(
137        &self,
138        context: &mut Context,
139        buff: &mut String,
140        value: Either<&Box<[Value]>, &Vec<Value>>,
141        ty: &Value,
142    ) {
143        buff.push_str("ARRAY[");
144        separated_by(
145            buff,
146            match value {
147                Either::Left(v) => v.iter(),
148                Either::Right(v) => v.iter(),
149            },
150            |buff, v| {
151                self.write_value(context, buff, v);
152            },
153            ",",
154        );
155        buff.push_str("]::");
156        self.write_column_type(context, buff, ty);
157    }
158
159    fn write_expression_operand_question_mark(&self, context: &mut Context, buff: &mut String) {
160        context.counter += 1;
161        let _ = write!(buff, "${}", context.counter);
162    }
163}