1use bytes::BufMut;
16use chrono::{NaiveDate, NaiveDateTime};
17use pgwire::types::ToSqlText;
18use postgres_types::{IsNull, ToSql, Type};
19use session::session_config::{PGDateOrder, PGDateTimeStyle};
20
21#[derive(Debug)]
22pub struct StylingDate(pub NaiveDate, pub PGDateTimeStyle, pub PGDateOrder);
23
24#[derive(Debug)]
25pub struct StylingDateTime(pub NaiveDateTime, pub PGDateTimeStyle, pub PGDateOrder);
26
27fn date_format_string(style: PGDateTimeStyle, order: PGDateOrder) -> &'static str {
28 match style {
29 PGDateTimeStyle::ISO => "%Y-%m-%d",
30 PGDateTimeStyle::German => "%d.%m.%Y",
31 PGDateTimeStyle::Postgres => match order {
32 PGDateOrder::MDY | PGDateOrder::YMD => "%m-%d-%Y",
33 PGDateOrder::DMY => "%d-%m-%Y",
34 },
35 PGDateTimeStyle::SQL => match order {
36 PGDateOrder::MDY | PGDateOrder::YMD => "%m/%d/%Y",
37 PGDateOrder::DMY => "%d/%m/%Y",
38 },
39 }
40}
41
42fn datetime_format_string(style: PGDateTimeStyle, order: PGDateOrder) -> &'static str {
43 match style {
44 PGDateTimeStyle::ISO => "%Y-%m-%d %H:%M:%S%.6f",
45 PGDateTimeStyle::German => "%d.%m.%Y %H:%M:%S%.6f",
46 PGDateTimeStyle::Postgres => match order {
47 PGDateOrder::MDY | PGDateOrder::YMD => "%a %b %d %H:%M:%S%.6f %Y",
48 PGDateOrder::DMY => "%a %d %b %H:%M:%S%.6f %Y",
49 },
50 PGDateTimeStyle::SQL => match order {
51 PGDateOrder::MDY | PGDateOrder::YMD => "%m/%d/%Y %H:%M:%S%.6f",
52 PGDateOrder::DMY => "%d/%m/%Y %H:%M:%S%.6f",
53 },
54 }
55}
56impl ToSqlText for StylingDate {
57 fn to_sql_text(
58 &self,
59 ty: &Type,
60 out: &mut bytes::BytesMut,
61 ) -> std::result::Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
62 where
63 Self: Sized,
64 {
65 match *ty {
66 Type::DATE => {
67 let fmt = self
68 .0
69 .format(date_format_string(self.1, self.2))
70 .to_string();
71 out.put_slice(fmt.as_bytes());
72 }
73 _ => {
74 self.0.to_sql_text(ty, out)?;
75 }
76 }
77 Ok(IsNull::No)
78 }
79}
80
81impl ToSqlText for StylingDateTime {
82 fn to_sql_text(
83 &self,
84 ty: &Type,
85 out: &mut bytes::BytesMut,
86 ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
87 where
88 Self: Sized,
89 {
90 match *ty {
91 Type::TIMESTAMP => {
92 let fmt = self
93 .0
94 .format(datetime_format_string(self.1, self.2))
95 .to_string();
96 out.put_slice(fmt.as_bytes());
97 }
98 Type::DATE => {
99 let fmt = self
100 .0
101 .format(date_format_string(self.1, self.2))
102 .to_string();
103 out.put_slice(fmt.as_bytes());
104 }
105 _ => {
106 self.0.to_sql_text(ty, out)?;
107 }
108 }
109 Ok(IsNull::No)
110 }
111}
112
113macro_rules! delegate_to_sql {
114 ($delegator:ident, $delegatee:ident) => {
115 impl ToSql for $delegator {
116 fn to_sql(
117 &self,
118 ty: &Type,
119 out: &mut bytes::BytesMut,
120 ) -> std::result::Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
121 self.0.to_sql(ty, out)
122 }
123
124 fn accepts(ty: &Type) -> bool {
125 <$delegatee as ToSql>::accepts(ty)
126 }
127
128 fn to_sql_checked(
129 &self,
130 ty: &Type,
131 out: &mut bytes::BytesMut,
132 ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
133 self.0.to_sql_checked(ty, out)
134 }
135 }
136 };
137}
138
139delegate_to_sql!(StylingDate, NaiveDate);
140delegate_to_sql!(StylingDateTime, NaiveDateTime);
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_styling_date() {
148 let naive_date = NaiveDate::from_ymd_opt(1997, 12, 17).unwrap();
149
150 {
151 let styling_date = StylingDate(naive_date, PGDateTimeStyle::ISO, PGDateOrder::MDY);
152 let expected = "1997-12-17";
153 let mut out = bytes::BytesMut::new();
154 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
155 assert!(matches!(is_null, IsNull::No));
156 assert_eq!(out, expected.as_bytes());
157 }
158
159 {
160 let styling_date = StylingDate(naive_date, PGDateTimeStyle::ISO, PGDateOrder::YMD);
161 let expected = "1997-12-17";
162 let mut out = bytes::BytesMut::new();
163 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
164 assert!(matches!(is_null, IsNull::No));
165 assert_eq!(out, expected.as_bytes());
166 }
167
168 {
169 let styling_date = StylingDate(naive_date, PGDateTimeStyle::ISO, PGDateOrder::DMY);
170 let expected = "1997-12-17";
171 let mut out = bytes::BytesMut::new();
172 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
173 assert!(matches!(is_null, IsNull::No));
174 assert_eq!(out, expected.as_bytes());
175 }
176
177 {
178 let styling_date = StylingDate(naive_date, PGDateTimeStyle::German, PGDateOrder::MDY);
179 let expected = "17.12.1997";
180 let mut out = bytes::BytesMut::new();
181 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
182 assert!(matches!(is_null, IsNull::No));
183 assert_eq!(out, expected.as_bytes());
184 }
185
186 {
187 let styling_date = StylingDate(naive_date, PGDateTimeStyle::German, PGDateOrder::YMD);
188 let expected = "17.12.1997";
189 let mut out = bytes::BytesMut::new();
190 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
191 assert!(matches!(is_null, IsNull::No));
192 assert_eq!(out, expected.as_bytes());
193 }
194
195 {
196 let styling_date = StylingDate(naive_date, PGDateTimeStyle::German, PGDateOrder::DMY);
197 let expected = "17.12.1997";
198 let mut out = bytes::BytesMut::new();
199 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
200 assert!(matches!(is_null, IsNull::No));
201 assert_eq!(out, expected.as_bytes());
202 }
203
204 {
205 let styling_date = StylingDate(naive_date, PGDateTimeStyle::Postgres, PGDateOrder::MDY);
206 let expected = "12-17-1997";
207 let mut out = bytes::BytesMut::new();
208 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
209 assert!(matches!(is_null, IsNull::No));
210 assert_eq!(out, expected.as_bytes());
211 }
212
213 {
214 let styling_date = StylingDate(naive_date, PGDateTimeStyle::Postgres, PGDateOrder::YMD);
215 let expected = "12-17-1997";
216 let mut out = bytes::BytesMut::new();
217 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
218 assert!(matches!(is_null, IsNull::No));
219 assert_eq!(out, expected.as_bytes());
220 }
221
222 {
223 let styling_date = StylingDate(naive_date, PGDateTimeStyle::Postgres, PGDateOrder::DMY);
224 let expected = "17-12-1997";
225 let mut out = bytes::BytesMut::new();
226 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
227 assert!(matches!(is_null, IsNull::No));
228 assert_eq!(out, expected.as_bytes());
229 }
230
231 {
232 let styling_date = StylingDate(naive_date, PGDateTimeStyle::SQL, PGDateOrder::MDY);
233 let expected = "12/17/1997";
234 let mut out = bytes::BytesMut::new();
235 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
236 assert!(matches!(is_null, IsNull::No));
237 assert_eq!(out, expected.as_bytes());
238 }
239
240 {
241 let styling_date = StylingDate(naive_date, PGDateTimeStyle::SQL, PGDateOrder::YMD);
242 let expected = "12/17/1997";
243 let mut out = bytes::BytesMut::new();
244 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
245 assert!(matches!(is_null, IsNull::No));
246 assert_eq!(out, expected.as_bytes());
247 }
248
249 {
250 let styling_date = StylingDate(naive_date, PGDateTimeStyle::SQL, PGDateOrder::DMY);
251 let expected = "17/12/1997";
252 let mut out = bytes::BytesMut::new();
253 let is_null = styling_date.to_sql_text(&Type::DATE, &mut out).unwrap();
254 assert!(matches!(is_null, IsNull::No));
255 assert_eq!(out, expected.as_bytes());
256 }
257 }
258
259 #[test]
260 fn test_styling_datetime() {
261 let input =
262 NaiveDateTime::parse_from_str("2021-09-01 12:34:56.789012", "%Y-%m-%d %H:%M:%S%.f")
263 .unwrap();
264
265 {
266 let styling_datetime = StylingDateTime(input, PGDateTimeStyle::ISO, PGDateOrder::MDY);
267 let expected = "2021-09-01 12:34:56.789012";
268 let mut out = bytes::BytesMut::new();
269 let is_null = styling_datetime
270 .to_sql_text(&Type::TIMESTAMP, &mut out)
271 .unwrap();
272 assert!(matches!(is_null, IsNull::No));
273 assert_eq!(out, expected.as_bytes());
274 }
275
276 {
277 let styling_datetime = StylingDateTime(input, PGDateTimeStyle::ISO, PGDateOrder::YMD);
278 let expected = "2021-09-01 12:34:56.789012";
279 let mut out = bytes::BytesMut::new();
280 let is_null = styling_datetime
281 .to_sql_text(&Type::TIMESTAMP, &mut out)
282 .unwrap();
283 assert!(matches!(is_null, IsNull::No));
284 assert_eq!(out, expected.as_bytes());
285 }
286
287 {
288 let styling_datetime = StylingDateTime(input, PGDateTimeStyle::ISO, PGDateOrder::DMY);
289 let expected = "2021-09-01 12:34:56.789012";
290 let mut out = bytes::BytesMut::new();
291 let is_null = styling_datetime
292 .to_sql_text(&Type::TIMESTAMP, &mut out)
293 .unwrap();
294 assert!(matches!(is_null, IsNull::No));
295 assert_eq!(out, expected.as_bytes());
296 }
297
298 {
299 let styling_datetime =
300 StylingDateTime(input, PGDateTimeStyle::German, PGDateOrder::MDY);
301 let expected = "01.09.2021 12:34:56.789012";
302 let mut out = bytes::BytesMut::new();
303 let is_null = styling_datetime
304 .to_sql_text(&Type::TIMESTAMP, &mut out)
305 .unwrap();
306 assert!(matches!(is_null, IsNull::No));
307 assert_eq!(out, expected.as_bytes());
308 }
309
310 {
311 let styling_datetime =
312 StylingDateTime(input, PGDateTimeStyle::German, PGDateOrder::YMD);
313 let expected = "01.09.2021 12:34:56.789012";
314 let mut out = bytes::BytesMut::new();
315 let is_null = styling_datetime
316 .to_sql_text(&Type::TIMESTAMP, &mut out)
317 .unwrap();
318 assert!(matches!(is_null, IsNull::No));
319 assert_eq!(out, expected.as_bytes());
320 }
321
322 {
323 let styling_datetime =
324 StylingDateTime(input, PGDateTimeStyle::German, PGDateOrder::DMY);
325 let expected = "01.09.2021 12:34:56.789012";
326 let mut out = bytes::BytesMut::new();
327 let is_null = styling_datetime
328 .to_sql_text(&Type::TIMESTAMP, &mut out)
329 .unwrap();
330 assert!(matches!(is_null, IsNull::No));
331 assert_eq!(out, expected.as_bytes());
332 }
333
334 {
335 let styling_datetime =
336 StylingDateTime(input, PGDateTimeStyle::Postgres, PGDateOrder::MDY);
337 let expected = "Wed Sep 01 12:34:56.789012 2021";
338 let mut out = bytes::BytesMut::new();
339 let is_null = styling_datetime
340 .to_sql_text(&Type::TIMESTAMP, &mut out)
341 .unwrap();
342 assert!(matches!(is_null, IsNull::No));
343 assert_eq!(out, expected.as_bytes());
344 }
345
346 {
347 let styling_datetime =
348 StylingDateTime(input, PGDateTimeStyle::Postgres, PGDateOrder::YMD);
349 let expected = "Wed Sep 01 12:34:56.789012 2021";
350 let mut out = bytes::BytesMut::new();
351 let is_null = styling_datetime
352 .to_sql_text(&Type::TIMESTAMP, &mut out)
353 .unwrap();
354 assert!(matches!(is_null, IsNull::No));
355 assert_eq!(out, expected.as_bytes());
356 }
357
358 {
359 let styling_datetime =
360 StylingDateTime(input, PGDateTimeStyle::Postgres, PGDateOrder::DMY);
361 let expected = "Wed 01 Sep 12:34:56.789012 2021";
362 let mut out = bytes::BytesMut::new();
363 let is_null = styling_datetime
364 .to_sql_text(&Type::TIMESTAMP, &mut out)
365 .unwrap();
366 assert!(matches!(is_null, IsNull::No));
367 assert_eq!(out, expected.as_bytes());
368 }
369
370 {
371 let styling_datetime = StylingDateTime(input, PGDateTimeStyle::SQL, PGDateOrder::MDY);
372 let expected = "09/01/2021 12:34:56.789012";
373 let mut out = bytes::BytesMut::new();
374 let is_null = styling_datetime
375 .to_sql_text(&Type::TIMESTAMP, &mut out)
376 .unwrap();
377 assert!(matches!(is_null, IsNull::No));
378 assert_eq!(out, expected.as_bytes());
379 }
380
381 {
382 let styling_datetime = StylingDateTime(input, PGDateTimeStyle::SQL, PGDateOrder::YMD);
383 let expected = "09/01/2021 12:34:56.789012";
384 let mut out = bytes::BytesMut::new();
385 let is_null = styling_datetime
386 .to_sql_text(&Type::TIMESTAMP, &mut out)
387 .unwrap();
388 assert!(matches!(is_null, IsNull::No));
389 assert_eq!(out, expected.as_bytes());
390 }
391
392 {
393 let styling_datetime = StylingDateTime(input, PGDateTimeStyle::SQL, PGDateOrder::DMY);
394 let expected = "01/09/2021 12:34:56.789012";
395 let mut out = bytes::BytesMut::new();
396 let is_null = styling_datetime
397 .to_sql_text(&Type::TIMESTAMP, &mut out)
398 .unwrap();
399 assert!(matches!(is_null, IsNull::No));
400 assert_eq!(out, expected.as_bytes());
401 }
402 }
403}