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

Merge branch 'master' into master

This commit is contained in:
Alexandr 2023-11-10 13:12:38 +03:00 committed by GitHub
commit 93c1b1f815
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 2327 additions and 352 deletions

95
doc/README.cast.format.md Normal file
View File

@ -0,0 +1,95 @@
## 1. DATETIME TO STRING
The following flags are currently implemented for datetime to string conversion:
| Format Pattern | Description |
| -------------- | ----------- |
| YEAR | Year (1 - 9999) |
| YYYY | Last 4 digits of Year (0001 - 9999) |
| YYY | Last 3 digits of Year (000 - 999) |
| YY | Last 2 digits of Year (00 - 99) |
| Y | Last 1 digits of Year (0 - 9) |
| Q | Quarter of the Year (1 - 4) |
| MM | Month (01 - 12) |
| MON | Short Month name (Apr) |
| MONTH | Full Month name (APRIL) |
| RM | Roman representation of the Month (I - XII) |
| WW | Week of the Year (01 - 53) |
| W | Week of the Month (1 - 5) |
| D | Day of the Week (1 - 7) |
| DAY | Full name of the Day (MONDAY) |
| DD | Day of the Month (01 - 31) |
| DDD | Day of the Year (001 - 366) |
| DY | Short name of the Day (Mon) |
| J | Julian Day (number of days since January 1, 4712 BC) |
| HH / HH12 | Hour of the Day (01 - 12) with period (AM, PM) |
| HH24 | Hour of the Day (00 - 23) |
| MI | Minutes (00 - 59) |
| SS | Seconds (00 - 59) |
| SSSSS | Seconds after midnight (0 - 86399) |
| FF1 - FF9 | Fractional seconds with the specified accuracy |
| TZH | Time zone in Hours (-14 - 14) |
| TZM | Time zone in Minutes (00 - 59) |
| TZR | Time zone Name |
The dividers are:
| Dividers |
| ------------- |
| . |
| / |
| , |
| ; |
| : |
| 'space' |
| - |
Patterns can be used without any dividers:
```
SELECT CAST(CURRENT_TIMESTAMP AS VARCHAR(50) FORMAT 'YEARMMDD HH24MISS') FROM RDB$DATABASE;
=========================
20230719 161757
```
However, be careful with patterns like `DDDDD`, it will be interpreted as `DDD` + `DD`.
It is possible to insert raw text into a format string with `""`: `... FORMAT '"Today is" DAY'` - Today is MONDAY. To add `"` in output raw string use `\"` (to print `\` use `\\`).
Also the format is case-insensitive, so `YYYY-MM` == `yyyy-mm`.
Example:
```
SELECT CAST(CURRENT_TIMESTAMP AS VARCHAR(45) FORMAT 'DD.MM.YEAR HH24:MI:SS "is" J "Julian day"') FROM RDB$DATABASE;
=========================
14.6.2023 15:41:29 is 2460110 Julian day
```
## 2. STRING TO DATETIME
The following flags are currently implemented for string to datetime conversion:
| Format Pattern | Description |
| ------------- | ------------- |
| YEAR | Year |
| YYYY | Last 4 digits of Year |
| YYY | Last 3 digits of Year |
| YY | Last 2 digits of Year |
| Y | Last 1 digits of Year |
| MM | Month (1 - 12) |
| MON | Short Month name (Apr) |
| MONTH | Full Month name (APRIL) |
| RM | Roman representation of the Month (I - XII) |
| DD | Day of the Month (1 - 31) |
| J | Julian Day (number of days since January 1, 4712 BC) |
| HH / HH12 | Hour of the Day (1 - 12) with period (AM, PM) |
| HH24 | Hour of the Day (0 - 23) |
| MI | Minutes (0 - 59) |
| SS | Seconds (0 - 59) |
| SSSSS | Seconds after midnight (0 - 86399) |
| FF1 - FF4 | Fractional seconds with the specified accuracy |
| TZH | Time zone in Hours (-14 - 14) |
| TZM | Time zone in Minutes (0 - 59) |
| TZR | Time zone Name |
Dividers are the same as for datetime to string conversion and can also be omitted.
Example:
```
SELECT CAST('2000.12.08 12:35:30.5000' AS TIMESTAMP FORMAT 'YEAR.MM.DD HH24:MI:SS.FF4') FROM RDB$DATABASE;
=====================
2000-12-08 12:35:30.5000
```

View File

@ -70,10 +70,14 @@ Usage:
CLIENT_HOST | The wire protocol host name of remote client. Value is
| returned for all supported protocols.
|
CLIENT_OS_USER | Remote OS user name
|
CLIENT_PID | Process ID of remote client application
|
CLIENT_PROCESS | Process name of remote client application
|
CLIENT_VERSION | Version of the client library used by client application
|
DB_NAME | Canonical name of current database. It is either alias
| name if connectivity via file names is not allowed or
| fully expanded database file name otherwise.

View File

@ -2517,6 +2517,7 @@ void write_database( const TEXT* dbb_file)
FbLocalStatus status_vector;
UCHAR buffer[256];
Firebird::IRequest* req_handle1 = nullptr;
Firebird::IRequest* req_handle2 = nullptr;
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
@ -2619,6 +2620,23 @@ void write_database( const TEXT* dbb_file)
ON_ERROR
general_on_error();
END_ERROR;
FOR (REQUEST_HANDLE req_handle2)
PUB IN RDB$PUBLICATIONS
WITH PUB.RDB$PUBLICATION_NAME EQ DEFAULT_PUBLICATION
{
fb_assert(PUB.RDB$SYSTEM_FLAG != 0);
if (!PUB.RDB$ACTIVE_FLAG.NULL)
put_boolean(att_default_pub_active, PUB.RDB$ACTIVE_FLAG);
if (!PUB.RDB$AUTO_ENABLE.NULL)
put_boolean(att_default_pub_auto_enable, PUB.RDB$AUTO_ENABLE);
}
END_FOR
ON_ERROR
general_on_error();
END_ERROR
}
else
{
@ -2636,6 +2654,7 @@ void write_database( const TEXT* dbb_file)
}
MISC_release_request_silent(req_handle1);
MISC_release_request_silent(req_handle2);
put(tdgbl, att_end);
}

View File

@ -572,8 +572,6 @@ int gbak(Firebird::UtilSvc* uSvc)
if (switches.exists(IN_SW_BURP_SE, argv.begin(), 1, argc))
return svc_api_gbak(uSvc, switches);
uSvc->started();
if (argc <= 1)
{
burp_usage(switches);
@ -1396,13 +1394,12 @@ int gbak(Firebird::UtilSvc* uSvc)
tdgbl->action->act_action = ACT_unknown;
action = open_files(file1, &file2, sw_replace, dpb);
MVOL_init(tdgbl->io_buffer_size);
uSvc->started();
int result;
tdgbl->gbl_dpb_data.add(dpb.getBuffer(), dpb.getBufferLength());
tdgbl->uSvc->started();
switch (action)
{
case RESTORE:

View File

@ -257,6 +257,8 @@ enum att_type {
att_database_sql_security_deprecated, // can be removed later
att_replica_mode, // replica mode
att_database_sql_security, // default sql security value
att_default_pub_active, // default publication status
att_default_pub_auto_enable,
// Relation attributes
@ -735,8 +737,7 @@ enum fld_flags_vals {
FLD_update_missing = 8,
FLD_null_flag = 16,
FLD_charset_flag = 32, // column has global charset
FLD_collate_flag = 64, // local column has specific collation
FLD_system_domain = 128 // field uses a system domain (on restore)
FLD_collate_flag = 64 // local column has specific collation
};
// relation definition - holds useful relation type stuff
@ -961,6 +962,7 @@ public:
GblPool(us->isService()),
gbl_sw_par_workers(1),
defaultCollations(getPool()),
systemFields(getPool()),
gbl_dpb_data(*getDefaultMemoryPool()),
uSvc(us),
master(true),
@ -1065,6 +1067,8 @@ public:
UCHAR* gbl_crypt_buffer;
ULONG gbl_crypt_left;
UCHAR* gbl_decompress;
bool gbl_default_pub_active = false;
bool gbl_default_pub_auto_enable = false;
burp_rel* relations;
burp_pkg* packages;
@ -1196,6 +1200,7 @@ public:
Firebird::Array<Firebird::Pair<Firebird::NonPooled<Firebird::MetaString, Firebird::MetaString> > >
defaultCollations;
Firebird::SortedArray<Firebird::MetaString> systemFields;
Firebird::Array<UCHAR> gbl_dpb_data;
Firebird::UtilSvc* uSvc;
bool master; // set for master thread only

View File

@ -198,7 +198,7 @@ static inline UCHAR get(BurpGlobals* tdgbl)
return tdgbl->get();
}
static inline FB_BOOLEAN get_boolean(BurpGlobals* tdgbl, bool deprecated)
static inline FB_BOOLEAN get_boolean(BurpGlobals* tdgbl, bool deprecated = false)
{
if (!deprecated)
{
@ -531,6 +531,40 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name)
MISC_release_request_silent(req_handle1);
}
// If the default publication was backed up with non-default values,
// update the table accordingly.
// NOTE: This change should be performed in the last transaction
// of the restore process, to avoid generating a replication stream
// before the database is restored successfully.
if ((tdgbl->gbl_default_pub_active || tdgbl->gbl_default_pub_auto_enable) &&
tdgbl->runtimeODS >= DB_VERSION_DDL12)
{
FOR (REQUEST_HANDLE req_handle1)
PUB IN RDB$PUBLICATIONS
WITH PUB.RDB$PUBLICATION_NAME EQ DEFAULT_PUBLICATION
{
fb_assert(PUB.RDB$SYSTEM_FLAG != 0);
MODIFY PUB USING
PUB.RDB$ACTIVE_FLAG.NULL = FALSE;
PUB.RDB$ACTIVE_FLAG = tdgbl->gbl_default_pub_active ? 1 : 0;
PUB.RDB$AUTO_ENABLE.NULL = FALSE;
PUB.RDB$AUTO_ENABLE = tdgbl->gbl_default_pub_auto_enable ? 1 : 0;
END_MODIFY;
ON_ERROR
general_on_error();
END_ERROR;
}
END_FOR;
ON_ERROR
general_on_error();
END_ERROR;
MISC_release_request_silent(req_handle1);
}
// Add missing privileges
fix_missing_privileges(tdgbl);
@ -3810,7 +3844,6 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation)
case att_field_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
X.RDB$SYSTEM_FLAG.NULL = FALSE;
field->fld_flags |= FLD_system_domain;
break;
case att_view_context:
@ -4015,7 +4048,6 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation)
case att_field_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
X.RDB$SYSTEM_FLAG.NULL = FALSE;
field->fld_flags |= FLD_system_domain;
break;
case att_view_context:
@ -7472,8 +7504,6 @@ bool get_publication(BurpGlobals* tdgbl)
*
**************************************/
att_type attribute;
TEXT temp[GDS_NAME_LEN];
SSHORT len;
scan_attr_t scan_next_attr;
if (tdgbl->runtimeODS >= DB_VERSION_DDL13)
@ -7571,8 +7601,6 @@ bool get_pub_table(BurpGlobals* tdgbl)
*
**************************************/
att_type attribute;
TEXT temp[GDS_NAME_LEN];
SSHORT len;
scan_attr_t scan_next_attr;
if (tdgbl->runtimeODS >= DB_VERSION_DDL13)
@ -10391,6 +10419,28 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file
Firebird::IRequest* req_handle4 = nullptr;
Firebird::IRequest* req_handle5 = nullptr;
// Collect system fields
{
Firebird::IRequest* req_handle = nullptr;
tdgbl->systemFields.setSortMode(Firebird::FB_ARRAY_SORT_MANUAL);
FOR(REQUEST_HANDLE req_handle)
X IN RDB$FIELDS WITH
X.RDB$SYSTEM_FLAG EQ 1
const auto len = MISC_symbol_length(X.RDB$FIELD_NAME, sizeof(X.RDB$FIELD_NAME));
MetaString name(X.RDB$FIELD_NAME, len);
tdgbl->systemFields.add(name);
END_FOR;
ON_ERROR
general_on_error();
END_ERROR;
MISC_release_request_silent(req_handle);
tdgbl->systemFields.sort();
}
while (get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
@ -10496,6 +10546,28 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file
}
break;
case att_default_pub_active:
if (tdgbl->RESTORE_format >= 11)
tdgbl->gbl_default_pub_active = get_boolean(tdgbl);
else
{
// Functions that use scan_next_attr initialize it to NO_SKIP using skip_init().
// Here we don't use that logic, hence the first param to bad_attribute is hardcoded.
bad_attribute(NO_SKIP, attribute, 352);
}
break;
case att_default_pub_auto_enable:
if (tdgbl->RESTORE_format >= 11)
tdgbl->gbl_default_pub_auto_enable = get_boolean(tdgbl);
else
{
// Functions that use scan_next_attr initialize it to NO_SKIP using skip_init().
// Here we don't use that logic, hence the first param to bad_attribute is hardcoded.
bad_attribute(NO_SKIP, attribute, 352);
}
break;
default:
{
SSHORT l = get(tdgbl);
@ -11585,6 +11657,12 @@ void WriteRelationMeta::setRelation(BurpGlobals* tdgbl, const burp_rel* relation
if (tdgbl->gbl_network_protocol == 0)
{
// If user relation uses system field there is a chance that definition of such
// system field was changed in target database. Old, BLR-based code, didn't
// coerce data types thus error could happen. To avoid it, let use SQL-based
// approach, while it is a bit slower in embedded mode.
// Here we are mostly interested in legacy UNICODE_FSS fields - see #7611.
bool sysDomFlag = false;
burp_fld* field;
@ -11593,27 +11671,7 @@ void WriteRelationMeta::setRelation(BurpGlobals* tdgbl, const burp_rel* relation
if (field->fld_flags & FLD_computed)
continue;
const char* dom = field->fld_source;
if (strncmp(dom, "RDB$", 4) == 0)
{
for (dom += 4; *dom; ++dom)
{
#ifdef HAVE_CTYPE_H
if (!isdigit(*dom))
#else
if (*dom < '0' || *dom > '9')
#endif
{
sysDomFlag = true;
break;
}
}
}
// Not all system domains starts with RDB$
// There is also SEC$ fields. Here we are mostly interested in legacy UNICODE_FSS fields - see #7611.
if (field->fld_flags & FLD_system_domain)
if (tdgbl->systemFields.exist(field->fld_source))
{
sysDomFlag = true;
break;

View File

@ -222,6 +222,7 @@ PARSER_TOKEN(TOK_FLOOR, "FLOOR", true)
PARSER_TOKEN(TOK_FOLLOWING, "FOLLOWING", true)
PARSER_TOKEN(TOK_FOR, "FOR", false)
PARSER_TOKEN(TOK_FOREIGN, "FOREIGN", false)
PARSER_TOKEN(TOK_FORMAT, "FORMAT", true)
PARSER_TOKEN(TOK_FREE_IT, "FREE_IT", true)
PARSER_TOKEN(TOK_FROM, "FROM", false)
PARSER_TOKEN(TOK_FULL, "FULL", false)

View File

@ -80,7 +80,6 @@ namespace
static const TimeZoneDesc* getDesc(USHORT timeZone);
static inline bool isOffset(USHORT timeZone);
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm);
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone);
static inline USHORT displacementToOffsetZone(SSHORT displacement);
static int parseNumber(const char*& p, const char* end);
@ -634,6 +633,20 @@ void TimeZoneUtil::extractOffset(const ISC_TIME_TZ& timeTz, SSHORT* offset)
extractOffset(tsTz, offset);
}
// Makes a time zone id from offsets.
USHORT TimeZoneUtil::makeFromOffset(int sign, unsigned tzh, unsigned tzm)
{
if (!TimeZoneUtil::isValidOffset(sign, tzh, tzm))
{
string str;
str.printf("%s%02u:%02u", (sign == -1 ? "-" : "+"), tzh, tzm);
status_exception::raise(Arg::Gds(isc_invalid_timezone_offset) << str);
}
return (USHORT)displacementToOffsetZone((tzh * 60 + tzm) * sign);
}
// Converts a time from local to UTC.
void TimeZoneUtil::localTimeToUtc(ISC_TIME& time, ISC_USHORT timeZone)
{
@ -1156,19 +1169,6 @@ static inline bool isOffset(USHORT timeZone)
return timeZone <= ONE_DAY * 2;
}
// Makes a time zone id from offsets.
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm)
{
if (!TimeZoneUtil::isValidOffset(sign, tzh, tzm))
{
string str;
str.printf("%s%02u:%02u", (sign == -1 ? "-" : "+"), tzh, tzm);
status_exception::raise(Arg::Gds(isc_invalid_timezone_offset) << str);
}
return (USHORT)displacementToOffsetZone((tzh * 60 + tzm) * sign);
}
// Gets the displacement from a offset-based time zone id.
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone)
{

View File

@ -97,6 +97,8 @@ public:
static void extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, SSHORT* offset);
static void extractOffset(const ISC_TIME_TZ& timeTz, SSHORT* offset);
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm);
static void localTimeToUtc(ISC_TIME& time, ISC_USHORT timeZone);
static void localTimeToUtc(ISC_TIME_TZ& timeTz);

View File

@ -337,6 +337,80 @@ void NoThrowTimeStamp::round_time(ISC_TIME &ntime, const int precision)
ntime -= (ntime % period);
}
int NoThrowTimeStamp::convertGregorianDateToWeekDate(const struct tm& times)
{
// Algorithm for Converting Gregorian Dates to ISO 8601 Week Date by Rick McCarty, 1999
// http://personal.ecu.edu/mccartyr/ISOwdALG.txt
const int y = times.tm_year + 1900;
const int dayOfYearNumber = times.tm_yday + 1;
// Find the jan1Weekday for y (Monday=1, Sunday=7)
const int yy = (y - 1) % 100;
const int c = (y - 1) - yy;
const int g = yy + yy / 4;
const int jan1Weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
// Find the weekday for y m d
const int h = dayOfYearNumber + (jan1Weekday - 1);
const int weekday = 1 + ((h - 1) % 7);
// Find if y m d falls in yearNumber y-1, weekNumber 52 or 53
int yearNumber, weekNumber;
if ((dayOfYearNumber <= (8 - jan1Weekday)) && (jan1Weekday > 4))
{
yearNumber = y - 1;
weekNumber = ((jan1Weekday == 5) || ((jan1Weekday == 6) &&
isLeapYear(yearNumber))) ? 53 : 52;
}
else
{
yearNumber = y;
// Find if y m d falls in yearNumber y+1, weekNumber 1
int i = isLeapYear(y) ? 366 : 365;
if ((i - dayOfYearNumber) < (4 - weekday))
{
yearNumber = y + 1;
weekNumber = 1;
}
}
// Find if y m d falls in yearNumber y, weekNumber 1 through 53
if (yearNumber == y)
{
int j = dayOfYearNumber + (7 - weekday) + (jan1Weekday - 1);
weekNumber = j / 7;
if (jan1Weekday > 4)
weekNumber--;
}
return weekNumber;
}
int NoThrowTimeStamp::convertGregorianDateToJulianDate(int year, int month, int day)
{
int jdn = (1461 * (year + 4800 + (month - 14)/12))/4 + (367 * (month - 2 - 12 * ((month - 14)/12)))
/ 12 - (3 * ((year + 4900 + (month - 14)/12)/100))/4 + day - 32075;
return jdn;
}
void NoThrowTimeStamp::convertJulianDateToGregorianDate(int jdn, int& outYear, int& outMonth, int& outDay)
{
int a = jdn + 32044;
int b = (4 * a +3 ) / 146097;
int c = a - (146097 * b) / 4;
int d = (4 * c + 3) / 1461;
int e = c - (1461 * d) / 4;
int m = (5 * e + 2) / 153;
outDay = e - (153 * m + 2) / 5 + 1;
outMonth = m + 3 - 12 * (m / 10);
outYear = 100 * b + d - 4800 + (m / 10);
}
// Encode timestamp from UNIX datetime structure
void NoThrowTimeStamp::encode(const struct tm* times, int fractions)
{

View File

@ -168,6 +168,10 @@ public:
static void add10msec(ISC_TIMESTAMP* v, SINT64 msec, SINT64 multiplier);
static void round_time(ISC_TIME& ntime, const int precision);
static int convertGregorianDateToWeekDate(const struct tm& times);
static int convertGregorianDateToJulianDate(int year, int month, int day);
static void convertJulianDateToGregorianDate(int jdn, int& outYear, int& outMonth, int& outDay);
static inline bool isLeapYear(const int year) noexcept
{
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);

View File

@ -29,6 +29,7 @@
#ifndef CLASSES_TRISTATE_H
#define CLASSES_TRISTATE_H
namespace Firebird {
class TriState
{
@ -129,5 +130,6 @@ inline bool TriState::toggle()
return true;
}
} // namespace Firebird
#endif // CLASSES_TRISTATE_H

View File

@ -136,12 +136,22 @@ public:
return *this;
}
Where* get() const
const Where* get() const
{
return ptr;
}
operator Where*() const
operator const Where*() const
{
return ptr;
}
Where* get()
{
return ptr;
}
operator Where*()
{
return ptr;
}

View File

@ -971,7 +971,7 @@ const int HIGH_WORD = 0;
#endif
#endif
static const TEXT FB_SHORT_MONTHS[][4] =
inline const TEXT FB_SHORT_MONTHS[][4] =
{
"Jan", "Feb", "Mar",
"Apr", "May", "Jun",
@ -980,7 +980,7 @@ static const TEXT FB_SHORT_MONTHS[][4] =
"\0"
};
static const TEXT* const FB_LONG_MONTHS_UPPER[] =
inline const TEXT* const FB_LONG_MONTHS_UPPER[] =
{
"JANUARY",
"FEBRUARY",
@ -997,6 +997,32 @@ static const TEXT* const FB_LONG_MONTHS_UPPER[] =
0
};
// Starts with SUNDAY cuz tm.tm_wday starts with it
inline const TEXT FB_SHORT_DAYS[][4] =
{
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"\0"
};
// Starts with SUNDAY cuz tm.tm_wday starts with it
inline const TEXT* const FB_LONG_DAYS_UPPER[] =
{
"SUNDAY",
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY",
"\0"
};
const FB_SIZE_T FB_MAX_SIZEOF = ~FB_SIZE_T(0); // Assume FB_SIZE_T is unsigned
inline FB_SIZE_T fb_strlen(const char* str)

File diff suppressed because it is too large Load Diff

View File

@ -108,5 +108,7 @@ 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, Firebird::Callbacks* cb);
#endif //COMMON_CVT_H

View File

@ -0,0 +1,321 @@
#include "boost/test/unit_test.hpp"
#include "../common/tests/CvtTestUtils.h"
#include "../jrd/cvt_proto.h"
using namespace Firebird;
using namespace Jrd;
using namespace CvtTestUtils;
BOOST_AUTO_TEST_SUITE(CVTSuite)
BOOST_AUTO_TEST_SUITE(CVTDatetimeFormat)
static void errFunc(const Firebird::Arg::StatusVector& v)
{
v.raise();
}
CVTCallback cb(errFunc);
BOOST_AUTO_TEST_SUITE(CVTDatetimeToFormatString)
template<typename T>
static void testCVTDatetimeToFormatString(T date, const string& format, const string& expected, Callbacks& cb)
{
dsc desc;
desc.dsc_dtype = getDSCTypeFromDateType<T>();
desc.dsc_length = sizeof(T);
desc.dsc_address = (UCHAR*) &date;
desc.dsc_scale = 0;
string result = CVT_datetime_to_format_string(&desc, format, &cb);
BOOST_TEST(result == expected, "\nRESULT: " << result.c_str() << "\nEXPECTED: " << expected.c_str());
}
BOOST_AUTO_TEST_SUITE(FunctionalTest)
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_DATE)
{
testCVTDatetimeToFormatString(createDate(1, 1, 1), "YEAR.YYYY.YYY.YY.Y", "1.0001.001.01.1", cb);
testCVTDatetimeToFormatString(createDate(1234, 1, 1), "YEAR.YYYY.YYY.YY.Y", "1234.1234.234.34.4", cb);
testCVTDatetimeToFormatString(createDate(9999, 1, 1), "YEAR.YYYY.YYY.YY.Y", "9999.9999.999.99.9", cb);
testCVTDatetimeToFormatString(createDate(1, 1, 1), "YEAR.YYYY.YYY.YY.Y", "1.0001.001.01.1", cb);
testCVTDatetimeToFormatString(createDate(1234, 1, 1), "YEAR.YYYY.YYY.YY.Y", "1234.1234.234.34.4", cb);
testCVTDatetimeToFormatString(createDate(9999, 1, 1), "YEAR.YYYY.YYY.YY.Y", "9999.9999.999.99.9", cb);
testCVTDatetimeToFormatString(createDate(1, 1, 1), "Q", "1", cb);
testCVTDatetimeToFormatString(createDate(1, 4, 1), "Q", "2", cb);
testCVTDatetimeToFormatString(createDate(1, 7, 1), "Q", "3", cb);
testCVTDatetimeToFormatString(createDate(1, 10, 1), "Q", "4", cb);
testCVTDatetimeToFormatString(createDate(1, 1, 1), "MM:RM-MON/MONTH", "01:I-Jan/JANUARY", cb);
testCVTDatetimeToFormatString(createDate(1, 6, 1), "MM-RM.MON;MONTH", "06-VI.Jun;JUNE", cb);
testCVTDatetimeToFormatString(createDate(1, 12, 1), "MM,RM.MON:MONTH", "12,XII.Dec:DECEMBER", cb);
testCVTDatetimeToFormatString(createDate(1, 1, 1), "WW/W", "01/1", cb);
testCVTDatetimeToFormatString(createDate(1, 6, 15), "WW-W", "24-3", cb);
testCVTDatetimeToFormatString(createDate(1, 12, 30), "WW.W", "52.5", cb);
testCVTDatetimeToFormatString(createDate(2023, 6, 4), "D;DAY-DY", "1;SUNDAY-Sun", cb);
testCVTDatetimeToFormatString(createDate(2023, 6, 7), "D.DAY,DY", "4.WEDNESDAY,Wed", cb);
testCVTDatetimeToFormatString(createDate(2023, 6, 10), "D DAY DY", "7 SATURDAY Sat", cb);
testCVTDatetimeToFormatString(createDate(1, 1, 1), "DDD", "001", cb);
testCVTDatetimeToFormatString(createDate(1, 1, 12), "DDD", "012", cb);
testCVTDatetimeToFormatString(createDate(1, 6, 15), "DDD", "166", cb);
testCVTDatetimeToFormatString(createDate(1, 12, 31), "DDD", "365", cb);
testCVTDatetimeToFormatString(createDate(1, 1, 1), "J", "1721426", cb);
testCVTDatetimeToFormatString(createDate(2000, 12, 8), "J", "2451887", cb);
testCVTDatetimeToFormatString(createDate(9999, 12, 31), "J", "5373484", cb);
}
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_TIME)
{
testCVTDatetimeToFormatString(createTime(0, 0, 0), "HH-HH12.HH24,MI/SS SSSSS", "12 AM-12 AM.00,00/00 0", cb);
testCVTDatetimeToFormatString(createTime(12, 35, 15), "HH.HH12:HH24;MI-SS/SSSSS", "12 PM.12 PM:12;35-15/45315", cb);
testCVTDatetimeToFormatString(createTime(23, 59, 59), " HH - HH12 . HH24 , MI / SS SSSSS ", " 11 PM - 11 PM . 23 , 59 / 59 86399 ", cb);
testCVTDatetimeToFormatString(createTime(0, 0, 0, 1), "FF1.FF2/FF3;FF4:FF5-FF6,FF7-FF8 FF9", "1.10/100;1000:10000-100000,1000000-10000000 100000000", cb);
testCVTDatetimeToFormatString(createTime(0, 0, 0, 1000), "FF1.FF2/FF3;FF4:FF5-FF6,FF7-FF8 FF9", "1.10/100;1000:10000-100000,1000000-10000000 100000000", cb);
testCVTDatetimeToFormatString(createTime(0, 0, 0, 9999), "FF1.FF2/FF3;FF4:FF5-FF6,FF7-FF8 FF9", "9.99/999;9999:99990-999900,9999000-99990000 999900000", cb);
}
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_TIMESTAMP)
{
ISC_TIMESTAMP timestamp = createTimeStamp(1982, 4, 21, 1, 34, 15, 2500);
testCVTDatetimeToFormatString(timestamp, "YEAR.YYYY.YYY.YY.Y/J", "1982.1982.982.82.2/2445081", cb);
testCVTDatetimeToFormatString(timestamp, "Q-MM-RM-MON-MONTH", "2-04-IV-Apr-APRIL", cb);
testCVTDatetimeToFormatString(timestamp, "WW,W-D;DAY:DD DDD.DY", "16,3-4;WEDNESDAY:21 111.Wed", cb);
testCVTDatetimeToFormatString(timestamp, "HH-HH12-HH24-MI-SS-SSSSS.FF2", "01 AM-01 AM-01-34-15-5655.25", cb);
}
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_TIME_TZ)
{
testCVTDatetimeToFormatString(createTimeTZ(15, 35, 59, 0, 900), "HH-HH12-HH24-MI-SS-SSSSS.FF1/TZH/TZM", "03 PM-03 PM-15-35-59-56159.9/+00/00", cb);
testCVTDatetimeToFormatString(createTimeTZ(15, 35, 59, 160), "HH24:MI-TZH:TZM", "18:15-+02:40", cb);
testCVTDatetimeToFormatString(createTimeTZ(15, 35, 59, -160), "HH24:MI TZH:TZM", "12:55 -02:40", cb);
testCVTDatetimeToFormatString(createTimeTZ(0, 0, 0, 160), "TZM:TZH", "+40:02", cb);
testCVTDatetimeToFormatString(createTimeTZ(0, 0, 0, 160), "TZH MI TZM", "+02 40 +40", cb);
testCVTDatetimeToFormatString(createTimeTZ(0, 0, 0, -160), "TZH MI TZM", "-02 20 -40", cb);
}
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_TIMESTAMP_TZ)
{
ISC_TIMESTAMP_TZ timestampTZ = createTimeStampTZ(1982, 4, 21, 1, 34, 15, 0, 500);
testCVTDatetimeToFormatString(timestampTZ, "YEAR.YYYY.YYY.YY.Y/J", "1982.1982.982.82.2/2445081", cb);
testCVTDatetimeToFormatString(timestampTZ, "Q-MM-RM-MON-MONTH", "2-04-IV-Apr-APRIL", cb);
testCVTDatetimeToFormatString(timestampTZ, "WW,W-D;DAY:DD DDD.DY", "16,3-4;WEDNESDAY:21 111.Wed", cb);
testCVTDatetimeToFormatString(timestampTZ, "HH-HH12-HH24-MI-SS-SSSSS.FF2/TZH/TZM", "01 AM-01 AM-01-34-15-5655.50/+00/00", cb);
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 1, 34, 15, 70), "HH24:MI-TZH:TZM", "02:44-+01:10", cb);
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 1, 34, 15, -70), "HH24:MI TZH:TZM", "00:24 -01:10", cb);
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 0, 0, 0, 160), "TZM:TZH", "+40:02", cb);
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 0, 0, 0, 160), "TZH MI TZM", "+02 40 +40", cb);
testCVTDatetimeToFormatString(createTimeStampTZ(1982, 4, 21, 0, 0, 0, -160), "TZH MI TZM", "-02 20 -40", cb);
}
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_SOLID_PATTERNS)
{
ISC_TIMESTAMP_TZ timestampTZ = createTimeStampTZ(1982, 4, 21, 1, 34, 15, 0, 500);
testCVTDatetimeToFormatString(timestampTZ, "YEARYYYYYYYYYYJ", "198219821982822445081", cb);
testCVTDatetimeToFormatString(timestampTZ, "QMMRMMONMONTH", "204IVAprAPRIL", cb);
testCVTDatetimeToFormatString(timestampTZ, "WWWD/DAYDDDDDDY", "1634/WEDNESDAY1111112", cb);
testCVTDatetimeToFormatString(timestampTZ, "HHHH12HH24MISSSSSSSFF2TZHTZM", "01 AM01 AM013456551550+0000", cb);
}
BOOST_AUTO_TEST_CASE(CVTDatetimeToFormatStringTest_RAW_TEXT)
{
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "YYYY-\"RaW TeXt\"-MON", "1981-RaW TeXt-Jul", cb);
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "YYYY-\"Raw Text with \\\"Quotes\\\"\"-MON", "1981-Raw Text with \"Quotes\"-Jul", cb);
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "YYYY-\"\\\\\\\"\\\\BS\\\\\\\"\\\\\"-YYYY", "1981-\\\"\\BS\\\"\\-1981", cb);
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"Test1\"-Y\"Test2\"", "Test1-1Test2", cb);
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"\"-Y\"Test2\"", "-1Test2", cb);
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"Test1\"-Y\"\"", "Test1-1", cb);
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"\"-Y\"\"", "-1", cb);
testCVTDatetimeToFormatString(createDate(1981, 7, 12), "\"\"\"\"", "", cb);
}
BOOST_AUTO_TEST_SUITE_END() // FunctionalTest
BOOST_AUTO_TEST_SUITE_END() // CVTDatetimeToFormatString
BOOST_AUTO_TEST_SUITE(CVTStringToFormatDateTime)
static void testCVTStringToFormatDateTime(const string& date, const string& format,
const ISC_TIMESTAMP_TZ& expected, Callbacks& cb)
{
string varyingString = "xx";
varyingString += date;
*(USHORT*) varyingString.data() = varyingString.size() - sizeof(USHORT);
dsc desc;
desc.dsc_dtype = dtype_varying;
desc.dsc_length = varyingString.size() + sizeof(USHORT);
desc.dsc_address = (UCHAR*) varyingString.data();
desc.dsc_scale = 0;
const ISC_TIMESTAMP_TZ result = CVT_string_to_format_datetime(&desc, format, &cb);
struct tm resultTimes;
memset(&resultTimes, 0, sizeof(resultTimes));
int resultFractions;
NoThrowTimeStamp::decode_timestamp(result.utc_timestamp, &resultTimes, &resultFractions);
SSHORT resultOffset;
TimeZoneUtil::extractOffset(result, &resultOffset);
struct tm expectedTimes;
memset(&expectedTimes, 0, sizeof(expectedTimes));
int expectedFractions;
NoThrowTimeStamp::decode_timestamp(expected.utc_timestamp, &expectedTimes, &expectedFractions);
SSHORT expectedOffset;
TimeZoneUtil::extractOffset(expected, &expectedOffset);
bool isEqual = !((bool) memcmp(&resultTimes, &expectedTimes, sizeof(struct tm)))
&& resultFractions == expectedFractions && resultOffset == expectedOffset;
BOOST_TEST(isEqual, "\nRESULT: " << DECOMPOSE_TM_STRUCT(resultTimes, resultFractions, resultOffset)
<< "\nEXPECTED: " << DECOMPOSE_TM_STRUCT(expectedTimes, expectedFractions, expectedOffset));
}
BOOST_AUTO_TEST_SUITE(FunctionalTest)
BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_DATE)
{
testCVTStringToFormatDateTime("1", "YEAR", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1234", "YEAR", createTimeStampTZ(1234, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("9999", "YEAR", createTimeStampTZ(9999, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1", "YYYY", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1234", "YYYY", createTimeStampTZ(1234, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("9999", "YYYY", createTimeStampTZ(9999, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1", "YYY", createTimeStampTZ(2001, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("450", "YYY", createTimeStampTZ(2450, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("451", "YYY", createTimeStampTZ(1451, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("999", "YYY", createTimeStampTZ(1999, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1", "YY", createTimeStampTZ(2001, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("45", "YY", createTimeStampTZ(2045, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("46", "YY", createTimeStampTZ(1946, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("99", "YY", createTimeStampTZ(1999, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1", "Y", createTimeStampTZ(2001, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("9", "Y", createTimeStampTZ(2009, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1", "MM", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("6", "MM", createTimeStampTZ(1, 6, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("12", "MM", createTimeStampTZ(1, 12, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("Jan", "MON", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("Jun", "MON", createTimeStampTZ(1, 6, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("Dec", "MON", createTimeStampTZ(1, 12, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("January", "MONTH", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("June", "MONTH", createTimeStampTZ(1, 6, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("December", "MONTH", createTimeStampTZ(1, 12, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("I", "RM", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("IV", "RM", createTimeStampTZ(1, 4, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("XII", "RM", createTimeStampTZ(1, 12, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1", "DD", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("15", "DD", createTimeStampTZ(1, 1, 15, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("31", "DD", createTimeStampTZ(1, 1, 31, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("2451887", "J", createTimeStampTZ(2000, 12, 8, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1721426", "J", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("5373484", "J", createTimeStampTZ(9999, 12, 31, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1:1,1", "YEAR.MM.DD", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1981-8/13", "YEAR.MM.DD", createTimeStampTZ(1981, 8, 13, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("9999 12;31", "YEAR.MM.DD", createTimeStampTZ(9999, 12, 31, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("25.Jan.25", "YY;MON;DD", createTimeStampTZ(2025, 1, 25, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("./.1981./-8--/13--", " YEAR. -.MM.,,-.DD//", createTimeStampTZ(1981, 8, 13, 0, 0, 0, 0), cb);
}
BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_TIME)
{
testCVTStringToFormatDateTime("12 AM", "HH", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1 AM", "HH", createTimeStampTZ(1, 1, 1, 1, 0, 0, 0), cb);
testCVTStringToFormatDateTime("11 AM", "HH", createTimeStampTZ(1, 1, 1, 11, 0, 0, 0), cb);
testCVTStringToFormatDateTime("12 PM", "HH", createTimeStampTZ(1, 1, 1, 12, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1 PM", "HH", createTimeStampTZ(1, 1, 1, 13, 0, 0, 0), cb);
testCVTStringToFormatDateTime("11 PM", "HH", createTimeStampTZ(1, 1, 1, 23, 0, 0, 0), cb);
testCVTStringToFormatDateTime("12 AM", "HH12", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1 AM", "HH12", createTimeStampTZ(1, 1, 1, 1, 0, 0, 0), cb);
testCVTStringToFormatDateTime("11 AM", "HH12", createTimeStampTZ(1, 1, 1, 11, 0, 0, 0), cb);
testCVTStringToFormatDateTime("12 PM", "HH12", createTimeStampTZ(1, 1, 1, 12, 0, 0, 0), cb);
testCVTStringToFormatDateTime("1 PM", "HH12", createTimeStampTZ(1, 1, 1, 13, 0, 0, 0), cb);
testCVTStringToFormatDateTime("11 PM", "HH12", createTimeStampTZ(1, 1, 1, 23, 0, 0, 0), cb);
testCVTStringToFormatDateTime("0", "HH24", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("12", "HH24", createTimeStampTZ(1, 1, 1, 12, 0, 0, 0), cb);
testCVTStringToFormatDateTime("23", "HH24", createTimeStampTZ(1, 1, 1, 23, 0, 0, 0), cb);
testCVTStringToFormatDateTime("0", "MI", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("30", "MI", createTimeStampTZ(1, 1, 1, 0, 30, 0, 0), cb);
testCVTStringToFormatDateTime("59", "MI", createTimeStampTZ(1, 1, 1, 0, 59, 0, 0), cb);
testCVTStringToFormatDateTime("0", "SS", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("30", "SS", createTimeStampTZ(1, 1, 1, 0, 0, 30, 0), cb);
testCVTStringToFormatDateTime("59", "SS", createTimeStampTZ(1, 1, 1, 0, 0, 59, 0), cb);
testCVTStringToFormatDateTime("0", "SSSSS", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0), cb);
testCVTStringToFormatDateTime("45315", "SSSSS", createTimeStampTZ(1, 1, 1, 12, 35, 15, 0), cb);
testCVTStringToFormatDateTime("86399", "SSSSS", createTimeStampTZ(1, 1, 1, 23, 59, 59, 0), cb);
testCVTStringToFormatDateTime("1", "FF1", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1000), cb);
testCVTStringToFormatDateTime("5", "FF1", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 5000), cb);
testCVTStringToFormatDateTime("9", "FF1", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 9000), cb);
testCVTStringToFormatDateTime("1", "FF2", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 100), cb);
testCVTStringToFormatDateTime("10", "FF2", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1000), cb);
testCVTStringToFormatDateTime("50", "FF2", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 5000), cb);
testCVTStringToFormatDateTime("99", "FF2", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 9900), cb);
testCVTStringToFormatDateTime("1", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 10), cb);
testCVTStringToFormatDateTime("10", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 100), cb);
testCVTStringToFormatDateTime("100", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1000), cb);
testCVTStringToFormatDateTime("500", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 5000), cb);
testCVTStringToFormatDateTime("999", "FF3", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 9990), cb);
testCVTStringToFormatDateTime("1", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1), cb);
testCVTStringToFormatDateTime("10", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 10), cb);
testCVTStringToFormatDateTime("100", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 100), cb);
testCVTStringToFormatDateTime("1000", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 1000), cb);
testCVTStringToFormatDateTime("5000", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 5000), cb);
testCVTStringToFormatDateTime("9999", "FF4", createTimeStampTZ(1, 1, 1, 0, 0, 0, 0, 9999), cb);
testCVTStringToFormatDateTime("1 PM - 25 - 45 - 200", "HH.MI.SS.FF4", createTimeStampTZ(1, 1, 1, 13, 25, 45, 0, 200), cb);
testCVTStringToFormatDateTime("15:0:15:2", "HH24.MI.SS.FF1", createTimeStampTZ(1, 1, 1, 15, 0, 15, 0, 2000), cb);
}
BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_TZ)
{
testCVTStringToFormatDateTime("12:00 2:30", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, 150, 0), cb);
testCVTStringToFormatDateTime("12:00 +2:30", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, 150, 0), cb);
testCVTStringToFormatDateTime("12:00 -2:30", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, -150, 0), cb);
testCVTStringToFormatDateTime("12:00 +0:30", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, 30, 0), cb);
testCVTStringToFormatDateTime("12:00 +0:00", "HH24:MI TZH:TZM", createTimeStampTZ(1, 1, 1, 12, 0, 0, 0, 0), cb);
}
BOOST_AUTO_TEST_CASE(CVTStringToFormatDateTime_SOLID_PATTERNS)
{
testCVTStringToFormatDateTime("1 PM - 25 - 45 - 200", "HHMISSFF4", createTimeStampTZ(1, 1, 1, 13, 25, 45, 0, 200), cb);
testCVTStringToFormatDateTime("1981-8/13", "YEARMMDD", createTimeStampTZ(1981, 8, 13, 0, 0, 0, 0), cb);
}
BOOST_AUTO_TEST_SUITE_END() // FunctionalTest
BOOST_AUTO_TEST_SUITE_END() // CVTStringToFormatDateTime
BOOST_AUTO_TEST_SUITE_END() // CVTDatetimeFormat
BOOST_AUTO_TEST_SUITE_END() // CVTSuite

View File

@ -0,0 +1,126 @@
#ifndef CVT_TEST_UTILS_H
#define CVT_TEST_UTILS_H
#include "firebird.h"
#include "../common/dsc.h"
#include "../common/TimeZoneUtil.h"
#include "../common/TimeZones.h"
using Firebird::NoThrowTimeStamp;
using Firebird::TimeZoneUtil;
namespace CvtTestUtils {
#define DECOMPOSE_TM_STRUCT(times, fractions, timezone) "Year:" << times.tm_year + 1900 << \
" Month:" << times.tm_mon << \
" Day:" << times.tm_mday << \
" Hour:" << times.tm_hour << \
" Min:" << times.tm_min << \
" Sec:" << times.tm_sec << \
" Fract: " << fractions << \
" IsDST:" << times.tm_isdst << \
" WDay:" << times.tm_wday << \
" YDay:" << times.tm_yday << \
" TZ Offset:" << timezone \
template<typename T>
static constexpr int sign(T value)
{
return (T(0) < value) - (value < T(0));
}
static struct tm initTMStruct(int year, int month, int day)
{
struct tm times;
memset(&times, 0, sizeof(struct tm));
times.tm_year = year - 1900;
times.tm_mon = month - 1;
times.tm_mday = day;
mktime(&times);
return times;
}
static ISC_DATE createDate(int year, int month, int day)
{
struct tm times = initTMStruct(year, month, day);
return NoThrowTimeStamp::encode_date(&times);
}
static ISC_TIME createTime(int hours, int minutes, int seconds, int fractions = 0)
{
return NoThrowTimeStamp::encode_time(hours, minutes, seconds, fractions);
}
static ISC_TIMESTAMP createTimeStamp(int year, int month, int day, int hours, int minutes, int seconds, int fractions = 0)
{
struct tm times = initTMStruct(year, month, day);
times.tm_hour = hours;
times.tm_min = minutes;
times.tm_sec = seconds;
return NoThrowTimeStamp::encode_timestamp(&times, fractions);
}
static ISC_TIME_TZ createTimeTZ(int hours, int minutes, int seconds, int offsetInMinutes, int fractions = 0)
{
ISC_TIME_TZ timeTZ;
timeTZ.time_zone = TimeZoneUtil::makeFromOffset(sign(offsetInMinutes), abs(offsetInMinutes / 60),
abs(offsetInMinutes % 60));
timeTZ.utc_time = createTime(hours, minutes, seconds, fractions);
return timeTZ;
}
static ISC_TIMESTAMP_TZ createTimeStampTZ(int year, int month, int day, int hours, int minutes, int seconds,
int offsetInMinutes, int fractions = 0)
{
ISC_TIMESTAMP_TZ timestampTZ;
timestampTZ.time_zone = TimeZoneUtil::makeFromOffset(sign(offsetInMinutes), abs(offsetInMinutes / 60),
abs(offsetInMinutes % 60));
timestampTZ.utc_timestamp = createTimeStamp(year, month, day, hours, minutes, seconds, fractions);
return timestampTZ;
}
class CVTCallback : public Firebird::Callbacks
{
public:
explicit CVTCallback(ErrorFunction aErr) : Callbacks(aErr)
{}
public:
bool transliterate(const dsc* from, dsc* to, CHARSET_ID&) override { return true; }
CHARSET_ID getChid(const dsc* d) override { return 0; }
Jrd::CharSet* getToCharset(CHARSET_ID charset2) override { return nullptr; }
void validateData(Jrd::CharSet* toCharset, SLONG length, const UCHAR* q) override { }
ULONG validateLength(Jrd::CharSet* charSet, CHARSET_ID charSetId, ULONG length, const UCHAR* start,
const USHORT size) override { return 0; }
SLONG getLocalDate() override { return 0; }
ISC_TIMESTAMP getCurrentGmtTimeStamp() override { ISC_TIMESTAMP ts; return ts; }
USHORT getSessionTimeZone() override { return 1439; }
void isVersion4(bool& v4) override { }
};
template<typename T>
static UCHAR getDSCTypeFromDateType() { return 0; }
template<>
UCHAR getDSCTypeFromDateType<ISC_DATE>() { return dtype_sql_date; }
template<>
UCHAR getDSCTypeFromDateType<ISC_TIME>() { return dtype_sql_time; }
template<>
UCHAR getDSCTypeFromDateType<ISC_TIMESTAMP>() { return dtype_timestamp; }
template<>
UCHAR getDSCTypeFromDateType<ISC_TIME_TZ>() { return dtype_sql_time_tz; }
template<>
UCHAR getDSCTypeFromDateType<ISC_TIMESTAMP_TZ>() { return dtype_timestamp_tz; }
} // namespace CvtTestUtils
#endif // CVT_TEST_UTILS_H

View File

@ -703,7 +703,7 @@ public:
MetaName name;
MetaName relationName;
std::optional<FB_UINT64> type;
TriState active;
Firebird::TriState active;
std::optional<int> position;
NestConst<ExternalClause> external;
Firebird::string source;
@ -993,7 +993,7 @@ public:
NestConst<ValueSourceClause> setDefault;
MetaName renameTo;
Firebird::AutoPtr<dsql_fld> type;
TriState notNullFlag; // true = NOT NULL / false = NULL
Firebird::TriState notNullFlag; // true = NOT NULL / false = NULL
};
@ -1217,7 +1217,7 @@ public:
MetaName identitySequence;
std::optional<IdentityType> identityType;
std::optional<USHORT> collationId;
TriState notNullFlag; // true = NOT NULL / false = NULL
Firebird::TriState notNullFlag; // true = NOT NULL / false = NULL
std::optional<USHORT> position;
Firebird::string defaultSource;
Firebird::ByteChunk defaultValue;
@ -1556,8 +1556,8 @@ public:
NestConst<RelationSourceNode> dsqlNode;
MetaName name;
Firebird::Array<NestConst<Clause> > clauses;
TriState ssDefiner;
TriState replicationState;
Firebird::TriState ssDefiner;
Firebird::TriState replicationState;
};
@ -1729,9 +1729,9 @@ public:
MetaName relation;
Firebird::ObjectsArray<MetaName> columns;
TriState unique;
TriState descending;
TriState inactive;
Firebird::TriState unique;
Firebird::TriState descending;
Firebird::TriState inactive;
SSHORT type;
bid expressionBlr;
bid expressionSource;
@ -2222,8 +2222,8 @@ public:
Firebird::string* lastName;
MetaName* plugin;
Firebird::string* comment;
TriState adminRole;
TriState active;
Firebird::TriState adminRole;
Firebird::TriState active;
Mode mode;
void addProperty(MetaName* pr, Firebird::string* val = NULL)
@ -2464,7 +2464,7 @@ public:
Firebird::Array<NestConst<DbFileClause> > files;
MetaName cryptPlugin;
MetaName keyName;
TriState ssDefiner;
Firebird::TriState ssDefiner;
Firebird::Array<MetaName> pubTables;
};

View File

@ -108,6 +108,10 @@ namespace
return castCoalesceNode->sameAs(coalesceNode, ignoreStreams);
}
}
else if (const auto castValueIfNode = nodeAs<ValueIfNode>(castNode->source))
{
return sameNodes(castValueIfNode, coalesceNode, ignoreStreams);
}
}
return false;
@ -3490,24 +3494,30 @@ dsc* BoolAsValueNode::execute(thread_db* tdbb, Request* request) const
//--------------------
static RegisterNode<CastNode> regCastNode({blr_cast});
static RegisterNode<CastNode> regCastNode({blr_cast, blr_cast_format});
CastNode::CastNode(MemoryPool& pool, ValueExprNode* aSource, dsql_fld* aDsqlField)
CastNode::CastNode(MemoryPool& pool, ValueExprNode* aSource, dsql_fld* aDsqlField, const string& aFormat)
: TypedNode<ValueExprNode, ExprNode::TYPE_CAST>(pool),
dsqlAlias("CAST"),
dsqlField(aDsqlField),
source(aSource),
itemInfo(NULL),
format(pool, aFormat),
artificial(false)
{
castDesc.clear();
}
// Parse a datatype cast.
DmlNode* CastNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/)
DmlNode* CastNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp)
{
fb_assert(blrOp == blr_cast || blrOp == blr_cast_format);
CastNode* node = FB_NEW_POOL(pool) CastNode(pool);
if (blrOp == blr_cast_format)
csb->csb_blr_reader.getString(node->format);
ItemInfo itemInfo;
PAR_desc(tdbb, csb, &node->castDesc, &itemInfo);
@ -3535,6 +3545,7 @@ string CastNode::internalPrint(NodePrinter& printer) const
NODE_PRINT(printer, castDesc);
NODE_PRINT(printer, source);
NODE_PRINT(printer, itemInfo);
NODE_PRINT(printer, format);
return "CastNode";
}
@ -3545,6 +3556,7 @@ ValueExprNode* CastNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
node->dsqlAlias = dsqlAlias;
node->source = doDsqlPass(dsqlScratch, source);
node->dsqlField = dsqlField;
node->format = format;
DDL_resolve_intl_type(dsqlScratch, node->dsqlField, NULL);
node->setParameterType(dsqlScratch, NULL, false);
@ -3589,8 +3601,16 @@ bool CastNode::setParameterType(DsqlCompilerScratch* /*dsqlScratch*/,
// Generate BLR for a data-type cast operation.
void CastNode::genBlr(DsqlCompilerScratch* dsqlScratch)
{
dsqlScratch->appendUChar(blr_cast);
if (format.hasData())
{
dsqlScratch->appendUChar(blr_cast_format);
dsqlScratch->appendString(0, format);
}
else
dsqlScratch->appendUChar(blr_cast);
dsqlScratch->putDtype(dsqlField, true);
GEN_expr(dsqlScratch, source);
}
@ -3636,6 +3656,7 @@ ValueExprNode* CastNode::copy(thread_db* tdbb, NodeCopier& copier) const
node->source = copier.copy(tdbb, source);
node->castDesc = castDesc;
node->itemInfo = itemInfo;
node->format = format;
return node;
}
@ -3648,7 +3669,7 @@ bool CastNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other
const CastNode* o = nodeAs<CastNode>(other);
fb_assert(o);
return dsqlField == o->dsqlField;
return dsqlField == o->dsqlField && format == o->format;
}
bool CastNode::sameAs(const ExprNode* other, bool ignoreStreams) const
@ -3662,7 +3683,7 @@ bool CastNode::sameAs(const ExprNode* other, bool ignoreStreams) const
const CastNode* const otherNode = nodeAs<CastNode>(other);
fb_assert(otherNode);
return DSC_EQUIV(&castDesc, &otherNode->castDesc, true);
return DSC_EQUIV(&castDesc, &otherNode->castDesc, true) && format == otherNode->format;
}
ValueExprNode* CastNode::pass1(thread_db* tdbb, CompilerScratch* csb)
@ -3702,12 +3723,12 @@ dsc* CastNode::execute(thread_db* tdbb, Request* request) const
const auto impure = request->getImpure<impure_value>(impureOffset);
return perform(tdbb, impure, value, &castDesc, itemInfo);
return perform(tdbb, impure, value, &castDesc, itemInfo, format);
}
// Cast from one datatype to another.
dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
const dsc* castDesc, const ItemInfo* itemInfo)
const dsc* castDesc, const ItemInfo* itemInfo, const Firebird::string& format)
{
// If validation is not required and the source value is either NULL
// or already in the desired data type, simply return it "as is"
@ -3763,7 +3784,58 @@ dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
if (!value)
return nullptr;
MOV_move(tdbb, value, &impure->vlu_desc);
if (format.hasData())
{
if (DTYPE_IS_TEXT(impure->vlu_desc.dsc_dtype))
{
string result = CVT_datetime_to_format_string(value, format, &EngineCallbacks::instance);
USHORT dscLength = DSC_string_length(&impure->vlu_desc);
string::size_type resultLength = result.length();
if (resultLength > dscLength)
{
ERR_post(Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) <<
Arg::Gds(isc_trunc_limits) << Arg::Num(dscLength) << Arg::Num(resultLength));
}
USHORT dscOffset = 0;
if (impure->vlu_desc.dsc_dtype == dtype_cstring)
dscOffset = 1;
else if (impure->vlu_desc.dsc_dtype == dtype_varying)
{
dscOffset = sizeof(USHORT);
((vary*) impure->vlu_desc.dsc_address)->vary_length = resultLength;
}
memcpy(impure->vlu_desc.dsc_address + dscOffset, result.c_str(), resultLength);
}
else
{
ISC_TIMESTAMP_TZ timestampTZ = CVT_string_to_format_datetime(value, format, &EngineCallbacks::instance);
switch (impure->vlu_desc.dsc_dtype)
{
case dtype_sql_time:
*(ISC_TIME*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp.timestamp_time;
break;
case dtype_sql_date:
*(ISC_DATE*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp.timestamp_date;
break;
case dtype_timestamp:
*(ISC_TIMESTAMP*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp;
break;
case dtype_sql_time_tz:
case dtype_ex_time_tz:
*(ISC_TIME_TZ*) impure->vlu_desc.dsc_address = TimeZoneUtil::timeStampTzToTimeTz(timestampTZ);
break;
case dtype_timestamp_tz:
case dtype_ex_timestamp_tz:
*(ISC_TIMESTAMP_TZ*) impure->vlu_desc.dsc_address = timestampTZ;
break;
}
}
}
else
MOV_move(tdbb, value, &impure->vlu_desc);
if (impure->vlu_desc.dsc_dtype == dtype_text)
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
@ -5777,55 +5849,7 @@ dsc* ExtractNode::execute(thread_db* tdbb, Request* request) const
case blr_extract_week:
{
// Algorithm for Converting Gregorian Dates to ISO 8601 Week Date by Rick McCarty, 1999
// http://personal.ecu.edu/mccartyr/ISOwdALG.txt
const int y = times.tm_year + 1900;
const int dayOfYearNumber = times.tm_yday + 1;
// Find the jan1Weekday for y (Monday=1, Sunday=7)
const int yy = (y - 1) % 100;
const int c = (y - 1) - yy;
const int g = yy + yy / 4;
const int jan1Weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
// Find the weekday for y m d
const int h = dayOfYearNumber + (jan1Weekday - 1);
const int weekday = 1 + ((h - 1) % 7);
// Find if y m d falls in yearNumber y-1, weekNumber 52 or 53
int yearNumber, weekNumber;
if ((dayOfYearNumber <= (8 - jan1Weekday)) && (jan1Weekday > 4))
{
yearNumber = y - 1;
weekNumber = ((jan1Weekday == 5) || ((jan1Weekday == 6) &&
TimeStamp::isLeapYear(yearNumber))) ? 53 : 52;
}
else
{
yearNumber = y;
// Find if y m d falls in yearNumber y+1, weekNumber 1
int i = TimeStamp::isLeapYear(y) ? 366 : 365;
if ((i - dayOfYearNumber) < (4 - weekday))
{
yearNumber = y + 1;
weekNumber = 1;
}
}
// Find if y m d falls in yearNumber y, weekNumber 1 through 53
if (yearNumber == y)
{
int j = dayOfYearNumber + (7 - weekday) + (jan1Weekday - 1);
weekNumber = j / 7;
if (jan1Weekday > 4)
weekNumber--;
}
part = weekNumber;
part = NoThrowTimeStamp::convertGregorianDateToWeekDate(times);
break;
}

View File

@ -242,7 +242,8 @@ public:
class CastNode final : public TypedNode<ValueExprNode, ExprNode::TYPE_CAST>
{
public:
explicit CastNode(MemoryPool& pool, ValueExprNode* aSource = NULL, dsql_fld* aDsqlField = NULL);
explicit CastNode(MemoryPool& pool, ValueExprNode* aSource = NULL, dsql_fld* aDsqlField = NULL,
const Firebird::string& aFormat = NULL);
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
@ -274,13 +275,14 @@ public:
virtual dsc* execute(thread_db* tdbb, Request* request) const;
static dsc* perform(thread_db* tdbb, impure_value* impure, dsc* value,
const dsc* castDesc, const ItemInfo* itemInfo);
const dsc* castDesc, const ItemInfo* itemInfo, const Firebird::string& format = nullptr);
public:
MetaName dsqlAlias;
dsql_fld* dsqlField;
NestConst<ValueExprNode> source;
NestConst<ItemInfo> itemInfo;
Firebird::string format;
dsc castDesc;
bool artificial;
};

View File

@ -267,7 +267,7 @@ public:
print(s, *array);
}
void print(const Firebird::string& s, const TriState& triState)
void print(const Firebird::string& s, const Firebird::TriState& triState)
{
if (triState.isAssigned())
print(s, triState.asBool());

View File

@ -261,13 +261,13 @@ private:
}
}
void setClause(TriState& clause, const char* duplicateMsg, bool value)
void setClause(Firebird::TriState& clause, const char* duplicateMsg, bool value)
{
checkDuplicateClause(clause, duplicateMsg);
clause = value;
}
void setClause(TriState& clause, const char* duplicateMsg, const TriState& value)
void setClause(Firebird::TriState& clause, const char* duplicateMsg, const Firebird::TriState& value)
{
if (value.isAssigned())
{
@ -322,7 +322,7 @@ private:
return clause.hasData();
}
bool isDuplicateClause(const TriState& clause)
bool isDuplicateClause(const Firebird::TriState& clause)
{
return clause.isAssigned();
}

View File

@ -10971,7 +10971,7 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d
auto inputFirst = input->first;
if (!inputFirst)
if (inputFirst->items.isEmpty())
{
// Process RETURNING *
inputFirst = FB_NEW_POOL(pool) ValueListNode(pool, 0u);

View File

@ -1340,7 +1340,7 @@ public:
public:
NestConst<SelectExprNode> selectExpr;
NestConst<RseNode> rse;
TriState optimizeForFirstRows;
Firebird::TriState optimizeForFirstRows;
bool forUpdate = false;
bool withLock = false;
bool skipLocked = false;
@ -1626,12 +1626,12 @@ public:
std::optional<CommitNumber> atSnapshotNumber;
std::optional<unsigned> isoLevel;
std::optional<USHORT> lockTimeout;
TriState readOnly;
TriState wait;
TriState noAutoUndo;
TriState ignoreLimbo;
TriState restartRequests;
TriState autoCommit;
Firebird::TriState readOnly;
Firebird::TriState wait;
Firebird::TriState noAutoUndo;
Firebird::TriState ignoreLimbo;
Firebird::TriState restartRequests;
Firebird::TriState autoCommit;
};
@ -1901,7 +1901,7 @@ public:
virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const;
public:
TriState optimizeMode;
Firebird::TriState optimizeMode;
};

View File

@ -700,6 +700,7 @@ using namespace Firebird;
%token <metaNamePtr> ANY_VALUE
%token <metaNamePtr> CALL
%token <metaNamePtr> FORMAT
%token <metaNamePtr> NAMED_ARG_ASSIGN
// precedence declarations for expression evaluation
@ -735,7 +736,7 @@ using namespace Firebird;
{}
std::optional<int> nullableIntVal;
TriState triState;
Firebird::TriState triState;
std::optional<Jrd::SqlSecurity> nullableSqlSecurityVal;
std::optional<Jrd::OverrideClause> nullableOverrideClause;
struct { bool first; bool second; } boolPair;
@ -4916,6 +4917,7 @@ non_charset_simple_type
| numeric_type
| float_type
| decfloat_type
| date_time_type
| BIGINT
{
$$ = newNode<dsql_fld>();
@ -4959,60 +4961,6 @@ non_charset_simple_type
$$->length = sizeof(SSHORT);
$$->flags |= FLD_has_prec;
}
| DATE
{
$$ = newNode<dsql_fld>();
stmt_ambiguous = true;
if (client_dialect <= SQL_DIALECT_V5)
{
// Post warning saying that DATE is equivalent to TIMESTAMP
ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(301) <<
Arg::Warning(isc_dtype_renamed));
$$->dtype = dtype_timestamp;
$$->length = sizeof(GDS_TIMESTAMP);
}
else if (client_dialect == SQL_DIALECT_V6_TRANSITION)
yyabandon(YYPOSNARG(1), -104, isc_transitional_date);
else
{
$$->dtype = dtype_sql_date;
$$->length = sizeof(ULONG);
}
$$->flags |= FLD_has_prec;
}
| TIME without_time_zone_opt
{
$$ = newNode<dsql_fld>();
checkTimeDialect();
$$->dtype = dtype_sql_time;
$$->length = sizeof(SLONG);
$$->flags |= FLD_has_prec;
}
| TIME WITH TIME ZONE
{
$$ = newNode<dsql_fld>();
checkTimeDialect();
$$->dtype = dtype_sql_time_tz;
$$->length = sizeof(ISC_TIME_TZ);
$$->flags |= FLD_has_prec;
}
| TIMESTAMP without_time_zone_opt
{
$$ = newNode<dsql_fld>();
$$->dtype = dtype_timestamp;
$$->length = sizeof(GDS_TIMESTAMP);
$$->flags |= FLD_has_prec;
}
| TIMESTAMP WITH TIME ZONE
{
$$ = newNode<dsql_fld>();
$$->dtype = dtype_timestamp_tz;
$$->length = sizeof(ISC_TIMESTAMP_TZ);
$$->flags |= FLD_has_prec;
}
| BOOLEAN
{
$$ = newNode<dsql_fld>();
@ -6155,7 +6103,7 @@ query_spec
rse->dsqlFirst = $2 ? $2->items[1] : NULL;
rse->dsqlSkip = $2 ? $2->items[0] : NULL;
rse->dsqlDistinct = $3;
rse->dsqlSelectList = $4;
rse->dsqlSelectList = $4->items.hasData() ? $4 : nullptr;
rse->dsqlFrom = $5;
rse->dsqlWhere = $6;
rse->dsqlGroup = $7;
@ -6190,14 +6138,14 @@ skip_clause
%type <valueListNode> distinct_clause
distinct_clause
: DISTINCT { $$ = newNode<ValueListNode>(0); }
: DISTINCT { $$ = newNode<ValueListNode>(0u); }
| all_noise { $$ = NULL; }
;
%type <valueListNode> select_list
select_list
: select_items { $$ = $1; }
| '*' { $$ = NULL; }
| '*' { $$ = newNode<ValueListNode>(0u); }
;
%type <valueListNode> select_items
@ -8781,6 +8729,77 @@ named_argument
cast_specification
: CAST '(' value AS data_type_descriptor ')'
{ $$ = newNode<CastNode>($3, $5); }
| CAST '(' value AS cast_format_type cast_format_clause utf_string ')'
{ $$ = newNode<CastNode>($3, $5, *$7); }
;
%type <metaNamePtr> cast_format_clause
cast_format_clause
: FORMAT
;
%type <legacyField> date_time_type
date_time_type
: DATE
{
$$ = newNode<dsql_fld>();
stmt_ambiguous = true;
if (client_dialect <= SQL_DIALECT_V5)
{
// Post warning saying that DATE is equivalent to TIMESTAMP
ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(301) <<
Arg::Warning(isc_dtype_renamed));
$$->dtype = dtype_timestamp;
$$->length = sizeof(GDS_TIMESTAMP);
}
else if (client_dialect == SQL_DIALECT_V6_TRANSITION)
yyabandon(YYPOSNARG(1), -104, isc_transitional_date);
else
{
$$->dtype = dtype_sql_date;
$$->length = sizeof(ULONG);
}
$$->flags |= FLD_has_prec;
}
| TIME without_time_zone_opt
{
$$ = newNode<dsql_fld>();
checkTimeDialect();
$$->dtype = dtype_sql_time;
$$->length = sizeof(SLONG);
$$->flags |= FLD_has_prec;
}
| TIME WITH TIME ZONE
{
$$ = newNode<dsql_fld>();
checkTimeDialect();
$$->dtype = dtype_sql_time_tz;
$$->length = sizeof(ISC_TIME_TZ);
$$->flags |= FLD_has_prec;
}
| TIMESTAMP without_time_zone_opt
{
$$ = newNode<dsql_fld>();
$$->dtype = dtype_timestamp;
$$->length = sizeof(GDS_TIMESTAMP);
$$->flags |= FLD_has_prec;
}
| TIMESTAMP WITH TIME ZONE
{
$$ = newNode<dsql_fld>();
$$->dtype = dtype_timestamp_tz;
$$->length = sizeof(ISC_TIMESTAMP_TZ);
$$->flags |= FLD_has_prec;
}
;
%type <legacyField> cast_format_type
cast_format_type
: character_type
| date_time_type
;
// case expressions
@ -9444,6 +9463,7 @@ non_reserved_word
| UNICODE_VAL
// added in FB 6.0
| ANY_VALUE
| FORMAT
;
%%

View File

@ -496,4 +496,6 @@
#define blr_default_arg (unsigned char) 227
#define blr_cast_format (unsigned char) 228
#endif // FIREBIRD_IMPL_BLR_H

View File

@ -972,3 +972,12 @@ FB_IMPL_MSG(JRD, 969, uninitialized_var, -625, "42", "000", "Variable @1 is not
FB_IMPL_MSG(JRD, 970, param_not_exist, -170, "07", "001", "Parameter @1 does not exist")
FB_IMPL_MSG(JRD, 971, param_no_default_not_specified, -170, "07", "001", "Parameter @1 has no default value and was not specified or was specified with DEFAULT")
FB_IMPL_MSG(JRD, 972, param_multiple_assignments, -170, "07", "001", "Parameter @1 has multiple assignments")
FB_IMPL_MSG(JRD, 973, invalid_date_format, -901, "HY", "000", "Cannot recognize \"@1\" part of date format")
FB_IMPL_MSG(JRD, 974, invalid_raw_string_in_date_format, -901, "HY", "000", "Cannot find closing \" for raw text in date format")
FB_IMPL_MSG(JRD, 975, invalid_data_type_for_date_format, -901, "HY", "000", "It is not possible to use this data type for date formatting")
FB_IMPL_MSG(JRD, 976, incompatible_date_format_with_current_date_type, -901, "HY", "000", "Cannot use \"@1\" format with current date type")
FB_IMPL_MSG(JRD, 977, value_for_pattern_is_out_of_range, -901, "HY", "000", "Value for @1 pattern is out of range [@2, @3]")
FB_IMPL_MSG(JRD, 978, month_name_mismatch, -901, "HY", "000", "@1 is not MONTH")
FB_IMPL_MSG(JRD, 979, incorrect_hours_period, -901, "HY", "000", "@1 is incorrect period for 12H, it should be AM or PM")
FB_IMPL_MSG(JRD, 980, data_for_format_is_exhausted, -901, "HY", "000", "All data has been read, but format pattern wants more. Unfilled patterns: \"@1\"")
FB_IMPL_MSG(JRD, 981, trailing_part_of_string, -901, "HY", "000", "There is a trailing part of input string that does not fit into FORMAT: \"@1\"")

View File

@ -5704,6 +5704,15 @@ const
isc_param_not_exist = 335545290;
isc_param_no_default_not_specified = 335545291;
isc_param_multiple_assignments = 335545292;
isc_invalid_date_format = 335545293;
isc_invalid_raw_string_in_date_format = 335545294;
isc_invalid_data_type_for_date_format = 335545295;
isc_incompatible_date_format_with_current_date_type = 335545296;
isc_value_for_pattern_is_out_of_range = 335545297;
isc_month_name_mismatch = 335545298;
isc_incorrect_hours_period = 335545299;
isc_data_for_format_is_exhausted = 335545300;
isc_trailing_part_of_string = 335545301;
isc_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;
isc_gfix_incmp_sw = 335740932;

View File

@ -637,11 +637,15 @@ void Jrd::Attachment::signalShutdown(ISC_STATUS code)
}
void Jrd::Attachment::mergeStats()
void Jrd::Attachment::mergeStats(bool pageStatsOnly)
{
MutexLockGuard guard(att_database->dbb_stats_mutex, FB_FUNCTION);
att_database->dbb_stats.adjust(att_base_stats, att_stats, true);
att_base_stats.assign(att_stats);
att_database->dbb_stats.adjustPageStats(att_base_stats, att_stats);
if (!pageStatsOnly)
{
att_database->dbb_stats.adjust(att_base_stats, att_stats, true);
att_base_stats.assign(att_stats);
}
}

View File

@ -640,7 +640,7 @@ public:
USHORT att_original_timezone;
USHORT att_current_timezone;
int att_parallel_workers;
TriState att_opt_first_rows;
Firebird::TriState att_opt_first_rows;
PageToBufferMap* att_bdb_cache; // managed in CCH, created in att_pool, freed with it
@ -736,7 +736,7 @@ public:
void signalCancel();
void signalShutdown(ISC_STATUS code);
void mergeStats();
void mergeStats(bool pageStatsOnly = false);
bool hasActiveRequests() const;
bool backupStateWriteLock(thread_db* tdbb, SSHORT wait);

View File

@ -532,7 +532,7 @@ public:
time_t dbb_linger_end;
Firebird::RefPtr<Firebird::IPluginConfig> dbb_plugin_config;
TriState dbb_repl_state; // replication state
Firebird::TriState dbb_repl_state; // replication state
Lock* dbb_repl_lock; // replication state lock
Firebird::SyncObject dbb_repl_sync;
FB_UINT64 dbb_repl_sequence; // replication sequence

View File

@ -747,6 +747,12 @@ ExtEngineManager::ExtRoutine::ExtRoutine(thread_db* tdbb, ExtEngineManager* aExt
engine->addRef();
}
void ExtEngineManager::ExtRoutine::PluginDeleter::operator()(IPluginBase* ptr)
{
if (ptr)
PluginManagerInterfacePtr()->releasePlugin(ptr);
}
//---------------------
@ -770,7 +776,7 @@ ExtEngineManager::Function::~Function()
void ExtEngineManager::Function::execute(thread_db* tdbb, UCHAR* inMsg, UCHAR* outMsg) const
{
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine.get());
const MetaString& userName = udf->invoker ? udf->invoker->getUserName() : "";
ContextManager<IExternalFunction> ctxManager(tdbb, attInfo, function,
(udf->getName().package.isEmpty() ?
@ -821,7 +827,7 @@ ExtEngineManager::ResultSet::ResultSet(thread_db* tdbb, UCHAR* inMsg, UCHAR* out
attachment(tdbb->getAttachment()),
firstFetch(true)
{
attInfo = procedure->extManager->getEngineAttachment(tdbb, procedure->engine);
attInfo = procedure->extManager->getEngineAttachment(tdbb, procedure->engine.get());
const MetaString& userName = procedure->prc->invoker ? procedure->prc->invoker->getUserName() : "";
ContextManager<IExternalProcedure> ctxManager(tdbb, attInfo, procedure->procedure,
(procedure->prc->getName().package.isEmpty() ?
@ -931,7 +937,7 @@ ExtEngineManager::Trigger::~Trigger()
void ExtEngineManager::Trigger::execute(thread_db* tdbb, Request* request, unsigned action,
record_param* oldRpb, record_param* newRpb) const
{
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine);
EngineAttachmentInfo* attInfo = extManager->getEngineAttachment(tdbb, engine.get());
const TriState& ssDefiner = trg->ssDefiner.isAssigned() ? trg->ssDefiner :
(trg->relation && trg->relation->rel_ss_definer.isAssigned() ? trg->relation->rel_ss_definer : TriState());
const MetaString& userName = ssDefiner.asBool() ?

View File

@ -24,6 +24,9 @@
#define JRD_EXT_ENGINE_MANAGER_H
#include "firebird/Interface.h"
#include <memory>
#include "../common/classes/array.h"
#include "../common/classes/fb_string.h"
#include "../common/classes/GenericMap.h"
@ -216,9 +219,16 @@ public:
ExtRoutine(thread_db* tdbb, ExtEngineManager* aExtManager,
Firebird::IExternalEngine* aEngine, RoutineMetadata* aMetadata);
private:
class PluginDeleter
{
public:
void operator()(Firebird::IPluginBase* ptr);
};
protected:
ExtEngineManager* extManager;
Firebird::AutoPlugin<Firebird::IExternalEngine> engine;
std::unique_ptr<Firebird::IExternalEngine, PluginDeleter> engine;
Firebird::AutoPtr<RoutineMetadata> metadata;
Database* database;
};

View File

@ -878,7 +878,7 @@ public:
Firebird::Array<NestConst<RecordSourceNode> > rse_relations;
USHORT flags = 0;
USHORT rse_jointype = blr_inner; // inner, left, full
TriState firstRows; // optimize for first rows
Firebird::TriState firstRows; // optimize for first rows
};
class SelectExprNode final : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_SELECT_EXPR>

View File

@ -264,9 +264,9 @@ public:
TrigVector* rel_post_store; // Post-operation store trigger
prim rel_primary_dpnds; // foreign dependencies on this relation's primary key
frgn rel_foreign_refs; // foreign references to other relations' primary keys
TriState rel_ss_definer;
TriState rel_repl_state; // replication state
Firebird::TriState rel_ss_definer;
Firebird::TriState rel_repl_state; // replication state
Firebird::Mutex rel_drop_mutex;

View File

@ -260,7 +260,21 @@ public:
addRelCounts(baseStats.rel_counts, false);
}
}
}
void adjustPageStats(RuntimeStatistics& baseStats, const RuntimeStatistics& newStats)
{
if (baseStats.allChgNumber != newStats.allChgNumber)
{
allChgNumber++;
for (size_t i = 0; i < REL_BASE_OFFSET; ++i)
{
const SINT64 delta = newStats.values[i] - baseStats.values[i];
values[i] += delta;
baseStats.values[i] += delta;
}
}
}
// copy counters values from other instance

View File

@ -388,8 +388,10 @@ const char
WIRE_CRYPT_PLUGIN_NAME[] = "WIRE_CRYPT_PLUGIN",
CLIENT_ADDRESS_NAME[] = "CLIENT_ADDRESS",
CLIENT_HOST_NAME[] = "CLIENT_HOST",
CLIENT_OS_USER_NAME[] = "CLIENT_OS_USER",
CLIENT_PID_NAME[] = "CLIENT_PID",
CLIENT_PROCESS_NAME[] = "CLIENT_PROCESS",
CLIENT_VERSION_NAME[] = "CLIENT_VERSION",
CURRENT_USER_NAME[] = "CURRENT_USER",
CURRENT_ROLE_NAME[] = "CURRENT_ROLE",
SESSION_IDLE_TIMEOUT[] = "SESSION_IDLE_TIMEOUT",
@ -4641,6 +4643,13 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar
resultStr = attachment->att_remote_host;
}
else if (nameStr == CLIENT_OS_USER_NAME)
{
if (attachment->att_remote_os_user.isEmpty())
return NULL;
resultStr = attachment->att_remote_os_user;
}
else if (nameStr == CLIENT_PID_NAME)
{
if (!attachment->att_remote_pid)
@ -4655,6 +4664,13 @@ dsc* evlGetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar
resultStr = attachment->att_remote_process.ToString();
}
else if (nameStr == CLIENT_VERSION_NAME)
{
if (attachment->att_client_version.isEmpty())
return NULL;
resultStr = attachment->att_client_version;
}
else if (nameStr == CURRENT_USER_NAME)
{
const MetaString& user = attachment->getUserName();

View File

@ -3,16 +3,16 @@
*** DO NOT EDIT ***
TO CHANGE ANY INFORMATION IN HERE PLEASE
EDIT src/misc/writeBuildNum.sh
FORMAL BUILD NUMBER:86
FORMAL BUILD NUMBER:107
*/
#define PRODUCT_VER_STRING "6.0.0.86"
#define FILE_VER_STRING "WI-T6.0.0.86"
#define LICENSE_VER_STRING "WI-T6.0.0.86"
#define FILE_VER_NUMBER 6, 0, 0, 86
#define PRODUCT_VER_STRING "6.0.0.107"
#define FILE_VER_STRING "WI-T6.0.0.107"
#define LICENSE_VER_STRING "WI-T6.0.0.107"
#define FILE_VER_NUMBER 6, 0, 0, 107
#define FB_MAJOR_VER "6"
#define FB_MINOR_VER "0"
#define FB_REV_NO "0"
#define FB_BUILD_NO "86"
#define FB_BUILD_NO "107"
#define FB_BUILD_TYPE "T"
#define FB_BUILD_SUFFIX "Firebird 6.0 Initial"

View File

@ -590,58 +590,19 @@ void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, TriggerActio
// Execute DDL triggers.
void EXE_execute_ddl_triggers(thread_db* tdbb, jrd_tra* transaction, bool preTriggers, int action)
{
Jrd::Attachment* attachment = tdbb->getAttachment();
const auto attachment = tdbb->getAttachment();
// Our caller verifies (ATT_no_db_triggers) if DDL triggers should not run.
// Our caller verifies (ATT_no_db_triggers) if DDL triggers should not run
if (attachment->att_ddl_triggers)
{
TrigVector triggers;
TrigVector* triggersPtr = &triggers;
HalfStaticArray<Trigger*, 4> cachedTriggers;
AutoSetRestore2<jrd_tra*, thread_db> tempTrans(tdbb,
&thread_db::getTransaction,
&thread_db::setTransaction,
transaction);
for (auto& trigger : *attachment->att_ddl_triggers)
{
const auto type = trigger.type & ~TRIGGER_TYPE_MASK;
const bool preTrigger = ((type & 1) == 0);
if ((type & (1LL << action)) && (preTriggers == preTrigger))
{
triggers.add() = trigger;
cachedTriggers.add(&trigger);
}
}
if (triggers.hasData())
{
FbLocalStatus tempStatus;
jrd_tra* const oldTransaction = tdbb->getTransaction();
tdbb->setTransaction(transaction);
try
{
EXE_execute_triggers(tdbb, &triggersPtr, NULL, NULL, TRIGGER_DDL,
preTriggers ? StmtNode::PRE_TRIG : StmtNode::POST_TRIG);
}
catch (const Exception& ex)
{
ex.stuffException(&tempStatus);
}
tdbb->setTransaction(oldTransaction);
// Triggers could be compiled inside EXE_execute_triggers(),
// so ensure the new pointers are copied back to the cache
fb_assert(triggers.getCount() == cachedTriggers.getCount());
for (unsigned i = 0; i < triggers.getCount(); i++)
{
*cachedTriggers[i] = triggers[i];
triggers[i].extTrigger = nullptr; // avoid deletion inside d'tor
}
tempStatus.check();
}
EXE_execute_triggers(tdbb, &attachment->att_ddl_triggers, NULL, NULL, TRIGGER_DDL,
preTriggers ? StmtNode::PRE_TRIG : StmtNode::POST_TRIG, action);
}
}
@ -1166,7 +1127,8 @@ void EXE_execute_triggers(thread_db* tdbb,
record_param* old_rpb,
record_param* new_rpb,
TriggerAction trigger_action,
StmtNode::WhichTrigger which_trig)
StmtNode::WhichTrigger which_trig,
int ddl_action)
{
/**************************************
*
@ -1219,6 +1181,20 @@ void EXE_execute_triggers(thread_db* tdbb,
{
for (TrigVector::iterator ptr = vector->begin(); ptr != vector->end(); ++ptr)
{
if (trigger_action == TRIGGER_DDL && ddl_action)
{
// Skip triggers not matching our action
fb_assert(which_trig == StmtNode::PRE_TRIG || which_trig == StmtNode::POST_TRIG);
const bool preTriggers = (which_trig == StmtNode::PRE_TRIG);
const auto type = ptr->type & ~TRIGGER_TYPE_MASK;
const bool preTrigger = ((type & 1) == 0);
if (!(type & (1LL << ddl_action)) || preTriggers != preTrigger)
continue;
}
ptr->compile(tdbb);
trigger = ptr->statement->findRequest(tdbb);

View File

@ -46,7 +46,7 @@ const Jrd::StmtNode* EXE_looper(Jrd::thread_db* tdbb, Jrd::Request* request,
const Jrd::StmtNode* in_node);
void EXE_execute_triggers(Jrd::thread_db*, Jrd::TrigVector**, Jrd::record_param*, Jrd::record_param*,
enum TriggerAction, Jrd::StmtNode::WhichTrigger);
enum TriggerAction, Jrd::StmtNode::WhichTrigger, int = 0);
void EXE_receive(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, void*, bool = false);
void EXE_release(Jrd::thread_db*, Jrd::Request*);

View File

@ -113,6 +113,12 @@ void IscConnection::attach(thread_db* tdbb)
validatePassword(tdbb, m_dbName, newDpb);
newDpb.insertInt(isc_dpb_ext_call_depth, attachment->att_ext_call_depth + 1);
if (newDpb.getBufferLength() > MAX_USHORT)
{
ERR_post(Arg::Gds(isc_imp_exc) <<
Arg::Gds(isc_random) << Arg::Str("DPB size greater than 64KB"));
}
FbLocalStatus status;
{
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);

View File

@ -292,7 +292,8 @@ enum UdfError
static SSHORT blob_get_segment(blb*, UCHAR*, USHORT, USHORT*);
static void blob_put_segment(blb*, const UCHAR*, USHORT);
static SLONG blob_lseek(blb*, USHORT, SLONG);
static SLONG get_scalar_array(const Parameter*, DSC*, scalar_array_desc*, UCharStack&);
static ULONG get_scalar_array(thread_db* tdbb, const Parameter*, DSC*,
scalar_array_desc*, UCharStack&);
static void invoke(thread_db* tdbb,
const Function* function,
const Parameter* return_ptr,
@ -436,8 +437,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra
temp_desc = parameter->prm_desc;
temp_desc.dsc_address = temp_ptr;
// CVC: There's a theoretical possibility of overflowing "length" here.
USHORT length = FB_ALIGN(temp_desc.dsc_length, FB_DOUBLE_ALIGN);
ULONG length = FB_ALIGN(temp_desc.dsc_length, FB_DOUBLE_ALIGN);
// If we've got a null argument, just pass zeros (got any better ideas?)
@ -446,7 +446,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra
if (parameter->prm_fun_mechanism == FUN_value)
{
UCHAR* p = (UCHAR *) arg_ptr;
MOVE_CLEAR(p, (SLONG) length);
MOVE_CLEAR(p, length);
p += length;
arg_ptr = reinterpret_cast<UDF_ARG*>(p);
continue;
@ -458,7 +458,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra
}
if (parameter->prm_fun_mechanism != FUN_ref_with_null)
MOVE_CLEAR(temp_ptr, (SLONG) length);
MOVE_CLEAR(temp_ptr, length);
else
{
// Probably for arrays and blobs it's better to preserve the
@ -468,7 +468,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra
case dtype_quad:
case dtype_array:
case dtype_blob:
MOVE_CLEAR(temp_ptr, (SLONG) length);
MOVE_CLEAR(temp_ptr, length);
break;
default: // FUN_ref_with_null, non-blob, non-array: we send null pointer.
*arg_ptr++ = 0;
@ -478,7 +478,8 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra
}
else if (parameter->prm_fun_mechanism == FUN_scalar_array)
{
length = get_scalar_array(parameter, input, (scalar_array_desc*)temp_ptr, array_stack);
length = get_scalar_array(tdbb, parameter, input, (scalar_array_desc*) temp_ptr,
array_stack);
}
else
{
@ -975,10 +976,11 @@ static SSHORT blob_get_segment(blb* blob, UCHAR* buffer, USHORT length, USHORT*
}
static SLONG get_scalar_array(const Parameter* arg,
DSC* value,
scalar_array_desc* scalar_desc,
UCharStack& stack)
static ULONG get_scalar_array(thread_db* tdbb,
const Parameter* arg,
DSC* value,
scalar_array_desc* scalar_desc,
UCharStack& stack)
{
/**************************************
*
@ -992,17 +994,16 @@ static SLONG get_scalar_array(const Parameter* arg,
* Return length of array desc.
*
**************************************/
thread_db* tdbb = JRD_get_thread_data();
MemoryPool& pool = *tdbb->getDefaultPool();
// Get first the array descriptor, then the array
SLONG stuff[IAD_LEN(16) / 4];
Ods::InternalArrayDesc* array_desc = (Ods::InternalArrayDesc*) stuff;
blb* blob = blb::get_array(tdbb, tdbb->getRequest()->req_transaction, (bid*)value->dsc_address,
array_desc);
blb* blob = blb::get_array(tdbb, tdbb->getRequest()->req_transaction,
(bid*) value->dsc_address, array_desc);
fb_assert(array_desc->iad_total_length >= 0); // check before upcasting to size_t
UCHAR* data = FB_NEW_POOL(*getDefaultMemoryPool()) UCHAR[static_cast<size_t>(array_desc->iad_total_length)];
AutoPtr<UCHAR, ArrayDelete> data(FB_NEW_POOL(pool) UCHAR[array_desc->iad_total_length]);
blob->BLB_get_data(tdbb, data, array_desc->iad_total_length);
const USHORT dimensions = array_desc->iad_dimensions;
@ -1012,39 +1013,29 @@ static SLONG get_scalar_array(const Parameter* arg,
dsc from = array_desc->iad_rpt[0].iad_desc;
if (to.dsc_dtype != from.dsc_dtype ||
to.dsc_scale != from.dsc_scale || to.dsc_length != from.dsc_length)
to.dsc_scale != from.dsc_scale ||
to.dsc_length != from.dsc_length)
{
SLONG n = array_desc->iad_count;
UCHAR* const temp = FB_NEW_POOL(*getDefaultMemoryPool()) UCHAR[static_cast<size_t>(to.dsc_length * n)];
ULONG n = array_desc->iad_count;
AutoPtr<UCHAR, ArrayDelete> temp(FB_NEW_POOL(pool) UCHAR[to.dsc_length * n]);
to.dsc_address = temp;
from.dsc_address = data;
// This loop may call ERR_post indirectly.
try
for (; n; --n, to.dsc_address += to.dsc_length,
from.dsc_address += array_desc->iad_element_length)
{
for (; n; --n, to.dsc_address += to.dsc_length,
from.dsc_address += array_desc->iad_element_length)
{
MOV_move(tdbb, &from, &to);
}
}
catch (const Exception&)
{
delete[] data;
delete[] temp;
throw;
MOV_move(tdbb, &from, &to);
}
delete[] data;
data = temp;
data = temp.release();
}
// Fill out the scalar array descriptor
stack.push(data);
scalar_desc->sad_desc = arg->prm_desc;
scalar_desc->sad_desc.dsc_address = data;
scalar_desc->sad_dimensions = dimensions;
stack.push(data.release());
const Ods::InternalArrayDesc::iad_repeat* tail1 = array_desc->iad_rpt;
scalar_array_desc::sad_repeat* tail2 = scalar_desc->sad_rpt;
@ -1055,7 +1046,8 @@ static SLONG get_scalar_array(const Parameter* arg,
tail2->sad_lower = tail1->iad_lower;
}
return static_cast<SLONG>(sizeof(scalar_array_desc) + (dimensions - 1u) * sizeof(scalar_array_desc::sad_repeat));
return static_cast<ULONG>(sizeof(scalar_array_desc) +
(dimensions - 1u) * sizeof(scalar_array_desc::sad_repeat));
}

View File

@ -830,6 +830,9 @@ AttachmentHolder::~AttachmentHolder()
{
Jrd::Attachment* attachment = sAtt->getHandle();
if (attachment)
attachment->mergeStats(true);
if (attachment && !async)
{
attachment->att_use_count--;

View File

@ -148,7 +148,7 @@ public:
Firebird::string entryPoint; // External trigger entrypoint
Firebird::string extBody; // External trigger body
ExtEngineManager::Trigger* extTrigger; // External trigger
TriState ssDefiner;
Firebird::TriState ssDefiner;
MetaName owner; // Owner for SQL SECURITY
bool isActive() const;
@ -498,6 +498,7 @@ const ULONG TDBB_reset_stack = 2048; // stack should be reset after stack ove
const ULONG TDBB_dfw_cleanup = 4096; // DFW cleanup phase is active
const ULONG TDBB_repl_in_progress = 8192; // Prevent recursion in replication
const ULONG TDBB_replicator = 16384; // Replicator
const ULONG TDBB_async = 32768; // Async context (set in AST)
class thread_db : public Firebird::ThreadData
{
@ -624,7 +625,11 @@ public:
reqStat->bumpValue(index, delta);
traStat->bumpValue(index, delta);
attStat->bumpValue(index, delta);
dbbStat->bumpValue(index, delta);
if ((tdbb_flags & TDBB_async) && !attachment)
dbbStat->bumpValue(index, delta);
// else dbbStat is adjusted from attStat, see Attachment::mergeAsyncStats()
}
void bumpRelStats(const RuntimeStatistics::StatType index, SLONG relation_id, SINT64 delta = 1)
@ -1109,6 +1114,8 @@ namespace Jrd {
fb_assert((operator thread_db*())->getAttachment());
}
(*this)->tdbb_flags |= TDBB_async;
}
private:

View File

@ -148,6 +148,6 @@ void MET_store_dependencies(Jrd::thread_db*, Firebird::Array<Jrd::CompilerScratc
const Jrd::jrd_rel*, const Jrd::MetaName&, int, Jrd::jrd_tra*);
int MET_get_linger(Jrd::thread_db*);
TriState MET_get_ss_definer(Jrd::thread_db*);
Firebird::TriState MET_get_ss_definer(Jrd::thread_db*);
#endif // JRD_MET_PROTO_H

View File

@ -189,21 +189,19 @@ void InnerJoin::estimateCost(unsigned position,
joinedStreams[position].selectivity = candidate->selectivity;
// Get the stream cardinality
const auto tail = &csb->csb_rpt[stream->number];
const auto streamCardinality = tail->csb_cardinality;
const auto streamCardinality = csb->csb_rpt[stream->number].csb_cardinality;
auto currentCardinality = streamCardinality * candidate->selectivity;
auto currentCardinality = candidate->unique ?
MINIMUM_CARDINALITY : streamCardinality * candidate->selectivity;
auto currentCost = candidate->cost;
// Unless an external sort is to be applied, adjust estimated cost and cardinality
// accordingly to the "first-rows" retrieval (if specified)
// Given the "first-rows" mode specified (or implied)
// and unless an external sort is to be applied afterwards,
// fake the expected cardinality to look as low as possible
// to estimate the cost just for a single row being produced
if ((!sort || candidate->navigated) && optimizer->favorFirstRows())
{
currentCost -= DEFAULT_INDEX_COST;
currentCost /= MAX(currentCardinality, MINIMUM_CARDINALITY);
currentCost += DEFAULT_INDEX_COST;
currentCardinality = MINIMUM_CARDINALITY;
}
// Calculate the nested loop cost, it's our default option
const auto loopCost = currentCost * cardinality;
@ -687,7 +685,7 @@ InnerJoin::StreamInfo* InnerJoin::getStreamInfo(StreamType stream)
// Dump finally selected stream order
void InnerJoin::printBestOrder() const
{
if (bestStreams.isEmpty())
if (bestStreams.getCount() < 2)
return;
optimizer->printf(" best order, streams:");
@ -742,7 +740,7 @@ void InnerJoin::printFoundOrder(StreamType position,
// Dump finally selected stream order
void InnerJoin::printStartOrder() const
{
optimizer->printf("Start join order, streams:");
bool found = false;
const auto end = innerStreams.end();
for (auto iter = innerStreams.begin(); iter != end; iter++)
@ -750,6 +748,12 @@ void InnerJoin::printStartOrder() const
const auto innerStream = *iter;
if (!innerStream->used)
{
if (!found)
{
optimizer->printf("Start join order, streams:");
found = true;
}
const auto name = optimizer->getStreamName(innerStream->number);
optimizer->printf(" %u (%s) base cost (%1.2f)",
innerStream->number, name.c_str(), innerStream->baseCost);

View File

@ -356,6 +356,16 @@ public:
static double getSelectivity(const BoolExprNode* node)
{
if (const auto listNode = nodeAs<InListBoolNode>(node))
{
const auto selectivity = REDUCE_SELECTIVITY_FACTOR_EQUALITY *
listNode->list->items.getCount();
return MIN(selectivity, MAXIMUM_SELECTIVITY);
}
if (nodeIs<MissingBoolNode>(node))
return REDUCE_SELECTIVITY_FACTOR_EQUALITY;
if (const auto cmpNode = nodeAs<ComparativeBoolNode>(node))
{
switch (cmpNode->blrOp)
@ -382,16 +392,6 @@ public:
break;
}
}
else if (const auto listNode = nodeAs<InListBoolNode>(node))
{
const auto selectivity = REDUCE_SELECTIVITY_FACTOR_EQUALITY *
listNode->list->items.getCount();
return MIN(selectivity, MAXIMUM_SELECTIVITY);
}
else if (nodeIs<MissingBoolNode>(node))
{
return REDUCE_SELECTIVITY_FACTOR_EQUALITY;
}
return REDUCE_SELECTIVITY_FACTOR_OTHER;
}

View File

@ -104,6 +104,34 @@ namespace
return newValue;
}
bool matchSubset(const BoolExprNode* boolean, const BoolExprNode* sub)
{
if (boolean->sameAs(sub, true))
return true;
auto binaryNode = nodeAs<BinaryBoolNode>(boolean);
if (binaryNode && binaryNode->blrOp == blr_or)
{
if (matchSubset(binaryNode->arg1, sub) ||
matchSubset(binaryNode->arg2, sub))
{
return true;
}
binaryNode = nodeAs<BinaryBoolNode>(sub);
if (binaryNode && binaryNode->blrOp == blr_or)
{
if (matchSubset(boolean, binaryNode->arg1) &&
matchSubset(boolean, binaryNode->arg2))
{
return true;
}
}
}
return false;
};
} // namespace
@ -723,24 +751,22 @@ bool Retrieval::checkIndexCondition(index_desc& idx, MatchedBooleanList& matches
const auto boolean = idxIter.object();
// If the index condition is (A OR B) and any of the {A, B} is present
// among the available booleans, then the index is possibly usable
const auto binaryNode = nodeAs<BinaryBoolNode>(boolean);
if (binaryNode && binaryNode->blrOp == blr_or)
// among the available booleans, then the index is possibly usable.
// Note: this check also includes the exact match.
for (iter.rewind(); iter.hasData(); ++iter)
{
for (iter.rewind(); iter.hasData(); ++iter)
if (matchSubset(boolean, *iter))
{
if (binaryNode->arg1->sameAs(*iter, true) ||
binaryNode->arg2->sameAs(*iter, true))
{
matches.add(*iter);
break;
}
matches.add(*iter);
break;
}
}
// If the index condition is (A IS NOT NULL) and the available booleans
// includes any comparative predicate that explicitly mentions A,
// then the index is possibly usable
const auto notNode = nodeAs<NotBoolNode>(boolean);
const auto missingNode = notNode ? nodeAs<MissingBoolNode>(notNode->arg) : nullptr;
if (missingNode)
@ -767,17 +793,6 @@ bool Retrieval::checkIndexCondition(index_desc& idx, MatchedBooleanList& matches
}
}
// If conjunct of the index condition matches any available boolean,
// then the index is possibly usable
for (iter.rewind(); iter.hasData(); ++iter)
{
if (idxIter.object()->sameAs(*iter, true))
{
matches.add(*iter);
break;
}
}
idx.idx_fraction *= optimizer->getSelectivity(boolean);
}
@ -833,7 +848,7 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
if (scratch.candidate)
{
matches.assign(scratch.matches);
scratch.selectivity = idx->idx_fraction;
scratch.selectivity = MAXIMUM_SELECTIVITY;
bool unique = false;
unsigned listCount = 0;
@ -1027,7 +1042,7 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool());
invCandidate->unique = unique;
invCandidate->selectivity = selectivity;
invCandidate->selectivity = idx->idx_fraction * selectivity;
invCandidate->cost = cost;
invCandidate->nonFullMatchedSegments = scratch.nonFullMatchedSegments;
invCandidate->matchedSegments = MAX(scratch.lowerCount, scratch.upperCount);
@ -1054,6 +1069,13 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
invCandidate->scratch = &scratch;
invCandidate->matches.assign(scratch.matches);
for (auto match : invCandidate->matches)
{
match->findDependentFromStreams(csb, stream,
&invCandidate->dependentFromStreams);
}
invCandidate->dependencies = invCandidate->dependentFromStreams.getCount();
inversions.add(invCandidate);
}
}
@ -1377,6 +1399,9 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
}
}
if (currentInv->boolean && matches.exist(currentInv->boolean))
anyMatchAlreadyUsed = true;
if (anyMatchAlreadyUsed && !customPlan)
{
currentInv->used = true;

View File

@ -31,7 +31,6 @@
#include "../include/fb_blk.h"
#include "../common/classes/array.h"
#include "../common/classes/TriState.h"
#include "../jrd/intl_classes.h"
#include "../jrd/MetaName.h"
#include "../jrd/QualifiedName.h"

View File

@ -9,7 +9,7 @@ BuildType=T
MajorVer=6
MinorVer=0
RevNo=0
BuildNum=86
BuildNum=107
NowAt=`pwd`
cd `dirname $0`

View File

@ -1652,7 +1652,7 @@ ISC_STATUS API_ROUTINE isc_attach_database(ISC_STATUS* userStatus, SSHORT fileLe
return status[1];
YAttachment* attachment = dispatcher->attachDatabase(&statusWrapper, pathName.c_str(),
dpbLength, reinterpret_cast<const UCHAR*>(dpb));
static_cast<USHORT>(dpbLength), reinterpret_cast<const UCHAR*>(dpb));
if (status.getState() & IStatus::STATE_ERRORS)
return status[1];