8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 16:43:03 +01:00

Refactoring of CAST FORMAT (#7881) (#8134)

* Refactoring of CAST FORMAT

Move CAST FORMAT logic to separate file.
Separation of logic into parsing stage and execution stage.
Add more FORMAT validation based on SQL standard.

* Add more unit tests for incorrect FORMAT

* Remove incorrect const specifier

* Fixes after review

Minor code changes.
Move public CVT_FORMAT functions  to CvtFormat.h.
Add new error message if same is used pattern twice in string to datetime conversion.

* Fix indents in comments

* Add new files to windows build

* Remove constexpr specifier from functions that is not real constexpr

Because std::isdigit and std::isalpha are not constexpr.

---------

Co-authored-by: Artyom Ivanov <artyom.ivanov@red-soft.ru>
This commit is contained in:
TreeHunter 2024-06-03 16:43:06 +03:00 committed by GitHub
parent f2d12e403b
commit 174038cbf5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 1790 additions and 1397 deletions

View File

@ -62,6 +62,7 @@
<EnableEnhancedInstructionSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
</ClCompile>
<ClCompile Include="..\..\..\src\common\cvt.cpp" />
<ClCompile Include="..\..\..\src\common\CvtFormat.cpp" />
<ClCompile Include="..\..\..\src\common\db_alias.cpp" />
<ClCompile Include="..\..\..\src\common\DecFloat.cpp" />
<ClCompile Include="..\..\..\src\common\dllinst.cpp" />
@ -170,6 +171,8 @@
<ClInclude Include="..\..\..\src\common\config\dir_list.h" />
<ClInclude Include="..\..\..\src\common\CsConvert.h" />
<ClInclude Include="..\..\..\src\common\cvt.h" />
<ClInclude Include="..\..\..\src\common\CvtFormat.h" />
<ClInclude Include="..\..\..\src\common\CvtFormatImpl.h" />
<ClInclude Include="..\..\..\src\common\db_alias.h" />
<ClInclude Include="..\..\..\src\common\DecFloat.h" />
<ClInclude Include="..\..\..\src\common\dllinst.h" />

View File

@ -30,6 +30,9 @@
<ClCompile Include="..\..\..\src\common\cvt.cpp">
<Filter>common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\common\CvtFormat.cpp">
<Filter>common</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\common\db_alias.cpp">
<Filter>common</Filter>
</ClCompile>
@ -275,6 +278,12 @@
<ClInclude Include="..\..\..\src\common\cvt.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\common\CvtFormat.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\common\CvtFormatImpl.h">
<Filter>headers</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\common\db_alias.h">
<Filter>headers</Filter>
</ClInclude>

1693
src/common/CvtFormat.cpp Normal file

File diff suppressed because it is too large Load Diff

11
src/common/CvtFormat.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef COMMON_CVT_FORMAT_H
#define COMMON_CVT_FORMAT_H
#include "firebird.h"
#include "../common/cvt.h"
Firebird::string CVT_format_datetime_to_string(const dsc* desc, const Firebird::string& format, Firebird::Callbacks* cb);
ISC_TIMESTAMP_TZ CVT_format_string_to_datetime(const dsc* desc, const Firebird::string& format,
const Firebird::EXPECT_DATETIME expectedType, Firebird::Callbacks* cb);
#endif // COMMON_CVT_FORMAT_H

View File

@ -0,0 +1,42 @@
CVT_FORMAT(__LINE__, YEAR)
CVT_FORMAT(__LINE__, YYYY)
CVT_FORMAT(__LINE__, YYY)
CVT_FORMAT(__LINE__, YY)
CVT_FORMAT(__LINE__, Y)
CVT_FORMAT(__LINE__, RR)
CVT_FORMAT(__LINE__, RRRR)
CVT_FORMAT(__LINE__, Q)
CVT_FORMAT(__LINE__, MM)
CVT_FORMAT(__LINE__, MON)
CVT_FORMAT(__LINE__, MONTH)
CVT_FORMAT(__LINE__, RM)
CVT_FORMAT(__LINE__, WW)
CVT_FORMAT(__LINE__, W)
CVT_FORMAT(__LINE__, D)
CVT_FORMAT(__LINE__, DAY)
CVT_FORMAT(__LINE__, DD)
CVT_FORMAT(__LINE__, DDD)
CVT_FORMAT(__LINE__, DY)
CVT_FORMAT(__LINE__, J)
CVT_FORMAT(__LINE__, HH)
CVT_FORMAT(__LINE__, HH12)
CVT_FORMAT(__LINE__, HH24)
CVT_FORMAT(__LINE__, MI)
CVT_FORMAT(__LINE__, SS)
CVT_FORMAT(__LINE__, SSSSS)
CVT_FORMAT(__LINE__, FF1)
CVT_FORMAT(__LINE__, FF2)
CVT_FORMAT(__LINE__, FF3)
CVT_FORMAT(__LINE__, FF4)
CVT_FORMAT(__LINE__, FF5)
CVT_FORMAT(__LINE__, FF6)
CVT_FORMAT(__LINE__, FF7)
CVT_FORMAT(__LINE__, FF8)
CVT_FORMAT(__LINE__, FF9)
CVT_FORMAT(__LINE__, TZH)
CVT_FORMAT(__LINE__, TZM)
CVT_FORMAT(__LINE__, TZR)
CVT_FORMAT2(__LINE__, A.M., AM)
CVT_FORMAT2(__LINE__, P.M., PM)
CVT_FORMAT_FLAG(__LINE__, SEPARATOR)

File diff suppressed because it is too large Load Diff

View File

@ -105,8 +105,5 @@ SQUAD CVT_get_quad(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction);
void CVT_string_to_datetime(const dsc*, ISC_TIMESTAMP_TZ*, bool*, const Firebird::EXPECT_DATETIME,
bool, Firebird::Callbacks*);
const UCHAR* CVT_get_bytes(const dsc*, unsigned&);
Firebird::string CVT_datetime_to_format_string(const dsc* desc, const Firebird::string& format, Firebird::Callbacks* cb);
ISC_TIMESTAMP_TZ CVT_string_to_format_datetime(const dsc* desc, const Firebird::string& format,
const Firebird::EXPECT_DATETIME expectedType, Firebird::Callbacks* cb);
#endif //COMMON_CVT_H

View File

@ -1,6 +1,8 @@
#include "boost/test/unit_test.hpp"
#include "../common/tests/CvtTestUtils.h"
#include "../jrd/cvt_proto.h"
#include "../common/StatusArg.h"
#include "../common/CvtFormat.h"
using namespace Firebird;
using namespace Jrd;
@ -33,7 +35,7 @@ static void testCVTDatetimeToFormatString(T date, const string& format, const st
BOOST_TEST_INFO("FORMAT: " << "\"" << format.c_str() << "\"");
string result = CVT_datetime_to_format_string(&desc, format, &cb);
string result = CVT_format_datetime_to_string(&desc, format, &cb);
BOOST_TEST(result == expected, "\nRESULT: " << result.c_str() << "\nEXPECTED: " << expected.c_str());
}
@ -278,7 +280,7 @@ static void testCVTStringToFormatDateTime(const string& date, const string& form
BOOST_TEST_INFO("INPUT: " << "\"" << date.c_str() << "\"");
BOOST_TEST_INFO("FORMAT: " << "\"" << format.c_str() << "\"");
const ISC_TIMESTAMP_TZ result = CVT_string_to_format_datetime(&desc, format, expectedType, &cb);
const ISC_TIMESTAMP_TZ result = CVT_format_string_to_datetime(&desc, format, expectedType, &cb);
struct tm resultTimes;
memset(&resultTimes, 0, sizeof(resultTimes));
@ -340,7 +342,7 @@ static void testExceptionCvtStringToFormatDateTime(const string& date, const str
BOOST_TEST_INFO("INPUT: " << "\"" << date.c_str() << "\"");
BOOST_TEST_INFO("FORMAT: " << "\"" << format.c_str() << "\"");
BOOST_CHECK_THROW(CVT_string_to_format_datetime(&desc, format, expect_timestamp_tz, &cb), status_exception);
BOOST_CHECK_THROW(CVT_format_string_to_datetime(&desc, format, expect_timestamp_tz, &cb), status_exception);
}
BOOST_AUTO_TEST_SUITE(FunctionalTest)
@ -590,9 +592,16 @@ BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_EXCEPTION_CHECK)
testExceptionCvtStringToFormatDateTime("2000.12.11", "WRONG FORMAT", cb);
testExceptionCvtStringToFormatDateTime("2000.12.11", "YYYY.MM.DD SS", cb);
testExceptionCvtStringToFormatDateTime("2000.12", "YYYY.MM.DD", cb);
testExceptionCvtStringToFormatDateTime("2000.12", "YYYY", cb);
testExceptionCvtStringToFormatDateTime("1 A.G.", "HH12 A.M.", cb);
testExceptionCvtStringToFormatDateTime("1 A.G.", "HH12 A.P.", cb);
testExceptionCvtStringToFormatDateTime("2 20 200 2000 2000", "Y YY YYY YYYY YEAR", cb);
testExceptionCvtStringToFormatDateTime("20 2000", "RR RRRR", cb);
testExceptionCvtStringToFormatDateTime("2000 2000", "YYYY RRRR", cb);
testExceptionCvtStringToFormatDateTime("2000 20", "YYYY RR", cb);
testExceptionCvtStringToFormatDateTime("200 2", "DDD MM", cb);
testExceptionCvtStringToFormatDateTime("200 2", "DDD DD", cb);
testExceptionCvtStringToFormatDateTime("24 12 A.M.", "HH24 HH12 P.M.", cb);
testExceptionCvtStringToFormatDateTime("24 12", "HH24 HH12", cb);
@ -600,6 +609,10 @@ BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_EXCEPTION_CHECK)
testExceptionCvtStringToFormatDateTime("12", "HH12", cb);
testExceptionCvtStringToFormatDateTime("A.M.", "P.M.", cb);
testExceptionCvtStringToFormatDateTime("P.M.", "A.M.", cb);
testExceptionCvtStringToFormatDateTime("10 P.M. A.M.", "HH P.M. A.M.", cb);
testExceptionCvtStringToFormatDateTime("1 A.G.", "HH12 A.M.", cb);
testExceptionCvtStringToFormatDateTime("1 A.G.", "HH12 A.P.", cb);
testExceptionCvtStringToFormatDateTime("1 1 A.M.", "SSSSS HH12 A.M.", cb);
testExceptionCvtStringToFormatDateTime("1 1 A.M.", "SSSSS HH12 P.M.", cb);
@ -611,6 +624,8 @@ BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_EXCEPTION_CHECK)
testExceptionCvtStringToFormatDateTime("30 1", "TZM SS", cb);
testExceptionCvtStringToFormatDateTime("30", "TZM", cb);
testExceptionCvtStringToFormatDateTime("12 12", "HH24 HH24", cb);
}
BOOST_AUTO_TEST_SUITE_END() // FunctionalTest

