34
34
35
35
#include < iomanip>
36
36
#include < iostream>
37
+ #include < regex>
37
38
#include < sstream>
38
39
39
40
#include " detail/prepared_statement_handle.h"
@@ -225,7 +226,6 @@ namespace sqlpp
225
226
}
226
227
}
227
228
228
- // same parsing logic as SQLite connector
229
229
// PostgreSQL will return one of those (using the default ISO client):
230
230
//
231
231
// 2010-10-11 01:02:03 - ISO timestamp without timezone
@@ -234,71 +234,6 @@ namespace sqlpp
234
234
// 1992-10-10 01:02:03-06:30 - for some timezones with non-hour offset
235
235
// 1900-01-01 - date only
236
236
// we do not support time-only values !
237
- namespace detail
238
- {
239
- inline auto check_first_digit (const char * text, bool digitFlag) -> bool
240
- {
241
- if (digitFlag)
242
- {
243
- if (not std::isdigit (*text))
244
- {
245
- return false ;
246
- }
247
- }
248
- else
249
- {
250
- if (std::isdigit (*text) or *text == ' \0 ' )
251
- {
252
- return false ;
253
- }
254
- }
255
- return true ;
256
- }
257
-
258
- inline auto check_date_digits (const char * text) -> bool
259
- {
260
- for (const auto digitFlag : {true , true , true , true , false , true , true , false , true , true }) // YYYY-MM-DD
261
- {
262
- if (not check_first_digit (text, digitFlag))
263
- return false ;
264
- ++text;
265
- }
266
- return true ;
267
- }
268
-
269
- inline auto check_time_digits (const char * text) -> bool
270
- {
271
- for (const auto digitFlag : {true , true , false , true , true , false , true , true }) // hh:mm:ss
272
- {
273
- if (not check_first_digit (text, digitFlag))
274
- return false ;
275
- ++text;
276
- }
277
- return true ;
278
- }
279
-
280
- inline auto check_us_digits (const char * text) -> bool
281
- {
282
- for (const auto digitFlag : {true , true , true , true , true , true })
283
- {
284
- if (not check_first_digit (text, digitFlag))
285
- return false ;
286
- ++text;
287
- }
288
- return true ;
289
- }
290
-
291
- inline auto check_tz_digits (const char * text) -> bool
292
- {
293
- for (const auto digitFlag : {false , true , true , false , true , true })
294
- {
295
- if (not check_first_digit (text, digitFlag))
296
- return false ;
297
- ++text;
298
- }
299
- return true ;
300
- }
301
- } // namespace
302
237
303
238
inline void bind_result_t::_bind_date_result (size_t _index, ::sqlpp::chrono::day_point* value, bool * is_null)
304
239
{
@@ -320,16 +255,19 @@ namespace sqlpp
320
255
std::cerr << " PostgreSQL debug: date string: " << date_string << std::endl;
321
256
}
322
257
323
- if (detail::check_date_digits (date_string))
324
- {
325
- const auto ymd =
326
- ::date::year (std::atoi(date_string)) / std::atoi(date_string + 5 ) / std::atoi(date_string + 8 );
327
- *value = ::sqlpp::chrono::day_point (ymd);
328
- }
329
- else
330
- {
331
- if (_handle->debug ())
258
+ static const std::regex rx {" (\\ d{4})-(\\ d{2})-(\\ d{2})" };
259
+ std::cmatch mr;
260
+ if (std::regex_match (date_string, mr, rx)) {
261
+ *value =
262
+ ::sqlpp::chrono::day_point{
263
+ ::date::year{std::atoi (date_string + mr.position (1 ))} / // Year
264
+ std::atoi (date_string + mr.position (2 )) / // Month
265
+ std::atoi (date_string + mr.position (3 )) // Day of month
266
+ };
267
+ } else {
268
+ if (_handle->debug ()) {
332
269
std::cerr << " PostgreSQL debug: got invalid date '" << date_string << " '" << std::endl;
270
+ }
333
271
*value = {};
334
272
}
335
273
}
@@ -339,7 +277,7 @@ namespace sqlpp
339
277
}
340
278
}
341
279
342
- // always returns local time for timestamp with time zone
280
+ // always returns UTC time for timestamp with time zone
343
281
inline void bind_result_t::_bind_date_time_result (size_t _index, ::sqlpp::chrono::microsecond_point* value, bool * is_null)
344
282
{
345
283
auto index = static_cast <int >(_index);
@@ -358,97 +296,91 @@ namespace sqlpp
358
296
{
359
297
std::cerr << " PostgreSQL debug: got date_time string: " << date_string << std::endl;
360
298
}
361
- if (detail::check_date_digits (date_string))
362
- {
363
- const auto ymd =
364
- ::date::year (std::atoi(date_string)) / std::atoi(date_string + 5 ) / std::atoi(date_string + 8 );
365
- *value = ::sqlpp::chrono::day_point (ymd);
366
- }
367
- else
368
- {
369
- if (_handle->debug ())
370
- std::cerr << " PostgreSQL debug: got invalid date_time" << std::endl;
371
- *value = {};
372
- return ;
373
- }
374
-
375
- if (std::strlen (date_string) <= 11 )
376
- return ;
377
- const auto time_string = date_string + 11 ; // YYYY-MM-DDT
378
- if (detail::check_time_digits (time_string))
379
- {
380
- *value += std::chrono::hours (std::atoi (time_string)) + std::chrono::minutes (std::atoi (time_string + 3 )) +
381
- std::chrono::seconds (std::atoi (time_string + 6 ));
382
- }
383
- else
384
- {
385
- return ;
386
- }
387
299
388
- if (std::strlen (time_string) <= 9 )
389
- return ;
390
- auto us_string = time_string + 9 ; // hh:mm:ss.
391
- int usec = 0 ;
392
- for (size_t i = 0u ; i < 6u ; ++i)
393
- {
394
- if (std::isdigit (us_string[0 ]))
395
- {
396
- usec = 10 * usec + (us_string[0 ] - ' 0' );
397
- ++us_string;
300
+ static const std::regex rx {
301
+ " (\\ d{4})-(\\ d{2})-(\\ d{2}) "
302
+ " (\\ d{2}):(\\ d{2}):(\\ d{2})(?:\\ .(\\ d{1,6}))?"
303
+ " (?:([+-])(\\ d{2})(?::(\\ d{2})(?::(\\ d{2}))?)?)?"
304
+ };
305
+ std::cmatch mr;
306
+ if (std::regex_match (date_string, mr, rx)) {
307
+ *value =
308
+ ::sqlpp::chrono::day_point{
309
+ ::date::year{std::atoi (date_string + mr.position (1 ))} / // Year
310
+ std::atoi (date_string + mr.position (2 )) / // Month
311
+ std::atoi (date_string + mr.position (3 )) // Day of month
312
+ } +
313
+ std::chrono::hours{std::atoi (date_string + mr.position (4 ))} + // Hour
314
+ std::chrono::minutes{std::atoi (date_string + mr.position (5 ))} + // Minute
315
+ std::chrono::seconds{std::atoi (date_string + mr.position (6 ))} + // Second
316
+ ::std::chrono::microseconds{ // Microsecond
317
+ mr[7 ].matched ? std::stoi ((mr[7 ].str () + " 000000" ).substr (0 , 6 )) : 0
318
+ };
319
+ if (mr[8 ].matched ) {
320
+ const auto tz_sign = (date_string[mr.position (8 )] == ' +' ) ? 1 : -1 ;
321
+ const auto tz_offset =
322
+ std::chrono::hours{std::atoi (date_string + mr.position (9 ))} +
323
+ std::chrono::minutes{mr[10 ].matched ? std::atoi (date_string + mr.position (10 )) : 0 } +
324
+ std::chrono::seconds{mr[11 ].matched ? std::atoi (date_string + mr.position (11 )) : 0 };
325
+ *value -= tz_sign * tz_offset;
326
+ }
327
+ } else {
328
+ if (_handle->debug ()) {
329
+ std::cerr << " PostgreSQL debug: got invalid date_time '" << date_string << " '" << std::endl;
398
330
}
399
- else
400
- usec *= 10 ;
331
+ *value = {};
401
332
}
402
- *value += ::std::chrono::microseconds (usec);
403
333
}
404
334
}
405
335
406
- // always returns local time for time with time zone
336
+ // always returns UTC time for time with time zone
407
337
inline void bind_result_t::_bind_time_of_day_result (size_t _index, ::std::chrono::microseconds* value, bool * is_null)
408
338
{
409
- auto index = static_cast <int >(_index);
410
- if (_handle->debug ())
411
- {
412
- std::cerr << " PostgreSQL debug: binding time result at index: " << index << std::endl;
413
- }
414
-
415
- *is_null = _handle->result .isNull (_handle->count , index );
339
+ auto index = static_cast <int >(_index);
340
+ if (_handle->debug ())
341
+ {
342
+ std::cerr << " PostgreSQL debug: binding time result at index: " << index << std::endl;
343
+ }
416
344
417
- if (!(*is_null))
418
- {
419
- const auto time_string = _handle->result .getCharPtrValue (_handle->count , index );
345
+ *is_null = _handle->result .isNull (_handle->count , index );
420
346
421
- if (_handle->debug ())
422
- {
423
- std::cerr << " PostgreSQL debug: got time string: " << time_string << std::endl;
424
- }
347
+ if (!(*is_null))
348
+ {
349
+ const auto time_string = _handle->result .getCharPtrValue (_handle->count , index );
425
350
426
- if (detail::check_time_digits (time_string))
427
- {
428
- *value += std::chrono::hours (std::atoi (time_string)) + std::chrono::minutes (std::atoi (time_string + 3 )) +
429
- std::chrono::seconds (std::atoi (time_string + 6 ));
430
- }
431
- else
432
- {
433
- return ;
434
- }
351
+ if (_handle->debug ())
352
+ {
353
+ std::cerr << " PostgreSQL debug: got time string: " << time_string << std::endl;
354
+ }
435
355
436
- if (std::strlen (time_string) <= 9 )
437
- return ;
438
- auto us_string = time_string + 9 ; // hh:mm:ss.
439
- int usec = 0 ;
440
- for (size_t i = 0u ; i < 6u ; ++i)
441
- {
442
- if (std::isdigit (us_string[0 ]))
443
- {
444
- usec = 10 * usec + (us_string[0 ] - ' 0' );
445
- ++us_string;
446
- }
447
- else
448
- usec *= 10 ;
356
+ static const std::regex rx {
357
+ " (\\ d{2}):(\\ d{2}):(\\ d{2})(?:\\ .(\\ d{1,6}))?"
358
+ " (?:([+-])(\\ d{2})(?::(\\ d{2})(?::(\\ d{2}))?)?)?"
359
+ };
360
+ std::cmatch mr;
361
+ if (std::regex_match (time_string, mr, rx)) {
362
+ *value =
363
+ std::chrono::hours{std::atoi (time_string + mr.position (1 ))} + // Hour
364
+ std::chrono::minutes{std::atoi (time_string + mr.position (2 ))} + // Minute
365
+ std::chrono::seconds{std::atoi (time_string + mr.position (3 ))} + // Second
366
+ ::std::chrono::microseconds{ // Microsecond
367
+ mr[4 ].matched ? std::stoi ((mr[4 ].str () + " 000000" ).substr (0 , 6 )) : 0
368
+ };
369
+ if (mr[5 ].matched ) {
370
+ const auto tz_sign = (time_string[mr.position (5 )] == ' +' ) ? 1 : -1 ;
371
+ const auto tz_offset =
372
+ std::chrono::hours{std::atoi (time_string + mr.position (6 ))} +
373
+ std::chrono::minutes{mr[7 ].matched ? std::atoi (time_string + mr.position (7 )) : 0 } +
374
+ std::chrono::seconds{mr[8 ].matched ? std::atoi (time_string + mr.position (8 )) : 0 };
375
+ *value -= tz_sign * tz_offset;
449
376
}
450
- *value += ::std::chrono::microseconds (usec);
377
+ } else {
378
+ if (_handle->debug ()) {
379
+ std::cerr << " PostgreSQL debug: got invalid time '" << time_string << " '" << std::endl;
380
+ }
381
+ *value = {};
451
382
}
383
+ }
452
384
}
453
385
454
386
inline void bind_result_t::_bind_blob_result (size_t _index, const uint8_t ** value, size_t * len)
0 commit comments