1use std::fmt::Write;
2use tank_core::{Context, SqlWriter, Value, future::Either, separated_by};
3use time::{Date, OffsetDateTime, 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, out: &mut String, value: &Value) {
13 match value {
14 Value::Boolean(..) => out.push_str("BOOLEAN"),
15 Value::Int8(..) => out.push_str("SMALLINT"),
16 Value::Int16(..) => out.push_str("SMALLINT"),
17 Value::Int32(..) => out.push_str("INTEGER"),
18 Value::Int64(..) => out.push_str("BIGINT"),
19 Value::Int128(..) => out.push_str("NUMERIC(38)"),
20 Value::UInt8(..) => out.push_str("SMALLINT"),
21 Value::UInt16(..) => out.push_str("INTEGER"),
22 Value::UInt32(..) => out.push_str("BIGINT"),
23 Value::UInt64(..) => out.push_str("NUMERIC(19)"),
24 Value::UInt128(..) => out.push_str("NUMERIC(38)"),
25 Value::Float32(..) => out.push_str("FLOAT4"),
26 Value::Float64(..) => out.push_str("FLOAT8"),
27 Value::Decimal(.., precision, scale) => {
28 out.push_str("NUMERIC");
29 if (precision, scale) != (&0, &0) {
30 let _ = write!(out, "({},{})", precision, scale);
31 }
32 }
33 Value::Char(..) => out.push_str("CHAR(1)"),
34 Value::Varchar(..) => out.push_str("TEXT"),
35 Value::Blob(..) => out.push_str("BYTEA"),
36 Value::Date(..) => out.push_str("DATE"),
37 Value::Time(..) => out.push_str("TIME"),
38 Value::Timestamp(..) => out.push_str("TIMESTAMP"),
39 Value::TimestampWithTimezone(..) => out.push_str("TIMESTAMP WITH TIME ZONE"),
40 Value::Interval(..) => out.push_str("INTERVAL"),
41 Value::Uuid(..) => out.push_str("UUID"),
42 Value::Array(.., inner, size) => {
43 self.write_column_type(context, out, inner);
44 let _ = write!(out, "[{}]", size);
45 }
46 Value::List(.., inner) => {
47 self.write_column_type(context, out, inner);
48 out.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, out: &mut String, value: &[u8]) {
58 out.push_str("'\\x");
59 for b in value {
60 let _ = write!(out, "{:X}", b);
61 }
62 out.push('\'');
63 }
64
65 fn write_value_date(
66 &self,
67 _context: &mut Context,
68 out: &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 (value.year().abs() + 1, " BC")
80 } else {
81 (value.year(), "")
82 };
83 let _ = write!(
84 out,
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 out: &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 out,
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 out: &mut String,
124 value: &PrimitiveDateTime,
125 ) {
126 out.push('\'');
127 self.write_value_date(context, out, &value.date(), true);
128 out.push('T');
129 self.write_value_time(context, out, &value.time(), true);
130 if value.date().year() <= 0 {
131 out.push_str(" BC");
132 }
133 out.push_str("'::TIMESTAMP");
134 }
135
136 fn write_value_timestamptz(
137 &self,
138 context: &mut Context,
139 out: &mut String,
140 value: &OffsetDateTime,
141 ) {
142 out.push('\'');
143 self.write_value_date(context, out, &value.date(), true);
144 out.push('T');
145 self.write_value_time(context, out, &value.time(), true);
146 let _ = write!(
147 out,
148 "{:+02}:{:02}",
149 value.offset().whole_hours(),
150 value.offset().whole_minutes()
151 );
152 if value.date().year() <= 0 {
153 out.push_str(" BC");
154 }
155 out.push_str("'::TIMESTAMPTZ");
156 }
157
158 fn write_value_list<'a>(
159 &self,
160 context: &mut Context,
161 out: &mut String,
162 value: Either<&Box<[Value]>, &Vec<Value>>,
163 ty: &Value,
164 ) {
165 out.push_str("ARRAY[");
166 separated_by(
167 out,
168 match value {
169 Either::Left(v) => v.iter(),
170 Either::Right(v) => v.iter(),
171 },
172 |out, v| {
173 self.write_value(context, out, v);
174 },
175 ",",
176 );
177 out.push_str("]::");
178 self.write_column_type(context, out, ty);
179 }
180
181 fn write_expression_operand_question_mark(&self, context: &mut Context, out: &mut String) {
182 context.counter += 1;
183 let _ = write!(out, "${}", context.counter);
184 }
185}