View File

@ -25,6 +25,7 @@
#include "../common/TimeZoneUtil.h"
#include "../common/classes/FpeControl.h"
#include "../common/classes/VaryStr.h"
#include "../common/CvtFormat.h"
#include "../dsql/ExprNodes.h"
#include "../dsql/BoolNodes.h"
#include "../dsql/StmtNodes.h"
@ -3789,7 +3790,7 @@ dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
{
if (DTYPE_IS_TEXT(impure->vlu_desc.dsc_dtype))
{
string result = CVT_datetime_to_format_string(value, format, &EngineCallbacks::instance);
string result = CVT_format_datetime_to_string(value, format, &EngineCallbacks::instance);
USHORT dscLength = DSC_string_length(&impure->vlu_desc);
string::size_type resultLength = result.length();
if (resultLength > dscLength)
@ -3815,21 +3816,21 @@ dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
{
case dtype_sql_time:
{
ISC_TIMESTAMP_TZ timestampTZ = CVT_string_to_format_datetime(value, format, expect_sql_time,
ISC_TIMESTAMP_TZ timestampTZ = CVT_format_string_to_datetime(value, format, expect_sql_time,
&EngineCallbacks::instance);
*(ISC_TIME*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp.timestamp_time;
break;
}
case dtype_sql_date:
{
ISC_TIMESTAMP_TZ timestampTZ = CVT_string_to_format_datetime(value, format, expect_sql_date,
ISC_TIMESTAMP_TZ timestampTZ = CVT_format_string_to_datetime(value, format, expect_sql_date,
&EngineCallbacks::instance);
*(ISC_DATE*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp.timestamp_date;
break;
}
case dtype_timestamp:
{
ISC_TIMESTAMP_TZ timestampTZ = CVT_string_to_format_datetime(value, format, expect_timestamp,
ISC_TIMESTAMP_TZ timestampTZ = CVT_format_string_to_datetime(value, format, expect_timestamp,
&EngineCallbacks::instance);
*(ISC_TIMESTAMP*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp;
break;
@ -3837,7 +3838,7 @@ dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
case dtype_sql_time_tz:
case dtype_ex_time_tz:
{
ISC_TIMESTAMP_TZ timestampTZ = CVT_string_to_format_datetime(value, format, expect_sql_time_tz,
ISC_TIMESTAMP_TZ timestampTZ = CVT_format_string_to_datetime(value, format, expect_sql_time_tz,
&EngineCallbacks::instance);
*(ISC_TIME_TZ*) impure->vlu_desc.dsc_address = TimeZoneUtil::timeStampTzToTimeTz(timestampTZ);
break;
@ -3845,7 +3846,7 @@ dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
case dtype_timestamp_tz:
case dtype_ex_timestamp_tz:
{
ISC_TIMESTAMP_TZ timestampTZ = CVT_string_to_format_datetime(value, format, expect_timestamp_tz,
ISC_TIMESTAMP_TZ timestampTZ = CVT_format_string_to_datetime(value, format, expect_timestamp_tz,
&EngineCallbacks::instance);
*(ISC_TIMESTAMP_TZ*) impure->vlu_desc.dsc_address = timestampTZ;
break;

View File

@ -984,3 +984,5 @@ FB_IMPL_MSG(JRD, 981, trailing_part_of_string, -901, "HY", "000", "There is a tr
FB_IMPL_MSG(JRD, 982, pattern_cant_be_used_without_other_pattern, -901, "HY", "000", "@1 can't be used without @2")
FB_IMPL_MSG(JRD, 983, pattern_cant_be_used_without_other_pattern_and_vice_versa, -901, "HY", "000", "@1 can't be used without @2 and vice versa")
FB_IMPL_MSG(JRD, 984, incompatible_format_patterns, -901, "HY", "000", "@1 incompatible with @2")
FB_IMPL_MSG(JRD, 985, only_one_pattern_can_be_used, -901, "HY", "000", "Can use only one of these patterns @1")
FB_IMPL_MSG(JRD, 986, can_not_use_same_pattern_twice, -901, "HY", "000", "Cannot use the same pattern twice: @1")

View File

@ -5727,6 +5727,8 @@ const
isc_pattern_cant_be_used_without_other_pattern = 335545302;
isc_pattern_cant_be_used_without_other_pattern_and_vice_versa = 335545303;
isc_incompatible_format_patterns = 335545304;
isc_only_one_pattern_can_be_used = 335545305;
isc_can_not_use_same_pattern_twice = 335545306;
isc_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;
isc_gfix_incmp_sw = 335740932;