diff --git a/src/common/cvt.cpp b/src/common/cvt.cpp index 1a876892a3f..f1b6a5008d9 100644 --- a/src/common/cvt.cpp +++ b/src/common/cvt.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include "iberror.h" @@ -127,10 +128,44 @@ constexpr SLONG LONG_LIMIT = ((1L << 30) / 5); //#define QUAD_LIMIT ((((SINT64) 1) << 62) / 5) constexpr SINT64 INT64_LIMIT = ((((SINT64) 1) << 62) / 5); -#define TODAY "TODAY" -#define NOW "NOW" -#define TOMORROW "TOMORROW" -#define YESTERDAY "YESTERDAY" +namespace { + +struct SpecialDateTimeName +{ + std::string_view name; + SpecialDateTime value; +}; + +constexpr SpecialDateTimeName SPECIAL_DATETIME_NAMES[] = +{ + {"NOW", SpecialDateTime::NOW}, + {"TODAY", SpecialDateTime::TODAY}, + {"TOMORROW", SpecialDateTime::TOMORROW}, + {"YESTERDAY", SpecialDateTime::YESTERDAY} +}; + +constexpr FB_SIZE_T maxSpecialDateTimeLength() +{ + FB_SIZE_T result = 0; + for (const auto& it : SPECIAL_DATETIME_NAMES) + { + if (it.name.length() > result) + result = it.name.length(); + } + + for (const TEXT* const* month = FB_LONG_MONTHS_UPPER; *month; ++month) + { + const FB_SIZE_T length = std::string_view(*month).length(); + if (length > result) + result = length; + } + + return result; +} + +constexpr FB_SIZE_T MAX_DATETIME_WORD_LENGTH = maxSpecialDateTimeLength(); + +} // anonymous namespace #define CVT_COPY_BUFF(from, to, len) \ {if (len) {memcpy(to, from, len); from += len; to += len;} } @@ -673,6 +708,49 @@ static void integer_to_text(const dsc* from, dsc* to, Callbacks* cb) *(USHORT*) (to->dsc_address) = static_cast(q - to->dsc_address - sizeof(SSHORT)); } +SpecialDateTime CVT_get_special_datetime(const char* str, FB_SIZE_T length) +{ +/************************************** + * + * C V T _ g e t _ s p e c i a l _ d a t e t i m e + * + ************************************** + * + * Functional description + * Recognize a special date/time expression such as 'NOW' or 'TODAY'. + * Leading and trailing blanks are ignored, the comparison is case + * insensitive. Return SpecialDateTime::NONE if the string is not one + * of the special expressions. + * + **************************************/ + const char* p = str; + const char* end = str + length; + + while (p < end && (*p == ' ' || *p == '\t')) + ++p; + + while (end > p && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\0')) + --end; + + const FB_SIZE_T len = end - p; + + for (const auto& item : SPECIAL_DATETIME_NAMES) + { + if (len != item.name.length()) + continue; + + FB_SIZE_T pos = 0; + + while (pos < len && UPPER7(p[pos]) == item.name[pos]) + ++pos; + + if (pos == len) + return item.value; + } + + return SpecialDateTime::NONE; +} + void CVT_string_to_datetime(const dsc* desc, ISC_TIMESTAMP_TZ* date, bool* timezone_present, @@ -793,14 +871,21 @@ void CVT_string_to_datetime(const dsc* desc, } else if (LETTER7_UPPER(c) && !have_english_month && i - start_component < 2) { - TEXT temp[sizeof(YESTERDAY) + 1]; + TEXT temp[MAX_DATETIME_WORD_LENGTH + 1]; TEXT* t = temp; - while ((p < end) && (t < &temp[sizeof(temp) - 1])) + while (p < end) { c = UPPER7(*p); if (!LETTER7_UPPER(c)) break; + + if (t >= &temp[sizeof(temp) - 1]) + { + CVT_conversion_error(desc, cb->err); + return; + } + *t++ = c; p++; } @@ -873,7 +958,9 @@ void CVT_string_to_datetime(const dsc* desc, break; } - if (strcmp(temp, NOW) == 0) + const SpecialDateTime special = CVT_get_special_datetime(temp, strlen(temp)); + + if (special == SpecialDateTime::NOW) return; if (expect_type == expect_sql_time || expect_type == expect_sql_time_tz) @@ -884,23 +971,23 @@ void CVT_string_to_datetime(const dsc* desc, date->utc_timestamp.timestamp_time = 0; - if (strcmp(temp, TODAY) == 0) + switch (special) + { + case SpecialDateTime::TODAY: return; - if (strcmp(temp, TOMORROW) == 0) - { + case SpecialDateTime::TOMORROW: ++date->utc_timestamp.timestamp_date; return; - } - if (strcmp(temp, YESTERDAY) == 0) - { + case SpecialDateTime::YESTERDAY: --date->utc_timestamp.timestamp_date; return; - } - CVT_conversion_error(desc, cb->err); - return; + default: + CVT_conversion_error(desc, cb->err); + return; + } } } n = month_ptr - FB_LONG_MONTHS_UPPER; diff --git a/src/common/cvt.h b/src/common/cvt.h index 984e6f151d9..b977b048791 100644 --- a/src/common/cvt.h +++ b/src/common/cvt.h @@ -81,6 +81,15 @@ enum EXPECT_DATETIME expect_sql_time_tz }; +enum class SpecialDateTime +{ + NONE, + NOW, + TODAY, + TOMORROW, + YESTERDAY +}; + class Int128; } // namespace Firebird @@ -103,6 +112,7 @@ USHORT CVT_get_string_ptr(const dsc*, TTypeId*, UCHAR**, vary*, USHORT, Firebird USHORT CVT_get_string_ptr_common(const dsc*, TTypeId*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, Firebird::Callbacks*); SINT64 CVT_get_int64(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); SQUAD CVT_get_quad(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); +Firebird::SpecialDateTime CVT_get_special_datetime(const char*, FB_SIZE_T); 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&); diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index 29f8a8eb78d..fd84e9af226 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -43,6 +43,7 @@ #include "../dsql/make_proto.h" #include "../dsql/pass1_proto.h" #include "../dsql/DSqlDataTypeUtil.h" +#include "../jrd/cvt2_proto.h" using namespace Firebird; using namespace Jrd; @@ -462,12 +463,39 @@ BoolExprNode* ComparativeBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) fb_assert(false); } + procArg1 = doDsqlPass(dsqlScratch, procArg1); procArg2 = doDsqlPass(dsqlScratch, procArg2); + procArg3 = doDsqlPass(dsqlScratch, procArg3); + + + const auto convertLiteralToOperand = + [&](NestConst& literalArg, NestConst& referenceArg) + { + LiteralNode* const literal = nodeAs(literalArg); + + if (!literal || literal->litDesc.dsc_dtype != dtype_text) + return; + + dsc referenceDesc; + DsqlDescMaker::fromNode(dsqlScratch, &referenceDesc, referenceArg); + + if (referenceDesc.isUnknown()) + return; + + if (ValueExprNode* const value = MAKE_constant_from_literal(literal, &referenceDesc)) + literalArg = value; + }; + + convertLiteralToOperand(procArg1, procArg2); + convertLiteralToOperand(procArg2, procArg1); + + if (blrOp == blr_between) + convertLiteralToOperand(procArg3, procArg1); ComparativeBoolNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ComparativeBoolNode(dsqlScratch->getPool(), blrOp, - doDsqlPass(dsqlScratch, procArg1), + procArg1, procArg2, - doDsqlPass(dsqlScratch, procArg3)); + procArg3); if (dsqlCheckBoolean) { diff --git a/src/dsql/make.cpp b/src/dsql/make.cpp index e30e24359ba..fb217fae763 100644 --- a/src/dsql/make.cpp +++ b/src/dsql/make.cpp @@ -54,6 +54,7 @@ #include "../jrd/ods.h" #include "../jrd/ini.h" #include "../jrd/cvt_proto.h" +#include "../jrd/cvt2_proto.h" #include "../jrd/scl_proto.h" #include "../common/dsc_proto.h" #include "../yvalve/why_proto.h" @@ -337,6 +338,65 @@ ValueExprNode* MAKE_constant(const char* str, dsql_constant_type numeric_flag, S } +ValueExprNode* MAKE_constant_from_literal(LiteralNode* from, const dsc* reference) +{ + if (from->litDesc.dsc_dtype != dtype_text) + return nullptr; + + if (CVT2_compare_priority[from->litDesc.dsc_dtype] >= + CVT2_compare_priority[reference->dsc_dtype]) + { + return nullptr; + } + + dsql_constant_type to; + + switch (reference->dsc_dtype) + { + case dtype_double: + to = CONSTANT_DOUBLE; + break; + case dtype_dec64: + case dtype_dec128: + to = CONSTANT_DECIMAL; + break; + case dtype_int128: + to = CONSTANT_NUM128; + break; + case dtype_sql_date: + to = CONSTANT_DATE; + break; + case dtype_sql_time: + case dtype_sql_time_tz: + to = CONSTANT_TIME; + break; + case dtype_timestamp: + case dtype_timestamp_tz: + to = CONSTANT_TIMESTAMP; + break; + default: + return nullptr; + } + + switch (to) + { + case CONSTANT_DATE: + case CONSTANT_TIME: + case CONSTANT_TIMESTAMP: + if (CVT_get_special_datetime(reinterpret_cast(from->litDesc.dsc_address), + from->litDesc.dsc_length) != SpecialDateTime::NONE) + { + return nullptr; + } + break; + default: + break; + } + + return MAKE_constant(reinterpret_cast(from->litDesc.dsc_address), to, 0); +} + + /** MAKE_str_constant diff --git a/src/dsql/make_proto.h b/src/dsql/make_proto.h index 270161fca9c..68de742615d 100644 --- a/src/dsql/make_proto.h +++ b/src/dsql/make_proto.h @@ -91,4 +91,6 @@ Jrd::dsql_par* MAKE_parameter(Jrd::dsql_msg*, bool, bool, USHORT, const Jrd::Val void MAKE_parameter_names(Jrd::dsql_par*, const Jrd::ValueExprNode*); Jrd::LiteralNode* MAKE_system_privilege(const char*); +Jrd::ValueExprNode* MAKE_constant_from_literal(Jrd::LiteralNode*, const dsc*); + #endif // DSQL_MAKE_PROTO_H