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:
commit
93c1b1f815
95
doc/README.cast.format.md
Normal file
95
doc/README.cast.format.md
Normal 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
|
||||
```
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
1107
src/common/cvt.cpp
1107
src/common/cvt.cpp
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
321
src/common/tests/CvtTest.cpp
Normal file
321
src/common/tests/CvtTest.cpp
Normal 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
|
126
src/common/tests/CvtTestUtils.h
Normal file
126
src/common/tests/CvtTestUtils.h
Normal 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(×, 0, sizeof(struct tm));
|
||||
|
||||
times.tm_year = year - 1900;
|
||||
times.tm_mon = month - 1;
|
||||
times.tm_mday = day;
|
||||
mktime(×);
|
||||
|
||||
return times;
|
||||
}
|
||||
|
||||
static ISC_DATE createDate(int year, int month, int day)
|
||||
{
|
||||
struct tm times = initTMStruct(year, month, day);
|
||||
return NoThrowTimeStamp::encode_date(×);
|
||||
}
|
||||
|
||||
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(×, 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
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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,6 +3784,57 @@ dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
|
||||
if (!value)
|
||||
return nullptr;
|
||||
|
||||
|
||||
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)
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
136
src/dsql/parse.y
136
src/dsql/parse.y
@ -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
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -496,4 +496,6 @@
|
||||
|
||||
#define blr_default_arg (unsigned char) 227
|
||||
|
||||
#define blr_cast_format (unsigned char) 228
|
||||
|
||||
#endif // FIREBIRD_IMPL_BLR_H
|
||||
|
@ -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\"")
|
||||
|
@ -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;
|
||||
|
@ -637,12 +637,16 @@ 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.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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Attachment::hasActiveRequests() const
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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() ?
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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*);
|
||||
|
@ -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);
|
||||
|
@ -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,7 +976,8 @@ static SSHORT blob_get_segment(blb* blob, UCHAR* buffer, USHORT length, USHORT*
|
||||
}
|
||||
|
||||
|
||||
static SLONG get_scalar_array(const Parameter* arg,
|
||||
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)
|
||||
{
|
||||
MOV_move(tdbb, &from, &to);
|
||||
}
|
||||
}
|
||||
catch (const Exception&)
|
||||
{
|
||||
delete[] data;
|
||||
delete[] temp;
|
||||
throw;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -830,6 +830,9 @@ AttachmentHolder::~AttachmentHolder()
|
||||
{
|
||||
Jrd::Attachment* attachment = sAtt->getHandle();
|
||||
|
||||
if (attachment)
|
||||
attachment->mergeStats(true);
|
||||
|
||||
if (attachment && !async)
|
||||
{
|
||||
attachment->att_use_count--;
|
||||
|
@ -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);
|
||||
|
||||
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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
if (binaryNode->arg1->sameAs(*iter, true) ||
|
||||
binaryNode->arg2->sameAs(*iter, true))
|
||||
if (matchSubset(boolean, *iter))
|
||||
{
|
||||
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;
|
||||
|
@ -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"
|
||||
|
@ -9,7 +9,7 @@ BuildType=T
|
||||
MajorVer=6
|
||||
MinorVer=0
|
||||
RevNo=0
|
||||
BuildNum=86
|
||||
BuildNum=107
|
||||
|
||||
NowAt=`pwd`
|
||||
cd `dirname $0`
|
||||
|
@ -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];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user