mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 16:03: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
|
CLIENT_HOST | The wire protocol host name of remote client. Value is
|
||||||
| returned for all supported protocols.
|
| returned for all supported protocols.
|
||||||
|
|
|
|
||||||
|
CLIENT_OS_USER | Remote OS user name
|
||||||
|
|
|
||||||
CLIENT_PID | Process ID of remote client application
|
CLIENT_PID | Process ID of remote client application
|
||||||
|
|
|
|
||||||
CLIENT_PROCESS | Process name 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
|
DB_NAME | Canonical name of current database. It is either alias
|
||||||
| name if connectivity via file names is not allowed or
|
| name if connectivity via file names is not allowed or
|
||||||
| fully expanded database file name otherwise.
|
| fully expanded database file name otherwise.
|
||||||
|
@ -2517,6 +2517,7 @@ void write_database( const TEXT* dbb_file)
|
|||||||
FbLocalStatus status_vector;
|
FbLocalStatus status_vector;
|
||||||
UCHAR buffer[256];
|
UCHAR buffer[256];
|
||||||
Firebird::IRequest* req_handle1 = nullptr;
|
Firebird::IRequest* req_handle1 = nullptr;
|
||||||
|
Firebird::IRequest* req_handle2 = nullptr;
|
||||||
|
|
||||||
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
|
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
|
||||||
|
|
||||||
@ -2619,6 +2620,23 @@ void write_database( const TEXT* dbb_file)
|
|||||||
ON_ERROR
|
ON_ERROR
|
||||||
general_on_error();
|
general_on_error();
|
||||||
END_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
|
else
|
||||||
{
|
{
|
||||||
@ -2636,6 +2654,7 @@ void write_database( const TEXT* dbb_file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MISC_release_request_silent(req_handle1);
|
MISC_release_request_silent(req_handle1);
|
||||||
|
MISC_release_request_silent(req_handle2);
|
||||||
|
|
||||||
put(tdgbl, att_end);
|
put(tdgbl, att_end);
|
||||||
}
|
}
|
||||||
|
@ -572,8 +572,6 @@ int gbak(Firebird::UtilSvc* uSvc)
|
|||||||
if (switches.exists(IN_SW_BURP_SE, argv.begin(), 1, argc))
|
if (switches.exists(IN_SW_BURP_SE, argv.begin(), 1, argc))
|
||||||
return svc_api_gbak(uSvc, switches);
|
return svc_api_gbak(uSvc, switches);
|
||||||
|
|
||||||
uSvc->started();
|
|
||||||
|
|
||||||
if (argc <= 1)
|
if (argc <= 1)
|
||||||
{
|
{
|
||||||
burp_usage(switches);
|
burp_usage(switches);
|
||||||
@ -1396,13 +1394,12 @@ int gbak(Firebird::UtilSvc* uSvc)
|
|||||||
tdgbl->action->act_action = ACT_unknown;
|
tdgbl->action->act_action = ACT_unknown;
|
||||||
|
|
||||||
action = open_files(file1, &file2, sw_replace, dpb);
|
action = open_files(file1, &file2, sw_replace, dpb);
|
||||||
|
|
||||||
MVOL_init(tdgbl->io_buffer_size);
|
MVOL_init(tdgbl->io_buffer_size);
|
||||||
|
uSvc->started();
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
tdgbl->gbl_dpb_data.add(dpb.getBuffer(), dpb.getBufferLength());
|
tdgbl->gbl_dpb_data.add(dpb.getBuffer(), dpb.getBufferLength());
|
||||||
|
|
||||||
tdgbl->uSvc->started();
|
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case RESTORE:
|
case RESTORE:
|
||||||
|
@ -257,6 +257,8 @@ enum att_type {
|
|||||||
att_database_sql_security_deprecated, // can be removed later
|
att_database_sql_security_deprecated, // can be removed later
|
||||||
att_replica_mode, // replica mode
|
att_replica_mode, // replica mode
|
||||||
att_database_sql_security, // default sql security value
|
att_database_sql_security, // default sql security value
|
||||||
|
att_default_pub_active, // default publication status
|
||||||
|
att_default_pub_auto_enable,
|
||||||
|
|
||||||
// Relation attributes
|
// Relation attributes
|
||||||
|
|
||||||
@ -735,8 +737,7 @@ enum fld_flags_vals {
|
|||||||
FLD_update_missing = 8,
|
FLD_update_missing = 8,
|
||||||
FLD_null_flag = 16,
|
FLD_null_flag = 16,
|
||||||
FLD_charset_flag = 32, // column has global charset
|
FLD_charset_flag = 32, // column has global charset
|
||||||
FLD_collate_flag = 64, // local column has specific collation
|
FLD_collate_flag = 64 // local column has specific collation
|
||||||
FLD_system_domain = 128 // field uses a system domain (on restore)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// relation definition - holds useful relation type stuff
|
// relation definition - holds useful relation type stuff
|
||||||
@ -961,6 +962,7 @@ public:
|
|||||||
GblPool(us->isService()),
|
GblPool(us->isService()),
|
||||||
gbl_sw_par_workers(1),
|
gbl_sw_par_workers(1),
|
||||||
defaultCollations(getPool()),
|
defaultCollations(getPool()),
|
||||||
|
systemFields(getPool()),
|
||||||
gbl_dpb_data(*getDefaultMemoryPool()),
|
gbl_dpb_data(*getDefaultMemoryPool()),
|
||||||
uSvc(us),
|
uSvc(us),
|
||||||
master(true),
|
master(true),
|
||||||
@ -1065,6 +1067,8 @@ public:
|
|||||||
UCHAR* gbl_crypt_buffer;
|
UCHAR* gbl_crypt_buffer;
|
||||||
ULONG gbl_crypt_left;
|
ULONG gbl_crypt_left;
|
||||||
UCHAR* gbl_decompress;
|
UCHAR* gbl_decompress;
|
||||||
|
bool gbl_default_pub_active = false;
|
||||||
|
bool gbl_default_pub_auto_enable = false;
|
||||||
|
|
||||||
burp_rel* relations;
|
burp_rel* relations;
|
||||||
burp_pkg* packages;
|
burp_pkg* packages;
|
||||||
@ -1196,6 +1200,7 @@ public:
|
|||||||
|
|
||||||
Firebird::Array<Firebird::Pair<Firebird::NonPooled<Firebird::MetaString, Firebird::MetaString> > >
|
Firebird::Array<Firebird::Pair<Firebird::NonPooled<Firebird::MetaString, Firebird::MetaString> > >
|
||||||
defaultCollations;
|
defaultCollations;
|
||||||
|
Firebird::SortedArray<Firebird::MetaString> systemFields;
|
||||||
Firebird::Array<UCHAR> gbl_dpb_data;
|
Firebird::Array<UCHAR> gbl_dpb_data;
|
||||||
Firebird::UtilSvc* uSvc;
|
Firebird::UtilSvc* uSvc;
|
||||||
bool master; // set for master thread only
|
bool master; // set for master thread only
|
||||||
|
@ -198,7 +198,7 @@ static inline UCHAR get(BurpGlobals* tdgbl)
|
|||||||
return tdgbl->get();
|
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)
|
if (!deprecated)
|
||||||
{
|
{
|
||||||
@ -531,6 +531,40 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name)
|
|||||||
MISC_release_request_silent(req_handle1);
|
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
|
// Add missing privileges
|
||||||
fix_missing_privileges(tdgbl);
|
fix_missing_privileges(tdgbl);
|
||||||
|
|
||||||
@ -3810,7 +3844,6 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation)
|
|||||||
case att_field_system_flag:
|
case att_field_system_flag:
|
||||||
X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
|
X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
|
||||||
X.RDB$SYSTEM_FLAG.NULL = FALSE;
|
X.RDB$SYSTEM_FLAG.NULL = FALSE;
|
||||||
field->fld_flags |= FLD_system_domain;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case att_view_context:
|
case att_view_context:
|
||||||
@ -4015,7 +4048,6 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation)
|
|||||||
case att_field_system_flag:
|
case att_field_system_flag:
|
||||||
X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
|
X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
|
||||||
X.RDB$SYSTEM_FLAG.NULL = FALSE;
|
X.RDB$SYSTEM_FLAG.NULL = FALSE;
|
||||||
field->fld_flags |= FLD_system_domain;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case att_view_context:
|
case att_view_context:
|
||||||
@ -7472,8 +7504,6 @@ bool get_publication(BurpGlobals* tdgbl)
|
|||||||
*
|
*
|
||||||
**************************************/
|
**************************************/
|
||||||
att_type attribute;
|
att_type attribute;
|
||||||
TEXT temp[GDS_NAME_LEN];
|
|
||||||
SSHORT len;
|
|
||||||
scan_attr_t scan_next_attr;
|
scan_attr_t scan_next_attr;
|
||||||
|
|
||||||
if (tdgbl->runtimeODS >= DB_VERSION_DDL13)
|
if (tdgbl->runtimeODS >= DB_VERSION_DDL13)
|
||||||
@ -7571,8 +7601,6 @@ bool get_pub_table(BurpGlobals* tdgbl)
|
|||||||
*
|
*
|
||||||
**************************************/
|
**************************************/
|
||||||
att_type attribute;
|
att_type attribute;
|
||||||
TEXT temp[GDS_NAME_LEN];
|
|
||||||
SSHORT len;
|
|
||||||
scan_attr_t scan_next_attr;
|
scan_attr_t scan_next_attr;
|
||||||
|
|
||||||
if (tdgbl->runtimeODS >= DB_VERSION_DDL13)
|
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_handle4 = nullptr;
|
||||||
Firebird::IRequest* req_handle5 = 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)
|
while (get_attribute(&attribute, tdgbl) != att_end)
|
||||||
{
|
{
|
||||||
switch (attribute)
|
switch (attribute)
|
||||||
@ -10496,6 +10546,28 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
{
|
{
|
||||||
SSHORT l = get(tdgbl);
|
SSHORT l = get(tdgbl);
|
||||||
@ -11585,6 +11657,12 @@ void WriteRelationMeta::setRelation(BurpGlobals* tdgbl, const burp_rel* relation
|
|||||||
|
|
||||||
if (tdgbl->gbl_network_protocol == 0)
|
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;
|
bool sysDomFlag = false;
|
||||||
burp_fld* field;
|
burp_fld* field;
|
||||||
|
|
||||||
@ -11593,27 +11671,7 @@ void WriteRelationMeta::setRelation(BurpGlobals* tdgbl, const burp_rel* relation
|
|||||||
if (field->fld_flags & FLD_computed)
|
if (field->fld_flags & FLD_computed)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const char* dom = field->fld_source;
|
if (tdgbl->systemFields.exist(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)
|
|
||||||
{
|
{
|
||||||
sysDomFlag = true;
|
sysDomFlag = true;
|
||||||
break;
|
break;
|
||||||
|
@ -222,6 +222,7 @@ PARSER_TOKEN(TOK_FLOOR, "FLOOR", true)
|
|||||||
PARSER_TOKEN(TOK_FOLLOWING, "FOLLOWING", true)
|
PARSER_TOKEN(TOK_FOLLOWING, "FOLLOWING", true)
|
||||||
PARSER_TOKEN(TOK_FOR, "FOR", false)
|
PARSER_TOKEN(TOK_FOR, "FOR", false)
|
||||||
PARSER_TOKEN(TOK_FOREIGN, "FOREIGN", false)
|
PARSER_TOKEN(TOK_FOREIGN, "FOREIGN", false)
|
||||||
|
PARSER_TOKEN(TOK_FORMAT, "FORMAT", true)
|
||||||
PARSER_TOKEN(TOK_FREE_IT, "FREE_IT", true)
|
PARSER_TOKEN(TOK_FREE_IT, "FREE_IT", true)
|
||||||
PARSER_TOKEN(TOK_FROM, "FROM", false)
|
PARSER_TOKEN(TOK_FROM, "FROM", false)
|
||||||
PARSER_TOKEN(TOK_FULL, "FULL", false)
|
PARSER_TOKEN(TOK_FULL, "FULL", false)
|
||||||
|
@ -80,7 +80,6 @@ namespace
|
|||||||
|
|
||||||
static const TimeZoneDesc* getDesc(USHORT timeZone);
|
static const TimeZoneDesc* getDesc(USHORT timeZone);
|
||||||
static inline bool isOffset(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 SSHORT offsetZoneToDisplacement(USHORT timeZone);
|
||||||
static inline USHORT displacementToOffsetZone(SSHORT displacement);
|
static inline USHORT displacementToOffsetZone(SSHORT displacement);
|
||||||
static int parseNumber(const char*& p, const char* end);
|
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);
|
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.
|
// Converts a time from local to UTC.
|
||||||
void TimeZoneUtil::localTimeToUtc(ISC_TIME& time, ISC_USHORT timeZone)
|
void TimeZoneUtil::localTimeToUtc(ISC_TIME& time, ISC_USHORT timeZone)
|
||||||
{
|
{
|
||||||
@ -1156,19 +1169,6 @@ static inline bool isOffset(USHORT timeZone)
|
|||||||
return timeZone <= ONE_DAY * 2;
|
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.
|
// Gets the displacement from a offset-based time zone id.
|
||||||
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone)
|
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_TIMESTAMP_TZ& timeStampTz, SSHORT* offset);
|
||||||
static void extractOffset(const ISC_TIME_TZ& timeTz, 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& time, ISC_USHORT timeZone);
|
||||||
static void localTimeToUtc(ISC_TIME_TZ& timeTz);
|
static void localTimeToUtc(ISC_TIME_TZ& timeTz);
|
||||||
|
|
||||||
|
@ -337,6 +337,80 @@ void NoThrowTimeStamp::round_time(ISC_TIME &ntime, const int precision)
|
|||||||
ntime -= (ntime % period);
|
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
|
// Encode timestamp from UNIX datetime structure
|
||||||
void NoThrowTimeStamp::encode(const struct tm* times, int fractions)
|
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 add10msec(ISC_TIMESTAMP* v, SINT64 msec, SINT64 multiplier);
|
||||||
static void round_time(ISC_TIME& ntime, const int precision);
|
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
|
static inline bool isLeapYear(const int year) noexcept
|
||||||
{
|
{
|
||||||
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#ifndef CLASSES_TRISTATE_H
|
#ifndef CLASSES_TRISTATE_H
|
||||||
#define CLASSES_TRISTATE_H
|
#define CLASSES_TRISTATE_H
|
||||||
|
|
||||||
|
namespace Firebird {
|
||||||
|
|
||||||
class TriState
|
class TriState
|
||||||
{
|
{
|
||||||
@ -129,5 +130,6 @@ inline bool TriState::toggle()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace Firebird
|
||||||
|
|
||||||
#endif // CLASSES_TRISTATE_H
|
#endif // CLASSES_TRISTATE_H
|
||||||
|
@ -136,12 +136,22 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Where* get() const
|
const Where* get() const
|
||||||
{
|
{
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator Where*() const
|
operator const Where*() const
|
||||||
|
{
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Where* get()
|
||||||
|
{
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator Where*()
|
||||||
{
|
{
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
@ -971,7 +971,7 @@ const int HIGH_WORD = 0;
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const TEXT FB_SHORT_MONTHS[][4] =
|
inline const TEXT FB_SHORT_MONTHS[][4] =
|
||||||
{
|
{
|
||||||
"Jan", "Feb", "Mar",
|
"Jan", "Feb", "Mar",
|
||||||
"Apr", "May", "Jun",
|
"Apr", "May", "Jun",
|
||||||
@ -980,7 +980,7 @@ static const TEXT FB_SHORT_MONTHS[][4] =
|
|||||||
"\0"
|
"\0"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const TEXT* const FB_LONG_MONTHS_UPPER[] =
|
inline const TEXT* const FB_LONG_MONTHS_UPPER[] =
|
||||||
{
|
{
|
||||||
"JANUARY",
|
"JANUARY",
|
||||||
"FEBRUARY",
|
"FEBRUARY",
|
||||||
@ -997,6 +997,32 @@ static const TEXT* const FB_LONG_MONTHS_UPPER[] =
|
|||||||
0
|
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
|
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)
|
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,
|
void CVT_string_to_datetime(const dsc*, ISC_TIMESTAMP_TZ*, bool*, const Firebird::EXPECT_DATETIME,
|
||||||
bool, Firebird::Callbacks*);
|
bool, Firebird::Callbacks*);
|
||||||
const UCHAR* CVT_get_bytes(const dsc*, unsigned&);
|
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
|
#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 name;
|
||||||
MetaName relationName;
|
MetaName relationName;
|
||||||
std::optional<FB_UINT64> type;
|
std::optional<FB_UINT64> type;
|
||||||
TriState active;
|
Firebird::TriState active;
|
||||||
std::optional<int> position;
|
std::optional<int> position;
|
||||||
NestConst<ExternalClause> external;
|
NestConst<ExternalClause> external;
|
||||||
Firebird::string source;
|
Firebird::string source;
|
||||||
@ -993,7 +993,7 @@ public:
|
|||||||
NestConst<ValueSourceClause> setDefault;
|
NestConst<ValueSourceClause> setDefault;
|
||||||
MetaName renameTo;
|
MetaName renameTo;
|
||||||
Firebird::AutoPtr<dsql_fld> type;
|
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;
|
MetaName identitySequence;
|
||||||
std::optional<IdentityType> identityType;
|
std::optional<IdentityType> identityType;
|
||||||
std::optional<USHORT> collationId;
|
std::optional<USHORT> collationId;
|
||||||
TriState notNullFlag; // true = NOT NULL / false = NULL
|
Firebird::TriState notNullFlag; // true = NOT NULL / false = NULL
|
||||||
std::optional<USHORT> position;
|
std::optional<USHORT> position;
|
||||||
Firebird::string defaultSource;
|
Firebird::string defaultSource;
|
||||||
Firebird::ByteChunk defaultValue;
|
Firebird::ByteChunk defaultValue;
|
||||||
@ -1556,8 +1556,8 @@ public:
|
|||||||
NestConst<RelationSourceNode> dsqlNode;
|
NestConst<RelationSourceNode> dsqlNode;
|
||||||
MetaName name;
|
MetaName name;
|
||||||
Firebird::Array<NestConst<Clause> > clauses;
|
Firebird::Array<NestConst<Clause> > clauses;
|
||||||
TriState ssDefiner;
|
Firebird::TriState ssDefiner;
|
||||||
TriState replicationState;
|
Firebird::TriState replicationState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1729,9 +1729,9 @@ public:
|
|||||||
|
|
||||||
MetaName relation;
|
MetaName relation;
|
||||||
Firebird::ObjectsArray<MetaName> columns;
|
Firebird::ObjectsArray<MetaName> columns;
|
||||||
TriState unique;
|
Firebird::TriState unique;
|
||||||
TriState descending;
|
Firebird::TriState descending;
|
||||||
TriState inactive;
|
Firebird::TriState inactive;
|
||||||
SSHORT type;
|
SSHORT type;
|
||||||
bid expressionBlr;
|
bid expressionBlr;
|
||||||
bid expressionSource;
|
bid expressionSource;
|
||||||
@ -2222,8 +2222,8 @@ public:
|
|||||||
Firebird::string* lastName;
|
Firebird::string* lastName;
|
||||||
MetaName* plugin;
|
MetaName* plugin;
|
||||||
Firebird::string* comment;
|
Firebird::string* comment;
|
||||||
TriState adminRole;
|
Firebird::TriState adminRole;
|
||||||
TriState active;
|
Firebird::TriState active;
|
||||||
Mode mode;
|
Mode mode;
|
||||||
|
|
||||||
void addProperty(MetaName* pr, Firebird::string* val = NULL)
|
void addProperty(MetaName* pr, Firebird::string* val = NULL)
|
||||||
@ -2464,7 +2464,7 @@ public:
|
|||||||
Firebird::Array<NestConst<DbFileClause> > files;
|
Firebird::Array<NestConst<DbFileClause> > files;
|
||||||
MetaName cryptPlugin;
|
MetaName cryptPlugin;
|
||||||
MetaName keyName;
|
MetaName keyName;
|
||||||
TriState ssDefiner;
|
Firebird::TriState ssDefiner;
|
||||||
Firebird::Array<MetaName> pubTables;
|
Firebird::Array<MetaName> pubTables;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,6 +108,10 @@ namespace
|
|||||||
return castCoalesceNode->sameAs(coalesceNode, ignoreStreams);
|
return castCoalesceNode->sameAs(coalesceNode, ignoreStreams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (const auto castValueIfNode = nodeAs<ValueIfNode>(castNode->source))
|
||||||
|
{
|
||||||
|
return sameNodes(castValueIfNode, coalesceNode, ignoreStreams);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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),
|
: TypedNode<ValueExprNode, ExprNode::TYPE_CAST>(pool),
|
||||||
dsqlAlias("CAST"),
|
dsqlAlias("CAST"),
|
||||||
dsqlField(aDsqlField),
|
dsqlField(aDsqlField),
|
||||||
source(aSource),
|
source(aSource),
|
||||||
itemInfo(NULL),
|
itemInfo(NULL),
|
||||||
|
format(pool, aFormat),
|
||||||
artificial(false)
|
artificial(false)
|
||||||
{
|
{
|
||||||
castDesc.clear();
|
castDesc.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a datatype cast.
|
// 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);
|
CastNode* node = FB_NEW_POOL(pool) CastNode(pool);
|
||||||
|
|
||||||
|
if (blrOp == blr_cast_format)
|
||||||
|
csb->csb_blr_reader.getString(node->format);
|
||||||
|
|
||||||
ItemInfo itemInfo;
|
ItemInfo itemInfo;
|
||||||
PAR_desc(tdbb, csb, &node->castDesc, &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, castDesc);
|
||||||
NODE_PRINT(printer, source);
|
NODE_PRINT(printer, source);
|
||||||
NODE_PRINT(printer, itemInfo);
|
NODE_PRINT(printer, itemInfo);
|
||||||
|
NODE_PRINT(printer, format);
|
||||||
|
|
||||||
return "CastNode";
|
return "CastNode";
|
||||||
}
|
}
|
||||||
@ -3545,6 +3556,7 @@ ValueExprNode* CastNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
|||||||
node->dsqlAlias = dsqlAlias;
|
node->dsqlAlias = dsqlAlias;
|
||||||
node->source = doDsqlPass(dsqlScratch, source);
|
node->source = doDsqlPass(dsqlScratch, source);
|
||||||
node->dsqlField = dsqlField;
|
node->dsqlField = dsqlField;
|
||||||
|
node->format = format;
|
||||||
|
|
||||||
DDL_resolve_intl_type(dsqlScratch, node->dsqlField, NULL);
|
DDL_resolve_intl_type(dsqlScratch, node->dsqlField, NULL);
|
||||||
node->setParameterType(dsqlScratch, NULL, false);
|
node->setParameterType(dsqlScratch, NULL, false);
|
||||||
@ -3589,8 +3601,16 @@ bool CastNode::setParameterType(DsqlCompilerScratch* /*dsqlScratch*/,
|
|||||||
// Generate BLR for a data-type cast operation.
|
// Generate BLR for a data-type cast operation.
|
||||||
void CastNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
void CastNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||||
{
|
{
|
||||||
dsqlScratch->appendUChar(blr_cast);
|
if (format.hasData())
|
||||||
|
{
|
||||||
|
dsqlScratch->appendUChar(blr_cast_format);
|
||||||
|
dsqlScratch->appendString(0, format);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dsqlScratch->appendUChar(blr_cast);
|
||||||
|
|
||||||
dsqlScratch->putDtype(dsqlField, true);
|
dsqlScratch->putDtype(dsqlField, true);
|
||||||
|
|
||||||
GEN_expr(dsqlScratch, source);
|
GEN_expr(dsqlScratch, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3636,6 +3656,7 @@ ValueExprNode* CastNode::copy(thread_db* tdbb, NodeCopier& copier) const
|
|||||||
node->source = copier.copy(tdbb, source);
|
node->source = copier.copy(tdbb, source);
|
||||||
node->castDesc = castDesc;
|
node->castDesc = castDesc;
|
||||||
node->itemInfo = itemInfo;
|
node->itemInfo = itemInfo;
|
||||||
|
node->format = format;
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -3648,7 +3669,7 @@ bool CastNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other
|
|||||||
const CastNode* o = nodeAs<CastNode>(other);
|
const CastNode* o = nodeAs<CastNode>(other);
|
||||||
fb_assert(o);
|
fb_assert(o);
|
||||||
|
|
||||||
return dsqlField == o->dsqlField;
|
return dsqlField == o->dsqlField && format == o->format;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastNode::sameAs(const ExprNode* other, bool ignoreStreams) const
|
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);
|
const CastNode* const otherNode = nodeAs<CastNode>(other);
|
||||||
fb_assert(otherNode);
|
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)
|
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);
|
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.
|
// Cast from one datatype to another.
|
||||||
dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
|
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
|
// If validation is not required and the source value is either NULL
|
||||||
// or already in the desired data type, simply return it "as is"
|
// or already in the desired data type, simply return it "as is"
|
||||||
@ -3763,7 +3784,58 @@ dsc* CastNode::perform(thread_db* tdbb, impure_value* impure, dsc* value,
|
|||||||
if (!value)
|
if (!value)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
MOV_move(tdbb, value, &impure->vlu_desc);
|
|
||||||
|
if (format.hasData())
|
||||||
|
{
|
||||||
|
if (DTYPE_IS_TEXT(impure->vlu_desc.dsc_dtype))
|
||||||
|
{
|
||||||
|
string result = CVT_datetime_to_format_string(value, format, &EngineCallbacks::instance);
|
||||||
|
USHORT dscLength = DSC_string_length(&impure->vlu_desc);
|
||||||
|
string::size_type resultLength = result.length();
|
||||||
|
if (resultLength > dscLength)
|
||||||
|
{
|
||||||
|
ERR_post(Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) <<
|
||||||
|
Arg::Gds(isc_trunc_limits) << Arg::Num(dscLength) << Arg::Num(resultLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
USHORT dscOffset = 0;
|
||||||
|
if (impure->vlu_desc.dsc_dtype == dtype_cstring)
|
||||||
|
dscOffset = 1;
|
||||||
|
else if (impure->vlu_desc.dsc_dtype == dtype_varying)
|
||||||
|
{
|
||||||
|
dscOffset = sizeof(USHORT);
|
||||||
|
((vary*) impure->vlu_desc.dsc_address)->vary_length = resultLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(impure->vlu_desc.dsc_address + dscOffset, result.c_str(), resultLength);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ISC_TIMESTAMP_TZ timestampTZ = CVT_string_to_format_datetime(value, format, &EngineCallbacks::instance);
|
||||||
|
switch (impure->vlu_desc.dsc_dtype)
|
||||||
|
{
|
||||||
|
case dtype_sql_time:
|
||||||
|
*(ISC_TIME*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp.timestamp_time;
|
||||||
|
break;
|
||||||
|
case dtype_sql_date:
|
||||||
|
*(ISC_DATE*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp.timestamp_date;
|
||||||
|
break;
|
||||||
|
case dtype_timestamp:
|
||||||
|
*(ISC_TIMESTAMP*) impure->vlu_desc.dsc_address = timestampTZ.utc_timestamp;
|
||||||
|
break;
|
||||||
|
case dtype_sql_time_tz:
|
||||||
|
case dtype_ex_time_tz:
|
||||||
|
*(ISC_TIME_TZ*) impure->vlu_desc.dsc_address = TimeZoneUtil::timeStampTzToTimeTz(timestampTZ);
|
||||||
|
break;
|
||||||
|
case dtype_timestamp_tz:
|
||||||
|
case dtype_ex_timestamp_tz:
|
||||||
|
*(ISC_TIMESTAMP_TZ*) impure->vlu_desc.dsc_address = timestampTZ;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MOV_move(tdbb, value, &impure->vlu_desc);
|
||||||
|
|
||||||
if (impure->vlu_desc.dsc_dtype == dtype_text)
|
if (impure->vlu_desc.dsc_dtype == dtype_text)
|
||||||
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
INTL_adjust_text_descriptor(tdbb, &impure->vlu_desc);
|
||||||
@ -5777,55 +5849,7 @@ dsc* ExtractNode::execute(thread_db* tdbb, Request* request) const
|
|||||||
|
|
||||||
case blr_extract_week:
|
case blr_extract_week:
|
||||||
{
|
{
|
||||||
// Algorithm for Converting Gregorian Dates to ISO 8601 Week Date by Rick McCarty, 1999
|
part = NoThrowTimeStamp::convertGregorianDateToWeekDate(times);
|
||||||
// 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;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +242,8 @@ public:
|
|||||||
class CastNode final : public TypedNode<ValueExprNode, ExprNode::TYPE_CAST>
|
class CastNode final : public TypedNode<ValueExprNode, ExprNode::TYPE_CAST>
|
||||||
{
|
{
|
||||||
public:
|
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);
|
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;
|
virtual dsc* execute(thread_db* tdbb, Request* request) const;
|
||||||
|
|
||||||
static dsc* perform(thread_db* tdbb, impure_value* impure, dsc* value,
|
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:
|
public:
|
||||||
MetaName dsqlAlias;
|
MetaName dsqlAlias;
|
||||||
dsql_fld* dsqlField;
|
dsql_fld* dsqlField;
|
||||||
NestConst<ValueExprNode> source;
|
NestConst<ValueExprNode> source;
|
||||||
NestConst<ItemInfo> itemInfo;
|
NestConst<ItemInfo> itemInfo;
|
||||||
|
Firebird::string format;
|
||||||
dsc castDesc;
|
dsc castDesc;
|
||||||
bool artificial;
|
bool artificial;
|
||||||
};
|
};
|
||||||
|
@ -267,7 +267,7 @@ public:
|
|||||||
print(s, *array);
|
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())
|
if (triState.isAssigned())
|
||||||
print(s, triState.asBool());
|
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);
|
checkDuplicateClause(clause, duplicateMsg);
|
||||||
clause = value;
|
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())
|
if (value.isAssigned())
|
||||||
{
|
{
|
||||||
@ -322,7 +322,7 @@ private:
|
|||||||
return clause.hasData();
|
return clause.hasData();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDuplicateClause(const TriState& clause)
|
bool isDuplicateClause(const Firebird::TriState& clause)
|
||||||
{
|
{
|
||||||
return clause.isAssigned();
|
return clause.isAssigned();
|
||||||
}
|
}
|
||||||
|
@ -10971,7 +10971,7 @@ static ReturningClause* dsqlProcessReturning(DsqlCompilerScratch* dsqlScratch, d
|
|||||||
|
|
||||||
auto inputFirst = input->first;
|
auto inputFirst = input->first;
|
||||||
|
|
||||||
if (!inputFirst)
|
if (inputFirst->items.isEmpty())
|
||||||
{
|
{
|
||||||
// Process RETURNING *
|
// Process RETURNING *
|
||||||
inputFirst = FB_NEW_POOL(pool) ValueListNode(pool, 0u);
|
inputFirst = FB_NEW_POOL(pool) ValueListNode(pool, 0u);
|
||||||
|
@ -1340,7 +1340,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
NestConst<SelectExprNode> selectExpr;
|
NestConst<SelectExprNode> selectExpr;
|
||||||
NestConst<RseNode> rse;
|
NestConst<RseNode> rse;
|
||||||
TriState optimizeForFirstRows;
|
Firebird::TriState optimizeForFirstRows;
|
||||||
bool forUpdate = false;
|
bool forUpdate = false;
|
||||||
bool withLock = false;
|
bool withLock = false;
|
||||||
bool skipLocked = false;
|
bool skipLocked = false;
|
||||||
@ -1626,12 +1626,12 @@ public:
|
|||||||
std::optional<CommitNumber> atSnapshotNumber;
|
std::optional<CommitNumber> atSnapshotNumber;
|
||||||
std::optional<unsigned> isoLevel;
|
std::optional<unsigned> isoLevel;
|
||||||
std::optional<USHORT> lockTimeout;
|
std::optional<USHORT> lockTimeout;
|
||||||
TriState readOnly;
|
Firebird::TriState readOnly;
|
||||||
TriState wait;
|
Firebird::TriState wait;
|
||||||
TriState noAutoUndo;
|
Firebird::TriState noAutoUndo;
|
||||||
TriState ignoreLimbo;
|
Firebird::TriState ignoreLimbo;
|
||||||
TriState restartRequests;
|
Firebird::TriState restartRequests;
|
||||||
TriState autoCommit;
|
Firebird::TriState autoCommit;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1901,7 +1901,7 @@ public:
|
|||||||
virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const;
|
virtual void execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** traHandle) const;
|
||||||
|
|
||||||
public:
|
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> ANY_VALUE
|
||||||
%token <metaNamePtr> CALL
|
%token <metaNamePtr> CALL
|
||||||
|
%token <metaNamePtr> FORMAT
|
||||||
%token <metaNamePtr> NAMED_ARG_ASSIGN
|
%token <metaNamePtr> NAMED_ARG_ASSIGN
|
||||||
|
|
||||||
// precedence declarations for expression evaluation
|
// precedence declarations for expression evaluation
|
||||||
@ -735,7 +736,7 @@ using namespace Firebird;
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
std::optional<int> nullableIntVal;
|
std::optional<int> nullableIntVal;
|
||||||
TriState triState;
|
Firebird::TriState triState;
|
||||||
std::optional<Jrd::SqlSecurity> nullableSqlSecurityVal;
|
std::optional<Jrd::SqlSecurity> nullableSqlSecurityVal;
|
||||||
std::optional<Jrd::OverrideClause> nullableOverrideClause;
|
std::optional<Jrd::OverrideClause> nullableOverrideClause;
|
||||||
struct { bool first; bool second; } boolPair;
|
struct { bool first; bool second; } boolPair;
|
||||||
@ -4916,6 +4917,7 @@ non_charset_simple_type
|
|||||||
| numeric_type
|
| numeric_type
|
||||||
| float_type
|
| float_type
|
||||||
| decfloat_type
|
| decfloat_type
|
||||||
|
| date_time_type
|
||||||
| BIGINT
|
| BIGINT
|
||||||
{
|
{
|
||||||
$$ = newNode<dsql_fld>();
|
$$ = newNode<dsql_fld>();
|
||||||
@ -4959,60 +4961,6 @@ non_charset_simple_type
|
|||||||
$$->length = sizeof(SSHORT);
|
$$->length = sizeof(SSHORT);
|
||||||
$$->flags |= FLD_has_prec;
|
$$->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
|
| BOOLEAN
|
||||||
{
|
{
|
||||||
$$ = newNode<dsql_fld>();
|
$$ = newNode<dsql_fld>();
|
||||||
@ -6155,7 +6103,7 @@ query_spec
|
|||||||
rse->dsqlFirst = $2 ? $2->items[1] : NULL;
|
rse->dsqlFirst = $2 ? $2->items[1] : NULL;
|
||||||
rse->dsqlSkip = $2 ? $2->items[0] : NULL;
|
rse->dsqlSkip = $2 ? $2->items[0] : NULL;
|
||||||
rse->dsqlDistinct = $3;
|
rse->dsqlDistinct = $3;
|
||||||
rse->dsqlSelectList = $4;
|
rse->dsqlSelectList = $4->items.hasData() ? $4 : nullptr;
|
||||||
rse->dsqlFrom = $5;
|
rse->dsqlFrom = $5;
|
||||||
rse->dsqlWhere = $6;
|
rse->dsqlWhere = $6;
|
||||||
rse->dsqlGroup = $7;
|
rse->dsqlGroup = $7;
|
||||||
@ -6190,14 +6138,14 @@ skip_clause
|
|||||||
|
|
||||||
%type <valueListNode> distinct_clause
|
%type <valueListNode> distinct_clause
|
||||||
distinct_clause
|
distinct_clause
|
||||||
: DISTINCT { $$ = newNode<ValueListNode>(0); }
|
: DISTINCT { $$ = newNode<ValueListNode>(0u); }
|
||||||
| all_noise { $$ = NULL; }
|
| all_noise { $$ = NULL; }
|
||||||
;
|
;
|
||||||
|
|
||||||
%type <valueListNode> select_list
|
%type <valueListNode> select_list
|
||||||
select_list
|
select_list
|
||||||
: select_items { $$ = $1; }
|
: select_items { $$ = $1; }
|
||||||
| '*' { $$ = NULL; }
|
| '*' { $$ = newNode<ValueListNode>(0u); }
|
||||||
;
|
;
|
||||||
|
|
||||||
%type <valueListNode> select_items
|
%type <valueListNode> select_items
|
||||||
@ -8781,6 +8729,77 @@ named_argument
|
|||||||
cast_specification
|
cast_specification
|
||||||
: CAST '(' value AS data_type_descriptor ')'
|
: CAST '(' value AS data_type_descriptor ')'
|
||||||
{ $$ = newNode<CastNode>($3, $5); }
|
{ $$ = 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
|
// case expressions
|
||||||
@ -9444,6 +9463,7 @@ non_reserved_word
|
|||||||
| UNICODE_VAL
|
| UNICODE_VAL
|
||||||
// added in FB 6.0
|
// added in FB 6.0
|
||||||
| ANY_VALUE
|
| ANY_VALUE
|
||||||
|
| FORMAT
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@ -496,4 +496,6 @@
|
|||||||
|
|
||||||
#define blr_default_arg (unsigned char) 227
|
#define blr_default_arg (unsigned char) 227
|
||||||
|
|
||||||
|
#define blr_cast_format (unsigned char) 228
|
||||||
|
|
||||||
#endif // FIREBIRD_IMPL_BLR_H
|
#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, 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, 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, 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_not_exist = 335545290;
|
||||||
isc_param_no_default_not_specified = 335545291;
|
isc_param_no_default_not_specified = 335545291;
|
||||||
isc_param_multiple_assignments = 335545292;
|
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_db_name = 335740929;
|
||||||
isc_gfix_invalid_sw = 335740930;
|
isc_gfix_invalid_sw = 335740930;
|
||||||
isc_gfix_incmp_sw = 335740932;
|
isc_gfix_incmp_sw = 335740932;
|
||||||
|
@ -637,11 +637,15 @@ void Jrd::Attachment::signalShutdown(ISC_STATUS code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Jrd::Attachment::mergeStats()
|
void Jrd::Attachment::mergeStats(bool pageStatsOnly)
|
||||||
{
|
{
|
||||||
MutexLockGuard guard(att_database->dbb_stats_mutex, FB_FUNCTION);
|
MutexLockGuard guard(att_database->dbb_stats_mutex, FB_FUNCTION);
|
||||||
att_database->dbb_stats.adjust(att_base_stats, att_stats, true);
|
att_database->dbb_stats.adjustPageStats(att_base_stats, att_stats);
|
||||||
att_base_stats.assign(att_stats);
|
if (!pageStatsOnly)
|
||||||
|
{
|
||||||
|
att_database->dbb_stats.adjust(att_base_stats, att_stats, true);
|
||||||
|
att_base_stats.assign(att_stats);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -640,7 +640,7 @@ public:
|
|||||||
USHORT att_original_timezone;
|
USHORT att_original_timezone;
|
||||||
USHORT att_current_timezone;
|
USHORT att_current_timezone;
|
||||||
int att_parallel_workers;
|
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
|
PageToBufferMap* att_bdb_cache; // managed in CCH, created in att_pool, freed with it
|
||||||
|
|
||||||
@ -736,7 +736,7 @@ public:
|
|||||||
void signalCancel();
|
void signalCancel();
|
||||||
void signalShutdown(ISC_STATUS code);
|
void signalShutdown(ISC_STATUS code);
|
||||||
|
|
||||||
void mergeStats();
|
void mergeStats(bool pageStatsOnly = false);
|
||||||
bool hasActiveRequests() const;
|
bool hasActiveRequests() const;
|
||||||
|
|
||||||
bool backupStateWriteLock(thread_db* tdbb, SSHORT wait);
|
bool backupStateWriteLock(thread_db* tdbb, SSHORT wait);
|
||||||
|
@ -532,7 +532,7 @@ public:
|
|||||||
time_t dbb_linger_end;
|
time_t dbb_linger_end;
|
||||||
Firebird::RefPtr<Firebird::IPluginConfig> dbb_plugin_config;
|
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
|
Lock* dbb_repl_lock; // replication state lock
|
||||||
Firebird::SyncObject dbb_repl_sync;
|
Firebird::SyncObject dbb_repl_sync;
|
||||||
FB_UINT64 dbb_repl_sequence; // replication sequence
|
FB_UINT64 dbb_repl_sequence; // replication sequence
|
||||||
|
@ -747,6 +747,12 @@ ExtEngineManager::ExtRoutine::ExtRoutine(thread_db* tdbb, ExtEngineManager* aExt
|
|||||||
engine->addRef();
|
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
|
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() : "";
|
const MetaString& userName = udf->invoker ? udf->invoker->getUserName() : "";
|
||||||
ContextManager<IExternalFunction> ctxManager(tdbb, attInfo, function,
|
ContextManager<IExternalFunction> ctxManager(tdbb, attInfo, function,
|
||||||
(udf->getName().package.isEmpty() ?
|
(udf->getName().package.isEmpty() ?
|
||||||
@ -821,7 +827,7 @@ ExtEngineManager::ResultSet::ResultSet(thread_db* tdbb, UCHAR* inMsg, UCHAR* out
|
|||||||
attachment(tdbb->getAttachment()),
|
attachment(tdbb->getAttachment()),
|
||||||
firstFetch(true)
|
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() : "";
|
const MetaString& userName = procedure->prc->invoker ? procedure->prc->invoker->getUserName() : "";
|
||||||
ContextManager<IExternalProcedure> ctxManager(tdbb, attInfo, procedure->procedure,
|
ContextManager<IExternalProcedure> ctxManager(tdbb, attInfo, procedure->procedure,
|
||||||
(procedure->prc->getName().package.isEmpty() ?
|
(procedure->prc->getName().package.isEmpty() ?
|
||||||
@ -931,7 +937,7 @@ ExtEngineManager::Trigger::~Trigger()
|
|||||||
void ExtEngineManager::Trigger::execute(thread_db* tdbb, Request* request, unsigned action,
|
void ExtEngineManager::Trigger::execute(thread_db* tdbb, Request* request, unsigned action,
|
||||||
record_param* oldRpb, record_param* newRpb) const
|
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 :
|
const TriState& ssDefiner = trg->ssDefiner.isAssigned() ? trg->ssDefiner :
|
||||||
(trg->relation && trg->relation->rel_ss_definer.isAssigned() ? trg->relation->rel_ss_definer : TriState());
|
(trg->relation && trg->relation->rel_ss_definer.isAssigned() ? trg->relation->rel_ss_definer : TriState());
|
||||||
const MetaString& userName = ssDefiner.asBool() ?
|
const MetaString& userName = ssDefiner.asBool() ?
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
#define JRD_EXT_ENGINE_MANAGER_H
|
#define JRD_EXT_ENGINE_MANAGER_H
|
||||||
|
|
||||||
#include "firebird/Interface.h"
|
#include "firebird/Interface.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "../common/classes/array.h"
|
#include "../common/classes/array.h"
|
||||||
#include "../common/classes/fb_string.h"
|
#include "../common/classes/fb_string.h"
|
||||||
#include "../common/classes/GenericMap.h"
|
#include "../common/classes/GenericMap.h"
|
||||||
@ -216,9 +219,16 @@ public:
|
|||||||
ExtRoutine(thread_db* tdbb, ExtEngineManager* aExtManager,
|
ExtRoutine(thread_db* tdbb, ExtEngineManager* aExtManager,
|
||||||
Firebird::IExternalEngine* aEngine, RoutineMetadata* aMetadata);
|
Firebird::IExternalEngine* aEngine, RoutineMetadata* aMetadata);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class PluginDeleter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void operator()(Firebird::IPluginBase* ptr);
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ExtEngineManager* extManager;
|
ExtEngineManager* extManager;
|
||||||
Firebird::AutoPlugin<Firebird::IExternalEngine> engine;
|
std::unique_ptr<Firebird::IExternalEngine, PluginDeleter> engine;
|
||||||
Firebird::AutoPtr<RoutineMetadata> metadata;
|
Firebird::AutoPtr<RoutineMetadata> metadata;
|
||||||
Database* database;
|
Database* database;
|
||||||
};
|
};
|
||||||
|
@ -878,7 +878,7 @@ public:
|
|||||||
Firebird::Array<NestConst<RecordSourceNode> > rse_relations;
|
Firebird::Array<NestConst<RecordSourceNode> > rse_relations;
|
||||||
USHORT flags = 0;
|
USHORT flags = 0;
|
||||||
USHORT rse_jointype = blr_inner; // inner, left, full
|
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>
|
class SelectExprNode final : public TypedNode<RecordSourceNode, RecordSourceNode::TYPE_SELECT_EXPR>
|
||||||
|
@ -264,9 +264,9 @@ public:
|
|||||||
TrigVector* rel_post_store; // Post-operation store trigger
|
TrigVector* rel_post_store; // Post-operation store trigger
|
||||||
prim rel_primary_dpnds; // foreign dependencies on this relation's primary key
|
prim rel_primary_dpnds; // foreign dependencies on this relation's primary key
|
||||||
frgn rel_foreign_refs; // foreign references to other relations' primary keys
|
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;
|
Firebird::Mutex rel_drop_mutex;
|
||||||
|
|
||||||
|
@ -260,7 +260,21 @@ public:
|
|||||||
addRelCounts(baseStats.rel_counts, false);
|
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
|
// copy counters values from other instance
|
||||||
|
@ -388,8 +388,10 @@ const char
|
|||||||
WIRE_CRYPT_PLUGIN_NAME[] = "WIRE_CRYPT_PLUGIN",
|
WIRE_CRYPT_PLUGIN_NAME[] = "WIRE_CRYPT_PLUGIN",
|
||||||
CLIENT_ADDRESS_NAME[] = "CLIENT_ADDRESS",
|
CLIENT_ADDRESS_NAME[] = "CLIENT_ADDRESS",
|
||||||
CLIENT_HOST_NAME[] = "CLIENT_HOST",
|
CLIENT_HOST_NAME[] = "CLIENT_HOST",
|
||||||
|
CLIENT_OS_USER_NAME[] = "CLIENT_OS_USER",
|
||||||
CLIENT_PID_NAME[] = "CLIENT_PID",
|
CLIENT_PID_NAME[] = "CLIENT_PID",
|
||||||
CLIENT_PROCESS_NAME[] = "CLIENT_PROCESS",
|
CLIENT_PROCESS_NAME[] = "CLIENT_PROCESS",
|
||||||
|
CLIENT_VERSION_NAME[] = "CLIENT_VERSION",
|
||||||
CURRENT_USER_NAME[] = "CURRENT_USER",
|
CURRENT_USER_NAME[] = "CURRENT_USER",
|
||||||
CURRENT_ROLE_NAME[] = "CURRENT_ROLE",
|
CURRENT_ROLE_NAME[] = "CURRENT_ROLE",
|
||||||
SESSION_IDLE_TIMEOUT[] = "SESSION_IDLE_TIMEOUT",
|
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;
|
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)
|
else if (nameStr == CLIENT_PID_NAME)
|
||||||
{
|
{
|
||||||
if (!attachment->att_remote_pid)
|
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();
|
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)
|
else if (nameStr == CURRENT_USER_NAME)
|
||||||
{
|
{
|
||||||
const MetaString& user = attachment->getUserName();
|
const MetaString& user = attachment->getUserName();
|
||||||
|
@ -3,16 +3,16 @@
|
|||||||
*** DO NOT EDIT ***
|
*** DO NOT EDIT ***
|
||||||
TO CHANGE ANY INFORMATION IN HERE PLEASE
|
TO CHANGE ANY INFORMATION IN HERE PLEASE
|
||||||
EDIT src/misc/writeBuildNum.sh
|
EDIT src/misc/writeBuildNum.sh
|
||||||
FORMAL BUILD NUMBER:86
|
FORMAL BUILD NUMBER:107
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PRODUCT_VER_STRING "6.0.0.86"
|
#define PRODUCT_VER_STRING "6.0.0.107"
|
||||||
#define FILE_VER_STRING "WI-T6.0.0.86"
|
#define FILE_VER_STRING "WI-T6.0.0.107"
|
||||||
#define LICENSE_VER_STRING "WI-T6.0.0.86"
|
#define LICENSE_VER_STRING "WI-T6.0.0.107"
|
||||||
#define FILE_VER_NUMBER 6, 0, 0, 86
|
#define FILE_VER_NUMBER 6, 0, 0, 107
|
||||||
#define FB_MAJOR_VER "6"
|
#define FB_MAJOR_VER "6"
|
||||||
#define FB_MINOR_VER "0"
|
#define FB_MINOR_VER "0"
|
||||||
#define FB_REV_NO "0"
|
#define FB_REV_NO "0"
|
||||||
#define FB_BUILD_NO "86"
|
#define FB_BUILD_NO "107"
|
||||||
#define FB_BUILD_TYPE "T"
|
#define FB_BUILD_TYPE "T"
|
||||||
#define FB_BUILD_SUFFIX "Firebird 6.0 Initial"
|
#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.
|
// Execute DDL triggers.
|
||||||
void EXE_execute_ddl_triggers(thread_db* tdbb, jrd_tra* transaction, bool preTriggers, int action)
|
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)
|
if (attachment->att_ddl_triggers)
|
||||||
{
|
{
|
||||||
TrigVector triggers;
|
AutoSetRestore2<jrd_tra*, thread_db> tempTrans(tdbb,
|
||||||
TrigVector* triggersPtr = &triggers;
|
&thread_db::getTransaction,
|
||||||
HalfStaticArray<Trigger*, 4> cachedTriggers;
|
&thread_db::setTransaction,
|
||||||
|
transaction);
|
||||||
|
|
||||||
for (auto& trigger : *attachment->att_ddl_triggers)
|
EXE_execute_triggers(tdbb, &attachment->att_ddl_triggers, NULL, NULL, TRIGGER_DDL,
|
||||||
{
|
preTriggers ? StmtNode::PRE_TRIG : StmtNode::POST_TRIG, action);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1166,7 +1127,8 @@ void EXE_execute_triggers(thread_db* tdbb,
|
|||||||
record_param* old_rpb,
|
record_param* old_rpb,
|
||||||
record_param* new_rpb,
|
record_param* new_rpb,
|
||||||
TriggerAction trigger_action,
|
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)
|
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);
|
ptr->compile(tdbb);
|
||||||
|
|
||||||
trigger = ptr->statement->findRequest(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);
|
const Jrd::StmtNode* in_node);
|
||||||
|
|
||||||
void EXE_execute_triggers(Jrd::thread_db*, Jrd::TrigVector**, Jrd::record_param*, Jrd::record_param*,
|
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_receive(Jrd::thread_db*, Jrd::Request*, USHORT, ULONG, void*, bool = false);
|
||||||
void EXE_release(Jrd::thread_db*, Jrd::Request*);
|
void EXE_release(Jrd::thread_db*, Jrd::Request*);
|
||||||
|
@ -113,6 +113,12 @@ void IscConnection::attach(thread_db* tdbb)
|
|||||||
validatePassword(tdbb, m_dbName, newDpb);
|
validatePassword(tdbb, m_dbName, newDpb);
|
||||||
newDpb.insertInt(isc_dpb_ext_call_depth, attachment->att_ext_call_depth + 1);
|
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;
|
FbLocalStatus status;
|
||||||
{
|
{
|
||||||
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
|
EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION);
|
||||||
|
@ -292,7 +292,8 @@ enum UdfError
|
|||||||
static SSHORT blob_get_segment(blb*, UCHAR*, USHORT, USHORT*);
|
static SSHORT blob_get_segment(blb*, UCHAR*, USHORT, USHORT*);
|
||||||
static void blob_put_segment(blb*, const UCHAR*, USHORT);
|
static void blob_put_segment(blb*, const UCHAR*, USHORT);
|
||||||
static SLONG blob_lseek(blb*, USHORT, SLONG);
|
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,
|
static void invoke(thread_db* tdbb,
|
||||||
const Function* function,
|
const Function* function,
|
||||||
const Parameter* return_ptr,
|
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 = parameter->prm_desc;
|
||||||
temp_desc.dsc_address = temp_ptr;
|
temp_desc.dsc_address = temp_ptr;
|
||||||
// CVC: There's a theoretical possibility of overflowing "length" here.
|
ULONG length = FB_ALIGN(temp_desc.dsc_length, FB_DOUBLE_ALIGN);
|
||||||
USHORT length = FB_ALIGN(temp_desc.dsc_length, FB_DOUBLE_ALIGN);
|
|
||||||
|
|
||||||
// If we've got a null argument, just pass zeros (got any better ideas?)
|
// 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)
|
if (parameter->prm_fun_mechanism == FUN_value)
|
||||||
{
|
{
|
||||||
UCHAR* p = (UCHAR *) arg_ptr;
|
UCHAR* p = (UCHAR *) arg_ptr;
|
||||||
MOVE_CLEAR(p, (SLONG) length);
|
MOVE_CLEAR(p, length);
|
||||||
p += length;
|
p += length;
|
||||||
arg_ptr = reinterpret_cast<UDF_ARG*>(p);
|
arg_ptr = reinterpret_cast<UDF_ARG*>(p);
|
||||||
continue;
|
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)
|
if (parameter->prm_fun_mechanism != FUN_ref_with_null)
|
||||||
MOVE_CLEAR(temp_ptr, (SLONG) length);
|
MOVE_CLEAR(temp_ptr, length);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Probably for arrays and blobs it's better to preserve the
|
// 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_quad:
|
||||||
case dtype_array:
|
case dtype_array:
|
||||||
case dtype_blob:
|
case dtype_blob:
|
||||||
MOVE_CLEAR(temp_ptr, (SLONG) length);
|
MOVE_CLEAR(temp_ptr, length);
|
||||||
break;
|
break;
|
||||||
default: // FUN_ref_with_null, non-blob, non-array: we send null pointer.
|
default: // FUN_ref_with_null, non-blob, non-array: we send null pointer.
|
||||||
*arg_ptr++ = 0;
|
*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)
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -975,10 +976,11 @@ static SSHORT blob_get_segment(blb* blob, UCHAR* buffer, USHORT length, USHORT*
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static SLONG get_scalar_array(const Parameter* arg,
|
static ULONG get_scalar_array(thread_db* tdbb,
|
||||||
DSC* value,
|
const Parameter* arg,
|
||||||
scalar_array_desc* scalar_desc,
|
DSC* value,
|
||||||
UCharStack& stack)
|
scalar_array_desc* scalar_desc,
|
||||||
|
UCharStack& stack)
|
||||||
{
|
{
|
||||||
/**************************************
|
/**************************************
|
||||||
*
|
*
|
||||||
@ -992,17 +994,16 @@ static SLONG get_scalar_array(const Parameter* arg,
|
|||||||
* Return length of array desc.
|
* Return length of array desc.
|
||||||
*
|
*
|
||||||
**************************************/
|
**************************************/
|
||||||
thread_db* tdbb = JRD_get_thread_data();
|
MemoryPool& pool = *tdbb->getDefaultPool();
|
||||||
|
|
||||||
// Get first the array descriptor, then the array
|
// Get first the array descriptor, then the array
|
||||||
|
|
||||||
SLONG stuff[IAD_LEN(16) / 4];
|
SLONG stuff[IAD_LEN(16) / 4];
|
||||||
Ods::InternalArrayDesc* array_desc = (Ods::InternalArrayDesc*) stuff;
|
Ods::InternalArrayDesc* array_desc = (Ods::InternalArrayDesc*) stuff;
|
||||||
blb* blob = blb::get_array(tdbb, tdbb->getRequest()->req_transaction, (bid*)value->dsc_address,
|
blb* blob = blb::get_array(tdbb, tdbb->getRequest()->req_transaction,
|
||||||
array_desc);
|
(bid*) value->dsc_address, array_desc);
|
||||||
|
|
||||||
fb_assert(array_desc->iad_total_length >= 0); // check before upcasting to size_t
|
AutoPtr<UCHAR, ArrayDelete> data(FB_NEW_POOL(pool) UCHAR[array_desc->iad_total_length]);
|
||||||
UCHAR* data = FB_NEW_POOL(*getDefaultMemoryPool()) UCHAR[static_cast<size_t>(array_desc->iad_total_length)];
|
|
||||||
blob->BLB_get_data(tdbb, data, array_desc->iad_total_length);
|
blob->BLB_get_data(tdbb, data, array_desc->iad_total_length);
|
||||||
const USHORT dimensions = array_desc->iad_dimensions;
|
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;
|
dsc from = array_desc->iad_rpt[0].iad_desc;
|
||||||
|
|
||||||
if (to.dsc_dtype != from.dsc_dtype ||
|
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;
|
ULONG n = array_desc->iad_count;
|
||||||
UCHAR* const temp = FB_NEW_POOL(*getDefaultMemoryPool()) UCHAR[static_cast<size_t>(to.dsc_length * n)];
|
AutoPtr<UCHAR, ArrayDelete> temp(FB_NEW_POOL(pool) UCHAR[to.dsc_length * n]);
|
||||||
to.dsc_address = temp;
|
to.dsc_address = temp;
|
||||||
from.dsc_address = data;
|
from.dsc_address = data;
|
||||||
|
|
||||||
// This loop may call ERR_post indirectly.
|
for (; n; --n, to.dsc_address += to.dsc_length,
|
||||||
try
|
from.dsc_address += array_desc->iad_element_length)
|
||||||
{
|
{
|
||||||
for (; n; --n, to.dsc_address += to.dsc_length,
|
MOV_move(tdbb, &from, &to);
|
||||||
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.release();
|
||||||
data = temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill out the scalar array descriptor
|
// Fill out the scalar array descriptor
|
||||||
|
|
||||||
stack.push(data);
|
|
||||||
scalar_desc->sad_desc = arg->prm_desc;
|
scalar_desc->sad_desc = arg->prm_desc;
|
||||||
scalar_desc->sad_desc.dsc_address = data;
|
scalar_desc->sad_desc.dsc_address = data;
|
||||||
scalar_desc->sad_dimensions = dimensions;
|
scalar_desc->sad_dimensions = dimensions;
|
||||||
|
stack.push(data.release());
|
||||||
|
|
||||||
const Ods::InternalArrayDesc::iad_repeat* tail1 = array_desc->iad_rpt;
|
const Ods::InternalArrayDesc::iad_repeat* tail1 = array_desc->iad_rpt;
|
||||||
scalar_array_desc::sad_repeat* tail2 = scalar_desc->sad_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;
|
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();
|
Jrd::Attachment* attachment = sAtt->getHandle();
|
||||||
|
|
||||||
|
if (attachment)
|
||||||
|
attachment->mergeStats(true);
|
||||||
|
|
||||||
if (attachment && !async)
|
if (attachment && !async)
|
||||||
{
|
{
|
||||||
attachment->att_use_count--;
|
attachment->att_use_count--;
|
||||||
|
@ -148,7 +148,7 @@ public:
|
|||||||
Firebird::string entryPoint; // External trigger entrypoint
|
Firebird::string entryPoint; // External trigger entrypoint
|
||||||
Firebird::string extBody; // External trigger body
|
Firebird::string extBody; // External trigger body
|
||||||
ExtEngineManager::Trigger* extTrigger; // External trigger
|
ExtEngineManager::Trigger* extTrigger; // External trigger
|
||||||
TriState ssDefiner;
|
Firebird::TriState ssDefiner;
|
||||||
MetaName owner; // Owner for SQL SECURITY
|
MetaName owner; // Owner for SQL SECURITY
|
||||||
|
|
||||||
bool isActive() const;
|
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_dfw_cleanup = 4096; // DFW cleanup phase is active
|
||||||
const ULONG TDBB_repl_in_progress = 8192; // Prevent recursion in replication
|
const ULONG TDBB_repl_in_progress = 8192; // Prevent recursion in replication
|
||||||
const ULONG TDBB_replicator = 16384; // Replicator
|
const ULONG TDBB_replicator = 16384; // Replicator
|
||||||
|
const ULONG TDBB_async = 32768; // Async context (set in AST)
|
||||||
|
|
||||||
class thread_db : public Firebird::ThreadData
|
class thread_db : public Firebird::ThreadData
|
||||||
{
|
{
|
||||||
@ -624,7 +625,11 @@ public:
|
|||||||
reqStat->bumpValue(index, delta);
|
reqStat->bumpValue(index, delta);
|
||||||
traStat->bumpValue(index, delta);
|
traStat->bumpValue(index, delta);
|
||||||
attStat->bumpValue(index, delta);
|
attStat->bumpValue(index, delta);
|
||||||
dbbStat->bumpValue(index, delta);
|
|
||||||
|
if ((tdbb_flags & TDBB_async) && !attachment)
|
||||||
|
dbbStat->bumpValue(index, delta);
|
||||||
|
|
||||||
|
// else dbbStat is adjusted from attStat, see Attachment::mergeAsyncStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
void bumpRelStats(const RuntimeStatistics::StatType index, SLONG relation_id, SINT64 delta = 1)
|
void bumpRelStats(const RuntimeStatistics::StatType index, SLONG relation_id, SINT64 delta = 1)
|
||||||
@ -1109,6 +1114,8 @@ namespace Jrd {
|
|||||||
|
|
||||||
fb_assert((operator thread_db*())->getAttachment());
|
fb_assert((operator thread_db*())->getAttachment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*this)->tdbb_flags |= TDBB_async;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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*);
|
const Jrd::jrd_rel*, const Jrd::MetaName&, int, Jrd::jrd_tra*);
|
||||||
|
|
||||||
int MET_get_linger(Jrd::thread_db*);
|
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
|
#endif // JRD_MET_PROTO_H
|
||||||
|
@ -189,21 +189,19 @@ void InnerJoin::estimateCost(unsigned position,
|
|||||||
joinedStreams[position].selectivity = candidate->selectivity;
|
joinedStreams[position].selectivity = candidate->selectivity;
|
||||||
|
|
||||||
// Get the stream cardinality
|
// Get the stream cardinality
|
||||||
const auto tail = &csb->csb_rpt[stream->number];
|
const auto streamCardinality = csb->csb_rpt[stream->number].csb_cardinality;
|
||||||
const auto streamCardinality = tail->csb_cardinality;
|
|
||||||
|
|
||||||
auto currentCardinality = streamCardinality * candidate->selectivity;
|
auto currentCardinality = candidate->unique ?
|
||||||
|
MINIMUM_CARDINALITY : streamCardinality * candidate->selectivity;
|
||||||
auto currentCost = candidate->cost;
|
auto currentCost = candidate->cost;
|
||||||
|
|
||||||
// Unless an external sort is to be applied, adjust estimated cost and cardinality
|
// Given the "first-rows" mode specified (or implied)
|
||||||
// accordingly to the "first-rows" retrieval (if specified)
|
// 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())
|
if ((!sort || candidate->navigated) && optimizer->favorFirstRows())
|
||||||
{
|
|
||||||
currentCost -= DEFAULT_INDEX_COST;
|
|
||||||
currentCost /= MAX(currentCardinality, MINIMUM_CARDINALITY);
|
|
||||||
currentCost += DEFAULT_INDEX_COST;
|
|
||||||
currentCardinality = MINIMUM_CARDINALITY;
|
currentCardinality = MINIMUM_CARDINALITY;
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the nested loop cost, it's our default option
|
// Calculate the nested loop cost, it's our default option
|
||||||
const auto loopCost = currentCost * cardinality;
|
const auto loopCost = currentCost * cardinality;
|
||||||
@ -687,7 +685,7 @@ InnerJoin::StreamInfo* InnerJoin::getStreamInfo(StreamType stream)
|
|||||||
// Dump finally selected stream order
|
// Dump finally selected stream order
|
||||||
void InnerJoin::printBestOrder() const
|
void InnerJoin::printBestOrder() const
|
||||||
{
|
{
|
||||||
if (bestStreams.isEmpty())
|
if (bestStreams.getCount() < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
optimizer->printf(" best order, streams:");
|
optimizer->printf(" best order, streams:");
|
||||||
@ -742,7 +740,7 @@ void InnerJoin::printFoundOrder(StreamType position,
|
|||||||
// Dump finally selected stream order
|
// Dump finally selected stream order
|
||||||
void InnerJoin::printStartOrder() const
|
void InnerJoin::printStartOrder() const
|
||||||
{
|
{
|
||||||
optimizer->printf("Start join order, streams:");
|
bool found = false;
|
||||||
|
|
||||||
const auto end = innerStreams.end();
|
const auto end = innerStreams.end();
|
||||||
for (auto iter = innerStreams.begin(); iter != end; iter++)
|
for (auto iter = innerStreams.begin(); iter != end; iter++)
|
||||||
@ -750,6 +748,12 @@ void InnerJoin::printStartOrder() const
|
|||||||
const auto innerStream = *iter;
|
const auto innerStream = *iter;
|
||||||
if (!innerStream->used)
|
if (!innerStream->used)
|
||||||
{
|
{
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
optimizer->printf("Start join order, streams:");
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
const auto name = optimizer->getStreamName(innerStream->number);
|
const auto name = optimizer->getStreamName(innerStream->number);
|
||||||
optimizer->printf(" %u (%s) base cost (%1.2f)",
|
optimizer->printf(" %u (%s) base cost (%1.2f)",
|
||||||
innerStream->number, name.c_str(), innerStream->baseCost);
|
innerStream->number, name.c_str(), innerStream->baseCost);
|
||||||
|
@ -356,6 +356,16 @@ public:
|
|||||||
|
|
||||||
static double getSelectivity(const BoolExprNode* node)
|
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))
|
if (const auto cmpNode = nodeAs<ComparativeBoolNode>(node))
|
||||||
{
|
{
|
||||||
switch (cmpNode->blrOp)
|
switch (cmpNode->blrOp)
|
||||||
@ -382,16 +392,6 @@ public:
|
|||||||
break;
|
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;
|
return REDUCE_SELECTIVITY_FACTOR_OTHER;
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,34 @@ namespace
|
|||||||
return newValue;
|
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
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
@ -723,24 +751,22 @@ bool Retrieval::checkIndexCondition(index_desc& idx, MatchedBooleanList& matches
|
|||||||
const auto boolean = idxIter.object();
|
const auto boolean = idxIter.object();
|
||||||
|
|
||||||
// If the index condition is (A OR B) and any of the {A, B} is present
|
// 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
|
// among the available booleans, then the index is possibly usable.
|
||||||
const auto binaryNode = nodeAs<BinaryBoolNode>(boolean);
|
// Note: this check also includes the exact match.
|
||||||
if (binaryNode && binaryNode->blrOp == blr_or)
|
|
||||||
|
for (iter.rewind(); iter.hasData(); ++iter)
|
||||||
{
|
{
|
||||||
for (iter.rewind(); iter.hasData(); ++iter)
|
if (matchSubset(boolean, *iter))
|
||||||
{
|
{
|
||||||
if (binaryNode->arg1->sameAs(*iter, true) ||
|
matches.add(*iter);
|
||||||
binaryNode->arg2->sameAs(*iter, true))
|
break;
|
||||||
{
|
|
||||||
matches.add(*iter);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the index condition is (A IS NOT NULL) and the available booleans
|
// If the index condition is (A IS NOT NULL) and the available booleans
|
||||||
// includes any comparative predicate that explicitly mentions A,
|
// includes any comparative predicate that explicitly mentions A,
|
||||||
// then the index is possibly usable
|
// then the index is possibly usable
|
||||||
|
|
||||||
const auto notNode = nodeAs<NotBoolNode>(boolean);
|
const auto notNode = nodeAs<NotBoolNode>(boolean);
|
||||||
const auto missingNode = notNode ? nodeAs<MissingBoolNode>(notNode->arg) : nullptr;
|
const auto missingNode = notNode ? nodeAs<MissingBoolNode>(notNode->arg) : nullptr;
|
||||||
if (missingNode)
|
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);
|
idx.idx_fraction *= optimizer->getSelectivity(boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -833,7 +848,7 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
|
|||||||
if (scratch.candidate)
|
if (scratch.candidate)
|
||||||
{
|
{
|
||||||
matches.assign(scratch.matches);
|
matches.assign(scratch.matches);
|
||||||
scratch.selectivity = idx->idx_fraction;
|
scratch.selectivity = MAXIMUM_SELECTIVITY;
|
||||||
|
|
||||||
bool unique = false;
|
bool unique = false;
|
||||||
unsigned listCount = 0;
|
unsigned listCount = 0;
|
||||||
@ -1027,7 +1042,7 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
|
|||||||
|
|
||||||
const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool());
|
const auto invCandidate = FB_NEW_POOL(getPool()) InversionCandidate(getPool());
|
||||||
invCandidate->unique = unique;
|
invCandidate->unique = unique;
|
||||||
invCandidate->selectivity = selectivity;
|
invCandidate->selectivity = idx->idx_fraction * selectivity;
|
||||||
invCandidate->cost = cost;
|
invCandidate->cost = cost;
|
||||||
invCandidate->nonFullMatchedSegments = scratch.nonFullMatchedSegments;
|
invCandidate->nonFullMatchedSegments = scratch.nonFullMatchedSegments;
|
||||||
invCandidate->matchedSegments = MAX(scratch.lowerCount, scratch.upperCount);
|
invCandidate->matchedSegments = MAX(scratch.lowerCount, scratch.upperCount);
|
||||||
@ -1054,6 +1069,13 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
|
|||||||
invCandidate->scratch = &scratch;
|
invCandidate->scratch = &scratch;
|
||||||
invCandidate->matches.assign(scratch.matches);
|
invCandidate->matches.assign(scratch.matches);
|
||||||
|
|
||||||
|
for (auto match : invCandidate->matches)
|
||||||
|
{
|
||||||
|
match->findDependentFromStreams(csb, stream,
|
||||||
|
&invCandidate->dependentFromStreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
invCandidate->dependencies = invCandidate->dependentFromStreams.getCount();
|
||||||
inversions.add(invCandidate);
|
inversions.add(invCandidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1377,6 +1399,9 @@ InversionCandidate* Retrieval::makeInversion(InversionCandidateList& inversions)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentInv->boolean && matches.exist(currentInv->boolean))
|
||||||
|
anyMatchAlreadyUsed = true;
|
||||||
|
|
||||||
if (anyMatchAlreadyUsed && !customPlan)
|
if (anyMatchAlreadyUsed && !customPlan)
|
||||||
{
|
{
|
||||||
currentInv->used = true;
|
currentInv->used = true;
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
|
|
||||||
#include "../include/fb_blk.h"
|
#include "../include/fb_blk.h"
|
||||||
#include "../common/classes/array.h"
|
#include "../common/classes/array.h"
|
||||||
#include "../common/classes/TriState.h"
|
|
||||||
#include "../jrd/intl_classes.h"
|
#include "../jrd/intl_classes.h"
|
||||||
#include "../jrd/MetaName.h"
|
#include "../jrd/MetaName.h"
|
||||||
#include "../jrd/QualifiedName.h"
|
#include "../jrd/QualifiedName.h"
|
||||||
|
@ -9,7 +9,7 @@ BuildType=T
|
|||||||
MajorVer=6
|
MajorVer=6
|
||||||
MinorVer=0
|
MinorVer=0
|
||||||
RevNo=0
|
RevNo=0
|
||||||
BuildNum=86
|
BuildNum=107
|
||||||
|
|
||||||
NowAt=`pwd`
|
NowAt=`pwd`
|
||||||
cd `dirname $0`
|
cd `dirname $0`
|
||||||
|
@ -1652,7 +1652,7 @@ ISC_STATUS API_ROUTINE isc_attach_database(ISC_STATUS* userStatus, SSHORT fileLe
|
|||||||
return status[1];
|
return status[1];
|
||||||
|
|
||||||
YAttachment* attachment = dispatcher->attachDatabase(&statusWrapper, pathName.c_str(),
|
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)
|
if (status.getState() & IStatus::STATE_ERRORS)
|
||||||
return status[1];
|
return status[1];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user