mirror of
https://github.com/rbock/sqlpp11.git
synced 2024-11-15 20:31:16 +08:00
Unify date/time value parsing in connectors (#517)
* Move the PostgreSQL date/time parsing code to common functions in sqlpp::detail * Use the common date/time-parsing functions to parse the MySQL date/time results. * Use regular expressions to parse date/time strings in the SQLite3 connector
This commit is contained in:
parent
fdbfbc345e
commit
d0dc081625
128
include/sqlpp11/detail/parse_date_time.h
Normal file
128
include/sqlpp11/detail/parse_date_time.h
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2023, Vesselin Atanasov
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
namespace sqlpp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
// Parse a date string formatted as YYYY-MM-DD
|
||||||
|
//
|
||||||
|
inline bool parse_string_date(::sqlpp::chrono::day_point& value, const char* date_string)
|
||||||
|
{
|
||||||
|
static const std::regex rx{"(\\d{4})-(\\d{2})-(\\d{2})"};
|
||||||
|
std::cmatch mr;
|
||||||
|
if (std::regex_match(date_string, mr, rx) == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = ::sqlpp::chrono::day_point{
|
||||||
|
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
||||||
|
std::atoi(date_string + mr.position(2)) / // Month
|
||||||
|
std::atoi(date_string + mr.position(3)) // Day of month
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a date string formatted as YYYY-MM-DD HH:MM:SS.US TZ
|
||||||
|
// .US are optional fractional seconds, up to 6 digits in length
|
||||||
|
// TZ is an optional time zone offset formatted as +HH[:MM] or -HH[:MM]
|
||||||
|
//
|
||||||
|
inline bool parse_string_date_time(::sqlpp::chrono::microsecond_point& value, const char* date_time_string)
|
||||||
|
{
|
||||||
|
static const std::regex rx{
|
||||||
|
"(\\d{4})-(\\d{2})-(\\d{2}) "
|
||||||
|
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
||||||
|
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
||||||
|
};
|
||||||
|
std::cmatch mr;
|
||||||
|
if (std::regex_match(date_time_string, mr, rx) == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value =
|
||||||
|
::sqlpp::chrono::day_point{
|
||||||
|
::date::year{std::atoi(date_time_string + mr.position(1))} / // Year
|
||||||
|
std::atoi(date_time_string + mr.position(2)) / // Month
|
||||||
|
std::atoi(date_time_string + mr.position(3)) // Day of month
|
||||||
|
} +
|
||||||
|
std::chrono::hours{std::atoi(date_time_string + mr.position(4))} + // Hour
|
||||||
|
std::chrono::minutes{std::atoi(date_time_string + mr.position(5))} + // Minute
|
||||||
|
std::chrono::seconds{std::atoi(date_time_string + mr.position(6))} + // Second
|
||||||
|
::std::chrono::microseconds{ // Second fraction
|
||||||
|
mr[7].matched ? std::stoi((mr[7].str() + "000000").substr(0, 6)) : 0
|
||||||
|
};
|
||||||
|
if (mr[8].matched)
|
||||||
|
{
|
||||||
|
const auto tz_sign = (date_time_string[mr.position(8)] == '+') ? 1 : -1;
|
||||||
|
const auto tz_offset =
|
||||||
|
std::chrono::hours{std::atoi(date_time_string + mr.position(9))} +
|
||||||
|
std::chrono::minutes{mr[10].matched ? std::atoi(date_time_string + mr.position(10)) : 0} +
|
||||||
|
std::chrono::seconds{mr[11].matched ? std::atoi(date_time_string + mr.position(11)) : 0};
|
||||||
|
value -= tz_sign * tz_offset;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a time string formatted as HH:MM:SS[.US][ TZ]
|
||||||
|
// .US is up to 6 digits in length
|
||||||
|
// TZ is an optional time zone offset formatted as +HH[:MM] or -HH[:MM]
|
||||||
|
//
|
||||||
|
inline bool parse_string_time_of_day(::std::chrono::microseconds& value, const char* time_string)
|
||||||
|
{
|
||||||
|
static const std::regex rx{
|
||||||
|
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
||||||
|
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
||||||
|
};
|
||||||
|
std::cmatch mr;
|
||||||
|
if (std::regex_match (time_string, mr, rx) == false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value =
|
||||||
|
std::chrono::hours{std::atoi(time_string + mr.position(1))} + // Hour
|
||||||
|
std::chrono::minutes{std::atoi(time_string + mr.position(2))} + // Minute
|
||||||
|
std::chrono::seconds{std::atoi(time_string + mr.position(3))} + // Second
|
||||||
|
::std::chrono::microseconds{ // Second fraction
|
||||||
|
mr[4].matched ? std::stoi((mr[4].str() + "000000").substr(0, 6)) : 0
|
||||||
|
};
|
||||||
|
if (mr[5].matched)
|
||||||
|
{
|
||||||
|
const auto tz_sign = (time_string[mr.position(5)] == '+') ? 1 : -1;
|
||||||
|
const auto tz_offset =
|
||||||
|
std::chrono::hours{std::atoi(time_string + mr.position(6))} +
|
||||||
|
std::chrono::minutes{mr[7].matched ? std::atoi(time_string + mr.position(7)) : 0} +
|
||||||
|
std::chrono::seconds{mr[8].matched ? std::atoi(time_string + mr.position(8)) : 0};
|
||||||
|
value -= tz_sign * tz_offset;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace sqlpp
|
@ -31,6 +31,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sqlpp11/chrono.h>
|
#include <sqlpp11/chrono.h>
|
||||||
|
#include <sqlpp11/detail/parse_date_time.h>
|
||||||
#include <sqlpp11/exception.h>
|
#include <sqlpp11/exception.h>
|
||||||
#include <sqlpp11/mysql/detail/result_handle.h>
|
#include <sqlpp11/mysql/detail/result_handle.h>
|
||||||
#include <sqlpp11/mysql/sqlpp_mysql.h>
|
#include <sqlpp11/mysql/sqlpp_mysql.h>
|
||||||
@ -40,50 +41,6 @@ namespace sqlpp
|
|||||||
{
|
{
|
||||||
namespace mysql
|
namespace mysql
|
||||||
{
|
{
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
inline auto check_first_digit(const char* text, bool digit_flag) -> bool
|
|
||||||
{
|
|
||||||
if (digit_flag)
|
|
||||||
{
|
|
||||||
if (not std::isdigit(*text))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (std::isdigit(*text) or *text == '\0')
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto check_date_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digit_flag : {true, true, true, true, false, true, true, false, true, true}) // YYYY-MM-DD
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digit_flag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto check_time_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digit_flag : {true, true, false, true, true, false, true, true}) // hh:mm:ss
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digit_flag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
class char_result_t
|
class char_result_t
|
||||||
{
|
{
|
||||||
std::unique_ptr<detail::result_handle> _handle;
|
std::unique_ptr<detail::result_handle> _handle;
|
||||||
@ -184,10 +141,10 @@ namespace sqlpp
|
|||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl;
|
std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl;
|
||||||
|
|
||||||
|
*value = {};
|
||||||
*is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr);
|
*is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr);
|
||||||
if (*is_null)
|
if (*is_null)
|
||||||
{
|
{
|
||||||
*value = {};
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,16 +152,10 @@ namespace sqlpp
|
|||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "MySQL debug: date string: " << date_string << std::endl;
|
std::cerr << "MySQL debug: date string: " << date_string << std::endl;
|
||||||
|
|
||||||
if (detail::check_date_digits(date_string))
|
if (::sqlpp::detail::parse_string_date(*value, date_string) == false)
|
||||||
{
|
|
||||||
const auto ymd = ::date::year(std::atoi(date_string)) / atoi(date_string + 5) / atoi(date_string + 8);
|
|
||||||
*value = ::sqlpp::chrono::day_point(ymd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "MySQL debug: invalid date result: " << date_string << std::endl;
|
std::cerr << "MySQL debug: invalid date result: " << date_string << std::endl;
|
||||||
*value = {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,10 +164,10 @@ namespace sqlpp
|
|||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl;
|
std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl;
|
||||||
|
|
||||||
|
*value = {};
|
||||||
*is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr);
|
*is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr);
|
||||||
if (*is_null)
|
if (*is_null)
|
||||||
{
|
{
|
||||||
*value = {};
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,41 +175,10 @@ namespace sqlpp
|
|||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "MySQL debug: date_time string: " << date_time_string << std::endl;
|
std::cerr << "MySQL debug: date_time string: " << date_time_string << std::endl;
|
||||||
|
|
||||||
if (detail::check_date_digits(date_time_string))
|
if (::sqlpp::detail::parse_string_date_time(*value, date_time_string) == false)
|
||||||
{
|
|
||||||
const auto ymd =
|
|
||||||
::date::year(std::atoi(date_time_string)) / atoi(date_time_string + 5) / atoi(date_time_string + 8);
|
|
||||||
*value = ::sqlpp::chrono::day_point(ymd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "MySQL debug: invalid date_time result: " << date_time_string << std::endl;
|
std::cerr << "MySQL debug: invalid date_time result: " << date_time_string << std::endl;
|
||||||
*value = {};
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto time_string = date_time_string + 11; // YYYY-MM-DDT
|
|
||||||
if (detail::check_time_digits(time_string))
|
|
||||||
{
|
|
||||||
*value += ::std::chrono::hours(std::atoi(time_string + 0)) +
|
|
||||||
std::chrono::minutes(std::atoi(time_string + 3)) + std::chrono::seconds(std::atoi(time_string + 6));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto mu_string = time_string + 8; // hh:mm:ss
|
|
||||||
if (mu_string[0] == '\0')
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto factor = 100 * 1000;
|
|
||||||
for (auto i = 1u; i <= 6u and std::isdigit(mu_string[i]); ++i, factor /= 10)
|
|
||||||
{
|
|
||||||
*value += ::std::chrono::microseconds(factor * (mu_string[i] - '0'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,13 +27,13 @@
|
|||||||
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <sqlpp11/chrono.h>
|
#include <sqlpp11/chrono.h>
|
||||||
#include <sqlpp11/data_types.h>
|
#include <sqlpp11/data_types.h>
|
||||||
|
#include <sqlpp11/detail/parse_date_time.h>
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <regex>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "detail/prepared_statement_handle.h"
|
#include "detail/prepared_statement_handle.h"
|
||||||
@ -227,36 +227,24 @@ namespace sqlpp
|
|||||||
std::cerr << "PostgreSQL debug: binding date result at index: " << index << std::endl;
|
std::cerr << "PostgreSQL debug: binding date result at index: " << index << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*value = {};
|
||||||
*is_null = _handle->result.is_null(_handle->count, index);
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
|
if (*is_null)
|
||||||
if (!(*is_null))
|
|
||||||
{
|
{
|
||||||
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl;
|
||||||
|
}
|
||||||
|
if (::sqlpp::detail::parse_string_date(*value, date_string) == false)
|
||||||
|
{
|
||||||
if (_handle->debug())
|
if (_handle->debug())
|
||||||
{
|
{
|
||||||
std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl;
|
std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::regex rx {"(\\d{4})-(\\d{2})-(\\d{2})"};
|
|
||||||
std::cmatch mr;
|
|
||||||
if (std::regex_match (date_string, mr, rx)) {
|
|
||||||
*value =
|
|
||||||
::sqlpp::chrono::day_point{
|
|
||||||
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
|
||||||
std::atoi(date_string + mr.position(2)) / // Month
|
|
||||||
std::atoi(date_string + mr.position(3)) // Day of month
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
if (_handle->debug()) {
|
|
||||||
std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl;
|
|
||||||
}
|
|
||||||
*value = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*value = {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,49 +257,23 @@ namespace sqlpp
|
|||||||
std::cerr << "PostgreSQL debug: binding date_time result at index: " << index << std::endl;
|
std::cerr << "PostgreSQL debug: binding date_time result at index: " << index << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*value = {};
|
||||||
*is_null = _handle->result.is_null(_handle->count, index);
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
|
if (*is_null)
|
||||||
if (!(*is_null))
|
|
||||||
{
|
{
|
||||||
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto date_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
||||||
|
if (_handle->debug())
|
||||||
|
{
|
||||||
|
std::cerr << "PostgreSQL debug: got date_time string: " << date_string << std::endl;
|
||||||
|
}
|
||||||
|
if (::sqlpp::detail::parse_string_date_time(*value, date_string) == false)
|
||||||
|
{
|
||||||
if (_handle->debug())
|
if (_handle->debug())
|
||||||
{
|
{
|
||||||
std::cerr << "PostgreSQL debug: got date_time string: " << date_string << std::endl;
|
std::cerr << "PostgreSQL debug: got invalid date_time '" << date_string << "'" << std::endl;
|
||||||
}
|
|
||||||
|
|
||||||
static const std::regex rx {
|
|
||||||
"(\\d{4})-(\\d{2})-(\\d{2}) "
|
|
||||||
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
|
||||||
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
|
||||||
};
|
|
||||||
std::cmatch mr;
|
|
||||||
if (std::regex_match (date_string, mr, rx)) {
|
|
||||||
*value =
|
|
||||||
::sqlpp::chrono::day_point{
|
|
||||||
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
|
||||||
std::atoi(date_string + mr.position(2)) / // Month
|
|
||||||
std::atoi(date_string + mr.position(3)) // Day of month
|
|
||||||
} +
|
|
||||||
std::chrono::hours{std::atoi(date_string + mr.position(4))} + // Hour
|
|
||||||
std::chrono::minutes{std::atoi(date_string + mr.position(5))} + // Minute
|
|
||||||
std::chrono::seconds{std::atoi(date_string + mr.position(6))} + // Second
|
|
||||||
::std::chrono::microseconds{ // Microsecond
|
|
||||||
mr[7].matched ? std::stoi((mr[7].str() + "000000").substr(0, 6)) : 0
|
|
||||||
};
|
|
||||||
if (mr[8].matched) {
|
|
||||||
const auto tz_sign = (date_string[mr.position(8)] == '+') ? 1 : -1;
|
|
||||||
const auto tz_offset =
|
|
||||||
std::chrono::hours{std::atoi(date_string + mr.position(9))} +
|
|
||||||
std::chrono::minutes{mr[10].matched ? std::atoi(date_string + mr.position(10)) : 0} +
|
|
||||||
std::chrono::seconds{mr[11].matched ? std::atoi(date_string + mr.position(11)) : 0};
|
|
||||||
*value -= tz_sign * tz_offset;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_handle->debug()) {
|
|
||||||
std::cerr << "PostgreSQL debug: got invalid date_time '" << date_string << "'" << std::endl;
|
|
||||||
}
|
|
||||||
*value = {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,43 +287,24 @@ namespace sqlpp
|
|||||||
std::cerr << "PostgreSQL debug: binding time result at index: " << index << std::endl;
|
std::cerr << "PostgreSQL debug: binding time result at index: " << index << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*value = {};
|
||||||
*is_null = _handle->result.is_null(_handle->count, index);
|
*is_null = _handle->result.is_null(_handle->count, index);
|
||||||
|
if (*is_null)
|
||||||
if (!(*is_null))
|
|
||||||
{
|
{
|
||||||
const auto time_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_handle->debug())
|
const auto time_string = _handle->result.get_char_ptr_value(_handle->count, index);
|
||||||
{
|
|
||||||
std::cerr << "PostgreSQL debug: got time string: " << time_string << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const std::regex rx {
|
if (_handle->debug())
|
||||||
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
{
|
||||||
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
std::cerr << "PostgreSQL debug: got time string: " << time_string << std::endl;
|
||||||
};
|
}
|
||||||
std::cmatch mr;
|
|
||||||
if (std::regex_match (time_string, mr, rx)) {
|
if (::sqlpp::detail::parse_string_time_of_day(*value, time_string) == false)
|
||||||
*value =
|
{
|
||||||
std::chrono::hours{std::atoi(time_string + mr.position(1))} + // Hour
|
if (_handle->debug()) {
|
||||||
std::chrono::minutes{std::atoi(time_string + mr.position(2))} + // Minute
|
std::cerr << "PostgreSQL debug: got invalid time '" << time_string << "'" << std::endl;
|
||||||
std::chrono::seconds{std::atoi(time_string + mr.position(3))} + // Second
|
|
||||||
::std::chrono::microseconds{ // Microsecond
|
|
||||||
mr[4].matched ? std::stoi((mr[4].str() + "000000").substr(0, 6)) : 0
|
|
||||||
};
|
|
||||||
if (mr[5].matched) {
|
|
||||||
const auto tz_sign = (time_string[mr.position(5)] == '+') ? 1 : -1;
|
|
||||||
const auto tz_offset =
|
|
||||||
std::chrono::hours{std::atoi(time_string + mr.position(6))} +
|
|
||||||
std::chrono::minutes{mr[7].matched ? std::atoi(time_string + mr.position(7)) : 0} +
|
|
||||||
std::chrono::seconds{mr[8].matched ? std::atoi(time_string + mr.position(8)) : 0};
|
|
||||||
*value -= tz_sign * tz_offset;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_handle->debug()) {
|
|
||||||
std::cerr << "PostgreSQL debug: got invalid time '" << time_string << "'" << std::endl;
|
|
||||||
}
|
|
||||||
*value = {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,16 @@
|
|||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sqlpp11/chrono.h>
|
#include <sqlpp11/chrono.h>
|
||||||
|
#include <sqlpp11/detail/parse_date_time.h>
|
||||||
#include <sqlpp11/exception.h>
|
#include <sqlpp11/exception.h>
|
||||||
#include <sqlpp11/sqlite3/detail/prepared_statement_handle.h>
|
#include <sqlpp11/sqlite3/detail/prepared_statement_handle.h>
|
||||||
#include <sqlpp11/sqlite3/export.h>
|
#include <sqlpp11/sqlite3/export.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#include <iso646.h>
|
#include <iso646.h>
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
@ -45,54 +48,33 @@ namespace sqlpp
|
|||||||
{
|
{
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
inline auto check_first_digit(const char* text, bool digit_flag) -> bool
|
// Parse a date string formatted as YYYY-MM-DD[ HH:MM:SS[.US]]
|
||||||
|
//
|
||||||
|
inline bool parse_string_date_opt_time(::sqlpp::chrono::microsecond_point& value, const char* date_time_string)
|
||||||
{
|
{
|
||||||
if (digit_flag)
|
static const std::regex rx{
|
||||||
|
"(\\d{4})-(\\d{2})-(\\d{2})"
|
||||||
|
"(?: (\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?)?"
|
||||||
|
};
|
||||||
|
std::cmatch mr;
|
||||||
|
if (std::regex_match(date_time_string, mr, rx) == false)
|
||||||
{
|
{
|
||||||
if (not std::isdigit(*text))
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
value = ::sqlpp::chrono::day_point{
|
||||||
|
::date::year{std::atoi(date_time_string + mr.position(1))} / // Year
|
||||||
|
std::atoi(date_time_string + mr.position(2)) / // Month
|
||||||
|
std::atoi(date_time_string + mr.position(3)) // Day of month
|
||||||
|
};
|
||||||
|
if (mr[4].matched)
|
||||||
{
|
{
|
||||||
if (std::isdigit(*text) or *text == '\0')
|
value +=
|
||||||
{
|
std::chrono::hours{std::atoi(date_time_string + mr.position(4))} + // Hour
|
||||||
return false;
|
std::chrono::minutes{std::atoi(date_time_string + mr.position(5))} + // Minute
|
||||||
}
|
std::chrono::seconds{std::atoi(date_time_string + mr.position(6))} + // Second
|
||||||
}
|
::std::chrono::microseconds{ // Second fraction
|
||||||
return true;
|
mr[7].matched ? std::stoi((mr[7].str() + "000000").substr(0, 6)) : 0
|
||||||
}
|
};
|
||||||
|
|
||||||
inline auto check_date_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digit_flag : {true, true, true, true, false, true, true, false, true, true}) // YYYY-MM-DD
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digit_flag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto check_time_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digit_flag : {true, true, false, true, true, false, true, true}) // hh:mm:ss
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digit_flag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto check_ms_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digit_flag : {true, true, true})
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digit_flag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -215,10 +197,10 @@ namespace sqlpp
|
|||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "Sqlite3 debug: binding date result at index: " << index << std::endl;
|
std::cerr << "Sqlite3 debug: binding date result at index: " << index << std::endl;
|
||||||
|
|
||||||
|
*value = {};
|
||||||
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
||||||
if (*is_null)
|
if (*is_null)
|
||||||
{
|
{
|
||||||
*value = {};
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,17 +208,10 @@ namespace sqlpp
|
|||||||
reinterpret_cast<const char*>(sqlite3_column_text(_handle->sqlite_statement, static_cast<int>(index)));
|
reinterpret_cast<const char*>(sqlite3_column_text(_handle->sqlite_statement, static_cast<int>(index)));
|
||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "Sqlite3 debug: date string: " << date_string << std::endl;
|
std::cerr << "Sqlite3 debug: date string: " << date_string << std::endl;
|
||||||
|
if (::sqlpp::detail::parse_string_date(*value, date_string) == false)
|
||||||
if (detail::check_date_digits(date_string))
|
|
||||||
{
|
|
||||||
const auto ymd = ::date::year(std::atoi(date_string)) / atoi(date_string + 5) / atoi(date_string + 8);
|
|
||||||
*value = ::sqlpp::chrono::day_point{ymd};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "Sqlite3 debug: invalid date result: " << date_string << std::endl;
|
std::cerr << "Sqlite3 debug: invalid date result: " << date_string << std::endl;
|
||||||
*value = {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,10 +220,10 @@ namespace sqlpp
|
|||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "Sqlite3 debug: binding date result at index: " << index << std::endl;
|
std::cerr << "Sqlite3 debug: binding date result at index: " << index << std::endl;
|
||||||
|
|
||||||
|
*value = {};
|
||||||
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
||||||
if (*is_null)
|
if (*is_null)
|
||||||
{
|
{
|
||||||
*value = {};
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,40 +231,11 @@ namespace sqlpp
|
|||||||
reinterpret_cast<const char*>(sqlite3_column_text(_handle->sqlite_statement, static_cast<int>(index)));
|
reinterpret_cast<const char*>(sqlite3_column_text(_handle->sqlite_statement, static_cast<int>(index)));
|
||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "Sqlite3 debug: date_time string: " << date_time_string << std::endl;
|
std::cerr << "Sqlite3 debug: date_time string: " << date_time_string << std::endl;
|
||||||
|
// We treat DATETIME fields as containing either date+time or just date.
|
||||||
if (detail::check_date_digits(date_time_string))
|
if (detail::parse_string_date_opt_time(*value, date_time_string) == false)
|
||||||
{
|
|
||||||
const auto ymd =
|
|
||||||
::date::year(std::atoi(date_time_string)) / atoi(date_time_string + 5) / atoi(date_time_string + 8);
|
|
||||||
*value = ::sqlpp::chrono::day_point{ymd};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (_handle->debug)
|
if (_handle->debug)
|
||||||
std::cerr << "Sqlite3 debug: invalid date_time result: " << date_time_string << std::endl;
|
std::cerr << "Sqlite3 debug: invalid date_time result: " << date_time_string << std::endl;
|
||||||
*value = {};
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto time_string = date_time_string + 11; // YYYY-MM-DDT
|
|
||||||
if (detail::check_time_digits(time_string))
|
|
||||||
{
|
|
||||||
*value += ::std::chrono::hours{std::atoi(time_string + 0)} +
|
|
||||||
std::chrono::minutes{std::atoi(time_string + 3)} + std::chrono::seconds{std::atoi(time_string + 6)};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto ms_string = time_string + 9; // hh:mm:ss.
|
|
||||||
if (detail::check_ms_digits(ms_string) and ms_string[4] == '\0')
|
|
||||||
{
|
|
||||||
*value += ::std::chrono::milliseconds{std::atoi(ms_string)};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user