mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 20:03:02 +01:00
Support for system packages and RDB$TIME_ZONE_UTIL system package implementation.
This commit is contained in:
parent
4ea34933f0
commit
3c4c8e781d
@ -221,11 +221,9 @@ select localtimestamp
|
||||
from rdb$database;
|
||||
```
|
||||
|
||||
# TODO: CURRENT_TIME and CURRENT_TIMESTAMP changes
|
||||
## TODO: CURRENT_TIME and CURRENT_TIMESTAMP changes
|
||||
|
||||
## Virtual tables
|
||||
|
||||
### `RDB$TIME_ZONES` table
|
||||
## Virtual table `RDB$TIME_ZONES`
|
||||
|
||||
This virtual table lists time zones supported in the engine.
|
||||
|
||||
@ -233,6 +231,64 @@ Columns:
|
||||
- `RDB$TIME_ZONE_ID` type `INTEGER`
|
||||
- `RDB$TIME_ZONE_NAME` type `CHAR(63)`
|
||||
|
||||
## Package `RDB$TIME_ZONE_UTIL`
|
||||
|
||||
This package has time zone utility functions and procedures.
|
||||
|
||||
### Function `DATABASE_VERSION`
|
||||
|
||||
`RDB$TIME_ZONE_UTIL.DATABASE_VERSION` returns the time zone database version.
|
||||
|
||||
Return type: `VARCHAR(10) CHARACTER SET ASCII`
|
||||
|
||||
```
|
||||
select rdb$time_zone_util.database_version()
|
||||
from rdb$database;
|
||||
```
|
||||
|
||||
Returns:
|
||||
```
|
||||
DATABASE_VERSION
|
||||
================
|
||||
2017c
|
||||
```
|
||||
|
||||
### Procedure `TRANSITIONS`
|
||||
|
||||
`RDB$TIME_ZONE_UTIL.TRANSITIONS` returns the set of rules between the start and end timestamps.
|
||||
|
||||
Input parameters:
|
||||
- `TIME_ZONE_NAME` type `CHAR(63)`
|
||||
- `FROM_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE`
|
||||
- `TO_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE`
|
||||
|
||||
Output parameters:
|
||||
- `START_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - the transition' start timestamp
|
||||
- `END_TIMESTAMP` type `TIMESTAMP WITH TIME ZONE` - the transition's end timestamp
|
||||
- `ZONE_OFFSET` type `SMALLINT` - number of minutes related to the zone's offset
|
||||
- `DST_OFFSET` type `SMALLINT` - number of minutes related to the zone's DST offset
|
||||
- `EFFECTIVE_OFFSET` type `SMALLINT` - effective offset (`ZONE_OFFSET + DST_OFFSET`)
|
||||
|
||||
```
|
||||
select *
|
||||
from rdb$time_zone_util.transitions(
|
||||
'America/Sao_Paulo',
|
||||
timestamp '2017-01-01',
|
||||
timestamp '2019-01-01');
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
```
|
||||
START_TIMESTAMP END_TIMESTAMP ZONE_OFFSET DST_OFFSET EFFECTIVE_OFFSET
|
||||
============================ ============================ =========== ========== ================
|
||||
2016-10-16 03:00:00.0000 GMT 2017-02-19 01:59:59.9999 GMT -180 60 -120
|
||||
2017-02-19 02:00:00.0000 GMT 2017-10-15 02:59:59.9999 GMT -180 0 -180
|
||||
2017-10-15 03:00:00.0000 GMT 2018-02-18 01:59:59.9999 GMT -180 60 -120
|
||||
2018-02-18 02:00:00.0000 GMT 2018-10-21 02:59:59.9999 GMT -180 0 -180
|
||||
2018-10-21 03:00:00.0000 GMT 2019-02-17 01:59:59.9999 GMT -180 60 -120
|
||||
```
|
||||
|
||||
|
||||
# Appendix: time zone regions
|
||||
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include "firebird.h"
|
||||
#include "../common/TimeZoneUtil.h"
|
||||
#include "../common/StatusHolder.h"
|
||||
#include "../common/unicode_util.h"
|
||||
#include "../common/classes/timestamp.h"
|
||||
#include "../common/classes/GenericMap.h"
|
||||
#include "unicode/ucal.h"
|
||||
@ -62,7 +61,6 @@ namespace
|
||||
static const TimeZoneDesc* getDesc(USHORT timeZone);
|
||||
static inline bool isOffset(USHORT timeZone);
|
||||
static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm);
|
||||
static USHORT makeFromRegion(const char* str, unsigned strLen);
|
||||
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone);
|
||||
static int parseNumber(const char*& p, const char* end);
|
||||
static void skipSpaces(const char*& p, const char* end);
|
||||
@ -249,6 +247,19 @@ USHORT TimeZoneUtil::getSystemTimeZone()
|
||||
return timeZoneStartup().systemTimeZone;
|
||||
}
|
||||
|
||||
void TimeZoneUtil::getDatabaseVersion(Firebird::string& str)
|
||||
{
|
||||
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
|
||||
UErrorCode icuErrorCode = U_ZERO_ERROR;
|
||||
|
||||
const char* version = icuLib.ucalGetTZDataVersion(&icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_getTZDataVersion.");
|
||||
|
||||
str = version;
|
||||
}
|
||||
|
||||
void TimeZoneUtil::iterateRegions(std::function<void (USHORT, const char*)> func)
|
||||
{
|
||||
for (USHORT i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
|
||||
@ -295,7 +306,45 @@ USHORT TimeZoneUtil::parse(const char* str, unsigned strLen)
|
||||
return makeFromOffset(sign, tzh, tzm);
|
||||
}
|
||||
else
|
||||
return makeFromRegion(p, str + strLen - p);
|
||||
return parseRegion(p, str + strLen - p);
|
||||
}
|
||||
|
||||
// Parses a time zone id from a region string.
|
||||
USHORT TimeZoneUtil::parseRegion(const char* str, unsigned strLen)
|
||||
{
|
||||
const char* end = str + strLen;
|
||||
|
||||
skipSpaces(str, end);
|
||||
|
||||
const char* start = str;
|
||||
|
||||
while (str < end &&
|
||||
((*str >= 'a' && *str <= 'z') ||
|
||||
(*str >= 'A' && *str <= 'Z') ||
|
||||
*str == '_' ||
|
||||
*str == '/') ||
|
||||
(str != start && *str >= '0' && *str <= '9') ||
|
||||
(str != start && *str == '+') ||
|
||||
(str != start && *str == '-'))
|
||||
{
|
||||
++str;
|
||||
}
|
||||
|
||||
unsigned len = str - start;
|
||||
|
||||
skipSpaces(str, end);
|
||||
|
||||
if (str == end)
|
||||
{
|
||||
string s(start, len);
|
||||
USHORT id;
|
||||
|
||||
if (timeZoneStartup().getId(s, id))
|
||||
return id;
|
||||
}
|
||||
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone region"); //// TODO:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Format a time zone to string, as offset or region.
|
||||
@ -353,7 +402,7 @@ void TimeZoneUtil::extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, int* sign,
|
||||
SINT64 ticks = timeStampTz.utc_timestamp.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY +
|
||||
timeStampTz.utc_timestamp.timestamp_time;
|
||||
|
||||
icuLib.ucalSetMillis(icuCalendar, (ticks - (40587 * TimeStamp::ISC_TICKS_PER_DAY)) / 10, &icuErrorCode);
|
||||
icuLib.ucalSetMillis(icuCalendar, ticksToIcuDate(ticks), &icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
{
|
||||
@ -521,7 +570,7 @@ void TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, struct t
|
||||
if (!icuCalendar)
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_open.");
|
||||
|
||||
icuLib.ucalSetMillis(icuCalendar, (ticks - (40587 * TimeStamp::ISC_TICKS_PER_DAY)) / 10, &icuErrorCode);
|
||||
icuLib.ucalSetMillis(icuCalendar, ticksToIcuDate(ticks), &icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
{
|
||||
@ -685,6 +734,103 @@ ISC_TIMESTAMP_TZ TimeZoneUtil::cvtDateToTimeStampTz(const ISC_DATE& date, Callba
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
TimeZoneRuleIterator::TimeZoneRuleIterator(USHORT aId, ISC_TIMESTAMP_TZ& aFrom, ISC_TIMESTAMP_TZ& aTo)
|
||||
: id(aId),
|
||||
icuLib(Jrd::UnicodeUtil::getConversionICU()),
|
||||
toTicks(aTo.utc_timestamp.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY + aTo.utc_timestamp.timestamp_time)
|
||||
{
|
||||
UErrorCode icuErrorCode = U_ZERO_ERROR;
|
||||
|
||||
icuCalendar = icuLib.ucalOpen(getDesc(id)->icuName, -1, NULL, UCAL_GREGORIAN, &icuErrorCode);
|
||||
|
||||
if (!icuCalendar)
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_open.");
|
||||
|
||||
SINT64 ticks = aFrom.utc_timestamp.timestamp_date * TimeStamp::ISC_TICKS_PER_DAY +
|
||||
aFrom.utc_timestamp.timestamp_time;
|
||||
|
||||
icuDate = TimeZoneUtil::ticksToIcuDate(ticks);
|
||||
|
||||
icuLib.ucalSetMillis(icuCalendar, icuDate, &icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
{
|
||||
fb_assert(false);
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_setMillis.");
|
||||
}
|
||||
|
||||
UBool hasNext = icuLib.ucalGetTimeZoneTransitionDate(icuCalendar, UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE,
|
||||
&icuDate, &icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
{
|
||||
fb_assert(false);
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_getTimeZoneTransitionDate.");
|
||||
}
|
||||
|
||||
if (!hasNext)
|
||||
icuDate = TimeZoneUtil::ticksToIcuDate(TimeStamp::MIN_DATE * TimeStamp::ISC_TICKS_PER_DAY);
|
||||
|
||||
icuLib.ucalSetMillis(icuCalendar, icuDate, &icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
{
|
||||
fb_assert(false);
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_setMillis.");
|
||||
}
|
||||
|
||||
startTicks = TimeZoneUtil::icuDateToTicks(icuDate);
|
||||
}
|
||||
|
||||
TimeZoneRuleIterator::~TimeZoneRuleIterator()
|
||||
{
|
||||
icuLib.ucalClose(icuCalendar);
|
||||
}
|
||||
|
||||
bool TimeZoneRuleIterator::next()
|
||||
{
|
||||
if (startTicks > toTicks)
|
||||
return false;
|
||||
|
||||
UErrorCode icuErrorCode = U_ZERO_ERROR;
|
||||
|
||||
startTimestamp.utc_timestamp.timestamp_date = startTicks / TimeStamp::ISC_TICKS_PER_DAY;
|
||||
startTimestamp.utc_timestamp.timestamp_time = startTicks % TimeStamp::ISC_TICKS_PER_DAY;
|
||||
startTimestamp.time_zone = TimeZoneUtil::GMT_ZONE;
|
||||
|
||||
zoneOffset = icuLib.ucalGet(icuCalendar, UCAL_ZONE_OFFSET, &icuErrorCode) / U_MILLIS_PER_MINUTE;
|
||||
dstOffset = icuLib.ucalGet(icuCalendar, UCAL_DST_OFFSET, &icuErrorCode) / U_MILLIS_PER_MINUTE;
|
||||
|
||||
UBool hasNext = icuLib.ucalGetTimeZoneTransitionDate(icuCalendar, UCAL_TZ_TRANSITION_NEXT,
|
||||
&icuDate, &icuErrorCode);
|
||||
|
||||
if (U_FAILURE(icuErrorCode))
|
||||
{
|
||||
fb_assert(false);
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_getTimeZoneTransitionDate.");
|
||||
}
|
||||
|
||||
if (!hasNext)
|
||||
{
|
||||
icuDate = TimeZoneUtil::ticksToIcuDate(
|
||||
TimeStamp::MAX_DATE * TimeStamp::ISC_TICKS_PER_DAY + TimeStamp::ISC_TICKS_PER_DAY);
|
||||
}
|
||||
|
||||
icuLib.ucalSetMillis(icuCalendar, icuDate, &icuErrorCode);
|
||||
|
||||
SINT64 endTicks = TimeZoneUtil::icuDateToTicks(icuDate) - 1;
|
||||
|
||||
endTimestamp.utc_timestamp.timestamp_date = endTicks / TimeStamp::ISC_TICKS_PER_DAY;
|
||||
endTimestamp.utc_timestamp.timestamp_time = endTicks % TimeStamp::ISC_TICKS_PER_DAY;
|
||||
endTimestamp.time_zone = TimeZoneUtil::GMT_ZONE;
|
||||
|
||||
startTicks = endTicks + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
static const TimeZoneDesc* getDesc(USHORT timeZone)
|
||||
{
|
||||
if (MAX_USHORT - timeZone < FB_NELEM(TIME_ZONE_LIST))
|
||||
@ -709,44 +855,6 @@ static USHORT makeFromOffset(int sign, unsigned tzh, unsigned tzm)
|
||||
return (USHORT)((tzh * 60 + tzm) * sign + TimeZoneUtil::ONE_DAY);
|
||||
}
|
||||
|
||||
// Makes a time zone id from a region.
|
||||
static USHORT makeFromRegion(const char* str, unsigned strLen)
|
||||
{
|
||||
const char* end = str + strLen;
|
||||
|
||||
skipSpaces(str, end);
|
||||
|
||||
const char* start = str;
|
||||
|
||||
while (str < end &&
|
||||
((*str >= 'a' && *str <= 'z') ||
|
||||
(*str >= 'A' && *str <= 'Z') ||
|
||||
*str == '_' ||
|
||||
*str == '/') ||
|
||||
(str != start && *str >= '0' && *str <= '9') ||
|
||||
(str != start && *str == '+') ||
|
||||
(str != start && *str == '-'))
|
||||
{
|
||||
++str;
|
||||
}
|
||||
|
||||
unsigned len = str - start;
|
||||
|
||||
skipSpaces(str, end);
|
||||
|
||||
if (str == end)
|
||||
{
|
||||
string s(start, len);
|
||||
USHORT id;
|
||||
|
||||
if (timeZoneStartup().getId(s, id))
|
||||
return id;
|
||||
}
|
||||
|
||||
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone region"); //// TODO:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Gets the displacement from a offset-based time zone id.
|
||||
static inline SSHORT offsetZoneToDisplacement(USHORT timeZone)
|
||||
{
|
||||
|
@ -29,7 +29,9 @@
|
||||
|
||||
#include <functional>
|
||||
#include "../common/classes/fb_string.h"
|
||||
#include "../common/classes/timestamp.h"
|
||||
#include "../common/cvt.h"
|
||||
#include "../common/unicode_util.h"
|
||||
|
||||
// struct tm declaration
|
||||
#if defined(TIME_WITH_SYS_TIME)
|
||||
@ -57,11 +59,25 @@ public:
|
||||
static const unsigned MAX_SIZE = MAX_LEN + 1;
|
||||
|
||||
public:
|
||||
static UDate ticksToIcuDate(SINT64 ticks)
|
||||
{
|
||||
return (ticks - (40587 * TimeStamp::ISC_TICKS_PER_DAY)) / 10;
|
||||
}
|
||||
|
||||
static SINT64 icuDateToTicks(UDate icuDate)
|
||||
{
|
||||
return (SINT64(icuDate) * 10) + (40587 * TimeStamp::ISC_TICKS_PER_DAY);
|
||||
}
|
||||
|
||||
static USHORT getSystemTimeZone();
|
||||
|
||||
static void getDatabaseVersion(Firebird::string& str);
|
||||
|
||||
static void iterateRegions(std::function<void (USHORT, const char*)> func);
|
||||
|
||||
static USHORT parse(const char* str, unsigned strLen);
|
||||
static USHORT parseRegion(const char* str, unsigned strLen);
|
||||
|
||||
static unsigned format(char* buffer, size_t bufferSize, USHORT timeZone);
|
||||
|
||||
static bool isValidOffset(int sign, unsigned tzh, unsigned tzm);
|
||||
@ -100,6 +116,30 @@ public:
|
||||
static ISC_TIMESTAMP_TZ cvtDateToTimeStampTz(const ISC_DATE& date, Callbacks* cb);
|
||||
};
|
||||
|
||||
class TimeZoneRuleIterator
|
||||
{
|
||||
public:
|
||||
TimeZoneRuleIterator(USHORT aId, ISC_TIMESTAMP_TZ& aFrom, ISC_TIMESTAMP_TZ& aTo);
|
||||
~TimeZoneRuleIterator();
|
||||
|
||||
public:
|
||||
bool next();
|
||||
|
||||
public:
|
||||
ISC_TIMESTAMP_TZ startTimestamp;
|
||||
ISC_TIMESTAMP_TZ endTimestamp;
|
||||
SSHORT zoneOffset;
|
||||
SSHORT dstOffset;
|
||||
|
||||
private:
|
||||
const USHORT id;
|
||||
Jrd::UnicodeUtil::ConversionICU& icuLib;
|
||||
SINT64 startTicks;
|
||||
SINT64 toTicks;
|
||||
UCalendar* icuCalendar;
|
||||
UDate icuDate;
|
||||
};
|
||||
|
||||
} // namespace Firebird
|
||||
|
||||
#endif // COMMON_TIME_ZONE_UTIL_H
|
||||
|
@ -284,6 +284,7 @@ private:
|
||||
if (!inModule)
|
||||
return;
|
||||
|
||||
getEntryPoint("ucal_getTZDataVersion", inModule, ucalGetTZDataVersion);
|
||||
getEntryPoint("ucal_open", inModule, ucalOpen);
|
||||
getEntryPoint("ucal_close", inModule, ucalClose);
|
||||
getEntryPoint("ucal_setMillis", inModule, ucalSetMillis);
|
||||
@ -291,6 +292,9 @@ private:
|
||||
getEntryPoint("ucal_setDateTime", inModule, ucalSetDateTime);
|
||||
getEntryPoint("ucal_getTimeZoneID", inModule, ucalGetTimeZoneID);
|
||||
|
||||
getEntryPoint("ucal_getNow", inModule, ucalGetNow);
|
||||
getEntryPoint("ucal_getTimeZoneTransitionDate", inModule, ucalGetTimeZoneTransitionDate);
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
getEntryPoint("ucal_openTimeZones", inModule, ucalOpenTimeZones);
|
||||
|
||||
|
@ -123,6 +123,7 @@ public:
|
||||
|
||||
int32_t (U_EXPORT2* ustrcmp) (const UChar* s1, const UChar* s2);
|
||||
|
||||
const char* (U_EXPORT2* ucalGetTZDataVersion) (UErrorCode* status);
|
||||
UCalendar* (U_EXPORT2* ucalOpen) (const UChar* zoneID, int32_t len, const char* locale, UCalendarType type,
|
||||
UErrorCode* err);
|
||||
void (U_EXPORT2* ucalClose) (UCalendar* cal);
|
||||
@ -133,6 +134,10 @@ public:
|
||||
int32_t (U_EXPORT2* ucalGetTimeZoneID) (const UCalendar* cal, UChar* result, int32_t resultLength,
|
||||
UErrorCode *status);
|
||||
|
||||
UDate (U_EXPORT2* ucalGetNow) ();
|
||||
UBool (U_EXPORT2* ucalGetTimeZoneTransitionDate) (const UCalendar* cal, UTimeZoneTransitionType type,
|
||||
UDate* transition, UErrorCode* status);
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
UEnumeration* (U_EXPORT2* ucalOpenTimeZones) (UErrorCode* ec);
|
||||
|
||||
|
@ -290,6 +290,23 @@ public:
|
||||
value = util->encodeDate(year, month, day);
|
||||
}
|
||||
|
||||
public:
|
||||
FbDate& operator=(ISC_DATE& val)
|
||||
{
|
||||
*(this) = *(FbDate*) &val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator ISC_DATE&()
|
||||
{
|
||||
return *(ISC_DATE*) this;
|
||||
}
|
||||
|
||||
operator const ISC_DATE&() const
|
||||
{
|
||||
return *(ISC_DATE*) this;
|
||||
}
|
||||
|
||||
public:
|
||||
ISC_DATE value;
|
||||
};
|
||||
@ -337,6 +354,23 @@ public:
|
||||
value = util->encodeTime(hours, minutes, seconds, fractions);
|
||||
}
|
||||
|
||||
public:
|
||||
FbTime& operator=(ISC_TIME& val)
|
||||
{
|
||||
*(this) = *(FbTime*) &val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator ISC_TIME&()
|
||||
{
|
||||
return *(ISC_TIME*) this;
|
||||
}
|
||||
|
||||
operator const ISC_TIME&() const
|
||||
{
|
||||
return *(ISC_TIME*) this;
|
||||
}
|
||||
|
||||
public:
|
||||
ISC_TIME value;
|
||||
};
|
||||
@ -344,6 +378,23 @@ public:
|
||||
// This class has memory layout identical to ISC_TIME_TZ.
|
||||
class FbTimeTz
|
||||
{
|
||||
public:
|
||||
FbTimeTz& operator=(ISC_TIME_TZ& val)
|
||||
{
|
||||
*(this) = *(FbTimeTz*) &val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator ISC_TIME_TZ&()
|
||||
{
|
||||
return *(ISC_TIME_TZ*) this;
|
||||
}
|
||||
|
||||
operator const ISC_TIME_TZ&() const
|
||||
{
|
||||
return *(ISC_TIME_TZ*) this;
|
||||
}
|
||||
|
||||
public:
|
||||
FbTime utcTime;
|
||||
ISC_USHORT timeZone;
|
||||
@ -352,6 +403,23 @@ public:
|
||||
// This class has memory layout identical to ISC_TIMESTAMP.
|
||||
class FbTimestamp
|
||||
{
|
||||
public:
|
||||
FbTimestamp& operator=(ISC_TIMESTAMP& val)
|
||||
{
|
||||
*(this) = *(FbTimestamp*) &val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator ISC_TIMESTAMP&()
|
||||
{
|
||||
return *(ISC_TIMESTAMP*) this;
|
||||
}
|
||||
|
||||
operator const ISC_TIMESTAMP&() const
|
||||
{
|
||||
return *(ISC_TIMESTAMP*) this;
|
||||
}
|
||||
|
||||
public:
|
||||
FbDate date;
|
||||
FbTime time;
|
||||
@ -360,6 +428,23 @@ public:
|
||||
// This class has memory layout identical to ISC_TIMESTAMP_TZ.
|
||||
class FbTimestampTz
|
||||
{
|
||||
public:
|
||||
FbTimestampTz& operator=(ISC_TIMESTAMP_TZ& val)
|
||||
{
|
||||
*(this) = *(FbTimestampTz*) &val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator ISC_TIMESTAMP_TZ&()
|
||||
{
|
||||
return *(ISC_TIMESTAMP_TZ*) this;
|
||||
}
|
||||
|
||||
operator const ISC_TIMESTAMP_TZ&() const
|
||||
{
|
||||
return *(ISC_TIMESTAMP_TZ*) this;
|
||||
}
|
||||
|
||||
public:
|
||||
FbTimestamp utcTimestamp;
|
||||
ISC_USHORT timeZone;
|
||||
|
@ -43,6 +43,8 @@
|
||||
#include "../jrd/mov_proto.h"
|
||||
#include "../jrd/par_proto.h"
|
||||
#include "../jrd/Function.h"
|
||||
#include "../jrd/TimeZone.h"
|
||||
#include "../jrd/SystemPackages.h"
|
||||
#include "../common/isc_proto.h"
|
||||
#include "../common/classes/auto.h"
|
||||
#include "../common/classes/fb_string.h"
|
||||
@ -1016,8 +1018,114 @@ unloaded only on program exit, causing at that moment AV if this code is active:
|
||||
//---------------------
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class SystemEngine : public StdPlugin<IExternalEngineImpl<SystemEngine, ThrowStatusExceptionWrapper> >
|
||||
{
|
||||
public:
|
||||
explicit SystemEngine()
|
||||
{
|
||||
}
|
||||
|
||||
int release() override
|
||||
{
|
||||
if (--refCounter == 0)
|
||||
{
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public:
|
||||
void open(ThrowStatusExceptionWrapper* status, IExternalContext* context,
|
||||
char* name, unsigned nameSize) override
|
||||
{
|
||||
}
|
||||
|
||||
void openAttachment(ThrowStatusExceptionWrapper* status, IExternalContext* context) override
|
||||
{
|
||||
}
|
||||
|
||||
void closeAttachment(ThrowStatusExceptionWrapper* status, IExternalContext* context) override
|
||||
{
|
||||
}
|
||||
|
||||
IExternalFunction* makeFunction(ThrowStatusExceptionWrapper* status, IExternalContext* context,
|
||||
IRoutineMetadata* metadata, IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder) override
|
||||
{
|
||||
const char* packageName = metadata->getPackage(status);
|
||||
const char* routineName = metadata->getName(status);
|
||||
|
||||
for (auto& package : SystemPackage::LIST)
|
||||
{
|
||||
if (strcmp(package.name, packageName) == 0)
|
||||
{
|
||||
for (auto& routine : package.functions)
|
||||
{
|
||||
if (strcmp(routine.name, routineName) == 0)
|
||||
return routine.factory(status, context, metadata, inBuilder, outBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fb_assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IExternalProcedure* makeProcedure(ThrowStatusExceptionWrapper* status, IExternalContext* context,
|
||||
IRoutineMetadata* metadata, IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder) override
|
||||
{
|
||||
const char* packageName = metadata->getPackage(status);
|
||||
const char* routineName = metadata->getName(status);
|
||||
|
||||
for (auto& package : SystemPackage::LIST)
|
||||
{
|
||||
if (strcmp(package.name, packageName) == 0)
|
||||
{
|
||||
for (auto& routine : package.procedures)
|
||||
{
|
||||
if (strcmp(routine.name, routineName) == 0)
|
||||
return routine.factory(status, context, metadata, inBuilder, outBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fb_assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IExternalTrigger* makeTrigger(ThrowStatusExceptionWrapper* status, IExternalContext* context,
|
||||
IRoutineMetadata* metadata, IMetadataBuilder* fieldsBuilder) override
|
||||
{
|
||||
fb_assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
static SystemEngine* INSTANCE;
|
||||
};
|
||||
|
||||
SystemEngine* SystemEngine::INSTANCE = nullptr;
|
||||
}
|
||||
|
||||
|
||||
//---------------------
|
||||
|
||||
|
||||
void ExtEngineManager::initialize()
|
||||
{
|
||||
SystemEngine::INSTANCE = FB_NEW SystemEngine();
|
||||
}
|
||||
|
||||
|
||||
ExtEngineManager::ExtEngineManager(MemoryPool& p)
|
||||
: PermanentStorage(p),
|
||||
engines(p),
|
||||
enginesAttachments(p)
|
||||
{
|
||||
engines.put("SYSTEM", SystemEngine::INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
|
@ -294,13 +294,7 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
explicit ExtEngineManager(MemoryPool& p)
|
||||
: PermanentStorage(p),
|
||||
engines(p),
|
||||
enginesAttachments(p)
|
||||
{
|
||||
}
|
||||
|
||||
explicit ExtEngineManager(MemoryPool& p);
|
||||
~ExtEngineManager();
|
||||
|
||||
public:
|
||||
|
@ -661,6 +661,16 @@ void SnapshotData::putField(thread_db* tdbb, Record* record, const DumpField& fi
|
||||
from_desc.makeTimestamp(&value);
|
||||
MOV_move(tdbb, &from_desc, &to_desc);
|
||||
}
|
||||
else if (field.type == VALUE_TIMESTAMP_TZ)
|
||||
{
|
||||
fb_assert(field.length == sizeof(ISC_TIMESTAMP_TZ));
|
||||
ISC_TIMESTAMP_TZ value;
|
||||
memcpy(&value, field.data, field.length);
|
||||
|
||||
dsc from_desc;
|
||||
from_desc.makeTimestampTz(&value);
|
||||
MOV_move(tdbb, &from_desc, &to_desc);
|
||||
}
|
||||
else if (field.type == VALUE_STRING)
|
||||
{
|
||||
if (to_desc.isBlob())
|
||||
|
@ -56,6 +56,7 @@ public:
|
||||
VALUE_TABLE_ID,
|
||||
VALUE_INTEGER,
|
||||
VALUE_TIMESTAMP,
|
||||
VALUE_TIMESTAMP_TZ,
|
||||
VALUE_STRING,
|
||||
VALUE_BOOLEAN
|
||||
};
|
||||
@ -142,6 +143,11 @@ public:
|
||||
storeField(field_id, VALUE_TIMESTAMP, sizeof(ISC_TIMESTAMP), &value);
|
||||
}
|
||||
|
||||
void storeTimestampTz(int field_id, const ISC_TIMESTAMP_TZ& value)
|
||||
{
|
||||
storeField(field_id, VALUE_TIMESTAMP_TZ, sizeof(ISC_TIMESTAMP), &value);
|
||||
}
|
||||
|
||||
void storeString(int field_id, const Firebird::string& value)
|
||||
{
|
||||
if (value.length())
|
||||
|
80
src/jrd/SystemPackages.cpp
Normal file
80
src/jrd/SystemPackages.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* The contents of this file are subject to the Initial
|
||||
* Developer's Public License Version 1.0 (the "License");
|
||||
* you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
||||
*
|
||||
* Software distributed under the License is distributed AS IS,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing rights
|
||||
* and limitations under the License.
|
||||
*
|
||||
* The Original Code was created by Adriano dos Santos Fernandes
|
||||
* for the Firebird Open Source RDBMS project.
|
||||
*
|
||||
* Copyright (c) 2018 Adriano dos Santos Fernandes <adrianosf@gmail.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*/
|
||||
|
||||
#include "firebird.h"
|
||||
#include "../jrd/SystemPackages.h"
|
||||
#include "../jrd/ods.h"
|
||||
#include "../jrd/ini.h"
|
||||
#include "../jrd/TimeZone.h"
|
||||
|
||||
using namespace Firebird;
|
||||
using namespace Jrd;
|
||||
|
||||
|
||||
std::initializer_list<SystemPackage> SystemPackage::LIST =
|
||||
{
|
||||
// packages
|
||||
{
|
||||
"RDB$TIME_ZONE_UTIL",
|
||||
ODS_13_0,
|
||||
{
|
||||
// procedures
|
||||
{
|
||||
"TRANSITIONS",
|
||||
[]
|
||||
(ThrowStatusExceptionWrapper* status, IExternalContext* /*context*/,
|
||||
IRoutineMetadata* /*metadata*/, IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder)
|
||||
{
|
||||
return FB_NEW TimeZoneTransitionsProcedure(status, inBuilder, outBuilder);
|
||||
},
|
||||
prc_selectable,
|
||||
{ // input parameters
|
||||
{"TIME_ZONE_NAME", fld_tz_name, false},
|
||||
{"FROM_TIMESTAMP", fld_timestamp_tz, false},
|
||||
{"TO_TIMESTAMP", fld_timestamp_tz, false}
|
||||
},
|
||||
{ // output parameters
|
||||
{"START_TIMESTAMP", fld_timestamp_tz, false},
|
||||
{"END_TIMESTAMP", fld_timestamp_tz, false},
|
||||
{"ZONE_OFFSET", fld_tz_offset, false},
|
||||
{"DST_OFFSET", fld_tz_offset, false},
|
||||
{"EFFECTIVE_OFFSET", fld_tz_offset, false}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
// functions
|
||||
{
|
||||
"DATABASE_VERSION",
|
||||
[]
|
||||
(ThrowStatusExceptionWrapper* status, IExternalContext* /*context*/,
|
||||
IRoutineMetadata* /*metadata*/, IMetadataBuilder* inBuilder, IMetadataBuilder* outBuilder)
|
||||
{
|
||||
return FB_NEW TimeZoneDatabaseVersionFunction(status, inBuilder, outBuilder);
|
||||
},
|
||||
{ // parameters
|
||||
},
|
||||
{fld_tz_db_version, false}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
95
src/jrd/SystemPackages.h
Normal file
95
src/jrd/SystemPackages.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* The contents of this file are subject to the Initial
|
||||
* Developer's Public License Version 1.0 (the "License");
|
||||
* you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
||||
*
|
||||
* Software distributed under the License is distributed AS IS,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing rights
|
||||
* and limitations under the License.
|
||||
*
|
||||
* The Original Code was created by Adriano dos Santos Fernandes
|
||||
* for the Firebird Open Source RDBMS project.
|
||||
*
|
||||
* Copyright (c) 2018 Adriano dos Santos Fernandes <adrianosf@gmail.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*/
|
||||
|
||||
#ifndef JRD_SYSTEM_PACKAGES_H
|
||||
#define JRD_SYSTEM_PACKAGES_H
|
||||
|
||||
#include "firebird.h"
|
||||
#include "../common/status.h"
|
||||
#include "../jrd/constants.h"
|
||||
#include "firebird/Interface.h"
|
||||
#include <initializer_list>
|
||||
#include <functional>
|
||||
|
||||
namespace Jrd
|
||||
{
|
||||
struct SystemProcedureParameter
|
||||
{
|
||||
const char* name;
|
||||
USHORT fieldId;
|
||||
bool nullable;
|
||||
};
|
||||
|
||||
struct SystemProcedure
|
||||
{
|
||||
const char* name;
|
||||
std::function<Firebird::IExternalProcedure* (
|
||||
Firebird::ThrowStatusExceptionWrapper*,
|
||||
Firebird::IExternalContext*,
|
||||
Firebird::IRoutineMetadata*,
|
||||
Firebird::IMetadataBuilder*,
|
||||
Firebird::IMetadataBuilder*
|
||||
)> factory;
|
||||
prc_t type;
|
||||
std::initializer_list<SystemProcedureParameter> inputParameters;
|
||||
std::initializer_list<SystemProcedureParameter> outputParameters;
|
||||
};
|
||||
|
||||
struct SystemFunctionParameter
|
||||
{
|
||||
const char* name;
|
||||
USHORT fieldId;
|
||||
bool nullable;
|
||||
};
|
||||
|
||||
struct SystemFunctionReturnType
|
||||
{
|
||||
USHORT fieldId;
|
||||
bool nullable;
|
||||
};
|
||||
|
||||
struct SystemFunction
|
||||
{
|
||||
const char* name;
|
||||
std::function<Firebird::IExternalFunction* (
|
||||
Firebird::ThrowStatusExceptionWrapper*,
|
||||
Firebird::IExternalContext*,
|
||||
Firebird::IRoutineMetadata*,
|
||||
Firebird::IMetadataBuilder*,
|
||||
Firebird::IMetadataBuilder*
|
||||
)> factory;
|
||||
std::initializer_list<SystemFunctionParameter> parameters;
|
||||
SystemFunctionReturnType returnType;
|
||||
};
|
||||
|
||||
struct SystemPackage
|
||||
{
|
||||
const char* name;
|
||||
USHORT odsVersion;
|
||||
std::initializer_list<SystemProcedure> procedures;
|
||||
std::initializer_list<SystemFunction> functions;
|
||||
|
||||
static std::initializer_list<SystemPackage> LIST;
|
||||
};
|
||||
} // namespace Jrd
|
||||
|
||||
#endif // JRD_SYSTEM_PACKAGES_H
|
@ -34,27 +34,28 @@ using namespace Firebird;
|
||||
TimeZoneSnapshot::TimeZoneSnapshot(thread_db* tdbb, MemoryPool& pool)
|
||||
: SnapshotData(pool)
|
||||
{
|
||||
RecordBuffer* buffer = allocBuffer(tdbb, pool, rel_time_zones);
|
||||
|
||||
Record* record = buffer->getTempRecord();
|
||||
record->nullify();
|
||||
RecordBuffer* tzBuffer = allocBuffer(tdbb, pool, rel_time_zones);
|
||||
Record* tzRecord = tzBuffer->getTempRecord();
|
||||
tzRecord->nullify();
|
||||
|
||||
TimeZoneUtil::iterateRegions(
|
||||
[=]
|
||||
(USHORT id, const char* name)
|
||||
{
|
||||
SINT64 idValue = id;
|
||||
putField(tdbb, record, DumpField(f_tz_id, VALUE_INTEGER, sizeof(idValue), &idValue));
|
||||
|
||||
putField(tdbb, record, DumpField(f_tz_name, VALUE_STRING, static_cast<USHORT>(strlen(name)), name));
|
||||
|
||||
buffer->store(record);
|
||||
putField(tdbb, tzRecord, DumpField(f_tz_id, VALUE_INTEGER, sizeof(idValue), &idValue));
|
||||
putField(tdbb, tzRecord, DumpField(f_tz_name, VALUE_STRING, static_cast<USHORT>(strlen(name)), name));
|
||||
tzBuffer->store(tzRecord);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
TimeZonesTableScan::TimeZonesTableScan(CompilerScratch* csb, const Firebird::string& alias,
|
||||
//--------------------------------------
|
||||
|
||||
|
||||
TimeZonesTableScan::TimeZonesTableScan(CompilerScratch* csb, const string& alias,
|
||||
StreamType stream, jrd_rel* relation)
|
||||
: VirtualTableScan(csb, alias, stream, relation)
|
||||
{
|
||||
@ -66,8 +67,59 @@ const Format* TimeZonesTableScan::getFormat(thread_db* tdbb, jrd_rel* relation)
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
|
||||
bool TimeZonesTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation,
|
||||
FB_UINT64 position, Record* record) const
|
||||
{
|
||||
return tdbb->getTransaction()->getTimeZoneSnapshot(tdbb)->getData(relation)->fetch(position, record);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
|
||||
TimeZoneTransitionsResultSet::TimeZoneTransitionsResultSet(ThrowStatusExceptionWrapper* status,
|
||||
IExternalContext* context, void* inMsg, void* outMsg)
|
||||
: out(static_cast<TimeZoneTransitionsOutput::Type*>(outMsg))
|
||||
{
|
||||
TimeZoneTransitionsInput::Type* in = static_cast<TimeZoneTransitionsInput::Type*>(inMsg);
|
||||
|
||||
out->startTimestampNull = out->endTimestampNull = out->zoneOffsetNull =
|
||||
out->dstOffsetNull = out->effectiveOffsetNull = FB_FALSE;
|
||||
|
||||
USHORT tzId = TimeZoneUtil::parseRegion(in->timeZoneName.str, in->timeZoneName.length);
|
||||
|
||||
iterator = FB_NEW TimeZoneRuleIterator(tzId, in->fromTimestamp, in->toTimestamp);
|
||||
}
|
||||
|
||||
FB_BOOLEAN TimeZoneTransitionsResultSet::fetch(ThrowStatusExceptionWrapper* status)
|
||||
{
|
||||
if (!iterator->next())
|
||||
return false;
|
||||
|
||||
out->startTimestamp = iterator->startTimestamp;
|
||||
out->endTimestamp = iterator->endTimestamp;
|
||||
out->zoneOffset = iterator->zoneOffset;
|
||||
out->dstOffset = iterator->dstOffset;
|
||||
out->effectiveOffset = iterator->zoneOffset + iterator->dstOffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
|
||||
void TimeZoneDatabaseVersionFunction::execute(ThrowStatusExceptionWrapper* status,
|
||||
IExternalContext* context, void* inMsg, void* outMsg)
|
||||
{
|
||||
TimeZoneDatabaseVersionOutput::Type* out = static_cast<TimeZoneDatabaseVersionOutput::Type*>(outMsg);
|
||||
|
||||
string str;
|
||||
TimeZoneUtil::getDatabaseVersion(str);
|
||||
|
||||
out->versionNull = FB_FALSE;
|
||||
out->version.set(str.c_str());
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#define JRD_TIME_ZONE_H
|
||||
|
||||
#include "firebird.h"
|
||||
#include "firebird/Message.h"
|
||||
#include "../common/classes/fb_string.h"
|
||||
#include "../jrd/Monitoring.h"
|
||||
#include "../jrd/recsrc/RecordSource.h"
|
||||
@ -52,6 +53,111 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
FB_MESSAGE(TimeZoneTransitionsInput, Firebird::ThrowStatusExceptionWrapper,
|
||||
(FB_INTL_VARCHAR(MAX_SQL_IDENTIFIER_LEN, CS_METADATA), timeZoneName)
|
||||
(FB_TIMESTAMP_TZ, fromTimestamp)
|
||||
(FB_TIMESTAMP_TZ, toTimestamp)
|
||||
);
|
||||
|
||||
FB_MESSAGE(TimeZoneTransitionsOutput, Firebird::ThrowStatusExceptionWrapper,
|
||||
(FB_TIMESTAMP_TZ, startTimestamp)
|
||||
(FB_TIMESTAMP_TZ, endTimestamp)
|
||||
(FB_SMALLINT, zoneOffset)
|
||||
(FB_SMALLINT, dstOffset)
|
||||
(FB_SMALLINT, effectiveOffset)
|
||||
);
|
||||
|
||||
class TimeZoneTransitionsResultSet :
|
||||
public Firebird::DisposeIface<
|
||||
Firebird::IExternalResultSetImpl<TimeZoneTransitionsResultSet, Firebird::ThrowStatusExceptionWrapper> >
|
||||
{
|
||||
public:
|
||||
TimeZoneTransitionsResultSet(Firebird::ThrowStatusExceptionWrapper* status, Firebird::IExternalContext* context,
|
||||
void* inMsg, void* outMsg);
|
||||
|
||||
public:
|
||||
void dispose() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
public:
|
||||
FB_BOOLEAN fetch(Firebird::ThrowStatusExceptionWrapper* status) override;
|
||||
|
||||
private:
|
||||
TimeZoneTransitionsOutput::Type* out;
|
||||
Firebird::AutoPtr<Firebird::TimeZoneRuleIterator> iterator;
|
||||
};
|
||||
|
||||
class TimeZoneTransitionsProcedure :
|
||||
public Firebird::DisposeIface<
|
||||
Firebird::IExternalProcedureImpl<TimeZoneTransitionsProcedure, Firebird::ThrowStatusExceptionWrapper> >
|
||||
{
|
||||
public:
|
||||
TimeZoneTransitionsProcedure(Firebird::ThrowStatusExceptionWrapper* status,
|
||||
Firebird::IMetadataBuilder* inBuilder, Firebird::IMetadataBuilder* outBuilder)
|
||||
{
|
||||
TimeZoneTransitionsInput::setup(status, inBuilder);
|
||||
TimeZoneTransitionsOutput::setup(status, outBuilder);
|
||||
}
|
||||
|
||||
public:
|
||||
void dispose() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
public:
|
||||
void getCharSet(Firebird::ThrowStatusExceptionWrapper* status, Firebird::IExternalContext* context,
|
||||
char* name, unsigned nameSize) override
|
||||
{
|
||||
strncpy(name, "UTF8", nameSize);
|
||||
}
|
||||
|
||||
Firebird::IExternalResultSet* open(Firebird::ThrowStatusExceptionWrapper* status,
|
||||
Firebird::IExternalContext* context, void* inMsg, void* outMsg) override
|
||||
{
|
||||
return FB_NEW TimeZoneTransitionsResultSet(status, context, inMsg, outMsg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FB_MESSAGE(TimeZoneDatabaseVersionInput, Firebird::ThrowStatusExceptionWrapper,
|
||||
);
|
||||
|
||||
FB_MESSAGE(TimeZoneDatabaseVersionOutput, Firebird::ThrowStatusExceptionWrapper,
|
||||
(FB_INTL_VARCHAR(10, CS_ASCII), version)
|
||||
);
|
||||
|
||||
class TimeZoneDatabaseVersionFunction :
|
||||
public Firebird::DisposeIface<
|
||||
Firebird::IExternalFunctionImpl<TimeZoneDatabaseVersionFunction, Firebird::ThrowStatusExceptionWrapper> >
|
||||
{
|
||||
public:
|
||||
TimeZoneDatabaseVersionFunction(Firebird::ThrowStatusExceptionWrapper* status,
|
||||
Firebird::IMetadataBuilder* inBuilder, Firebird::IMetadataBuilder* outBuilder)
|
||||
{
|
||||
TimeZoneDatabaseVersionInput::setup(status, inBuilder);
|
||||
TimeZoneDatabaseVersionOutput::setup(status, outBuilder);
|
||||
}
|
||||
|
||||
public:
|
||||
void dispose() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
public:
|
||||
void getCharSet(Firebird::ThrowStatusExceptionWrapper* status, Firebird::IExternalContext* context,
|
||||
char* name, unsigned nameSize) override
|
||||
{
|
||||
strncpy(name, "UTF8", nameSize);
|
||||
}
|
||||
|
||||
void execute(Firebird::ThrowStatusExceptionWrapper* status,
|
||||
Firebird::IExternalContext* context, void* inMsg, void* outMsg) override;
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -203,3 +203,7 @@
|
||||
|
||||
FIELD(fld_tz_id , nam_tz_id , dtype_long , sizeof(SLONG) , 0 , NULL , true)
|
||||
FIELD(fld_tz_name , nam_tz_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , true)
|
||||
FIELD(fld_tz_offset , nam_tz_offset , dtype_short , sizeof(SSHORT) , 0 , NULL , true)
|
||||
FIELD(fld_timestamp_tz , nam_timestamp_tz , dtype_timestamp_tz, TIMESTAMP_TZ_SIZE , 0 , NULL , true)
|
||||
|
||||
FIELD(fld_tz_db_version , nam_tz_db_version , dtype_varying , 10 , dsc_text_type_ascii , NULL , true)
|
||||
|
156
src/jrd/ini.epp
156
src/jrd/ini.epp
@ -59,6 +59,7 @@
|
||||
#include "../jrd/PreparedStatement.h"
|
||||
#include "../jrd/constants.h"
|
||||
#include "../jrd/grant_proto.h"
|
||||
#include "../jrd/SystemPackages.h"
|
||||
|
||||
using namespace Firebird;
|
||||
using namespace Jrd;
|
||||
@ -80,6 +81,7 @@ static void store_global_field(thread_db*, const gfld*, AutoRequest&, const Meta
|
||||
static void store_intlnames(thread_db*, const MetaName&);
|
||||
static void store_message(thread_db*, const trigger_msg*, AutoRequest&);
|
||||
static void store_relation_field(thread_db*, const int*, const int*, int, AutoRequest&);
|
||||
static void store_packages(thread_db* tdbb, const MetaName& owner);
|
||||
static void store_trigger(thread_db*, const jrd_trg*, AutoRequest&);
|
||||
static void store_admin_grant(thread_db*, const char* grantee, USHORT grantee_type,
|
||||
const char* object, USHORT object_type, const char* prvl, USHORT option = 0, bool dflt = false);
|
||||
@ -533,6 +535,8 @@ void INI_format(const char* owner, const char* charset)
|
||||
add_security_to_sys_rel(tdbb, ownerName, names[relfld[RFLD_R_NAME]], length, buffer);
|
||||
}
|
||||
|
||||
store_packages(tdbb, ownerName);
|
||||
|
||||
// store system-defined triggers
|
||||
|
||||
handle1.reset();
|
||||
@ -1376,6 +1380,10 @@ static void store_global_field(thread_db* tdbb, const gfld* gfield, AutoRequest&
|
||||
X.RDB$FIELD_TYPE = (int) blr_timestamp;
|
||||
break;
|
||||
|
||||
case dtype_timestamp_tz:
|
||||
X.RDB$FIELD_TYPE = (int) blr_timestamp_tz;
|
||||
break;
|
||||
|
||||
case dtype_sql_time:
|
||||
X.RDB$FIELD_TYPE = (int) blr_sql_time;
|
||||
break;
|
||||
@ -1620,6 +1628,154 @@ static void store_relation_field(thread_db* tdbb,
|
||||
}
|
||||
|
||||
|
||||
// Store system packages.
|
||||
static void store_packages(thread_db* tdbb, const MetaName& owner)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
Jrd::Attachment* attachment = tdbb->getAttachment();
|
||||
Database* const dbb = tdbb->getDatabase();
|
||||
const USHORT majorVersion = dbb->dbb_ods_version;
|
||||
const USHORT minorVersion = dbb->dbb_minor_version;
|
||||
|
||||
AutoRequest packageHandle, procedureHandle, procedureParameterHandle;
|
||||
AutoRequest functionHandle, functionReturnHandle, functionArgumentHandle;
|
||||
//// FIXME: check negative issue
|
||||
SSHORT procId = 0;
|
||||
SSHORT funcId = 0;
|
||||
|
||||
for (auto& systemPackage : SystemPackage::LIST)
|
||||
{
|
||||
if (systemPackage.odsVersion > ENCODE_ODS(majorVersion, minorVersion))
|
||||
continue;
|
||||
|
||||
STORE (REQUEST_HANDLE packageHandle) PKG IN RDB$PACKAGES
|
||||
{
|
||||
PAD(systemPackage.name, PKG.RDB$PACKAGE_NAME);
|
||||
|
||||
PAD(owner.c_str(), PKG.RDB$OWNER_NAME);
|
||||
PKG.RDB$SYSTEM_FLAG = RDB_system;
|
||||
PKG.RDB$VALID_BODY_FLAG = TRUE;
|
||||
|
||||
//// FIXME: RDB$SECURITY_CLASS, RDB$SQL_SECURITY
|
||||
}
|
||||
END_STORE
|
||||
|
||||
for (auto& procedure : systemPackage.procedures)
|
||||
{
|
||||
--procId;
|
||||
|
||||
STORE (REQUEST_HANDLE procedureHandle) PRC IN RDB$PROCEDURES
|
||||
{
|
||||
PAD(systemPackage.name, PRC.RDB$PACKAGE_NAME);
|
||||
PAD(procedure.name, PRC.RDB$PROCEDURE_NAME);
|
||||
|
||||
PAD(owner.c_str(), PRC.RDB$OWNER_NAME);
|
||||
PRC.RDB$SYSTEM_FLAG = RDB_system;
|
||||
|
||||
PRC.RDB$PROCEDURE_ID = procId;
|
||||
|
||||
PRC.RDB$PROCEDURE_INPUTS = (SSHORT) procedure.inputParameters.size();
|
||||
PRC.RDB$PROCEDURE_OUTPUTS = (SSHORT) procedure.outputParameters.size();
|
||||
PRC.RDB$PROCEDURE_TYPE = (SSHORT) procedure.type;
|
||||
PRC.RDB$PRIVATE_FLAG = FALSE;
|
||||
PRC.RDB$VALID_BLR = TRUE;
|
||||
PAD("SYSTEM", PRC.RDB$ENGINE_NAME);
|
||||
|
||||
//// FIXME: RDB$SECURITY_CLASS, RDB$SQL_SECURITY
|
||||
}
|
||||
END_STORE
|
||||
|
||||
for (SSHORT parameterType = 0; parameterType <= 1; ++parameterType)
|
||||
{
|
||||
SSHORT paramNumber = -1;
|
||||
|
||||
for (auto& parameter : parameterType == 0 ? procedure.inputParameters : procedure.outputParameters)
|
||||
{
|
||||
++paramNumber;
|
||||
|
||||
STORE (REQUEST_HANDLE procedureParameterHandle) PP IN RDB$PROCEDURE_PARAMETERS
|
||||
{
|
||||
PAD(systemPackage.name, PP.RDB$PACKAGE_NAME);
|
||||
PAD(procedure.name, PP.RDB$PROCEDURE_NAME);
|
||||
PAD(parameter.name, PP.RDB$PARAMETER_NAME);
|
||||
|
||||
PP.RDB$SYSTEM_FLAG = RDB_system;
|
||||
|
||||
PP.RDB$PARAMETER_NUMBER = paramNumber;
|
||||
PP.RDB$PARAMETER_TYPE = parameterType;
|
||||
PP.RDB$PARAMETER_MECHANISM = (SSHORT) prm_mech_normal;
|
||||
PP.RDB$NULL_FLAG = !parameter.nullable;
|
||||
|
||||
PAD(names[gfields[parameter.fieldId].gfld_name], PP.RDB$FIELD_SOURCE);
|
||||
}
|
||||
END_STORE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& function : systemPackage.functions)
|
||||
{
|
||||
--funcId;
|
||||
|
||||
STORE (REQUEST_HANDLE functionHandle) FUN IN RDB$FUNCTIONS
|
||||
{
|
||||
PAD(systemPackage.name, FUN.RDB$PACKAGE_NAME);
|
||||
PAD(function.name, FUN.RDB$FUNCTION_NAME);
|
||||
|
||||
PAD(owner.c_str(), FUN.RDB$OWNER_NAME);
|
||||
FUN.RDB$SYSTEM_FLAG = RDB_system;
|
||||
|
||||
FUN.RDB$FUNCTION_ID = procId;
|
||||
|
||||
FUN.RDB$RETURN_ARGUMENT = 0;
|
||||
FUN.RDB$PRIVATE_FLAG = FALSE;
|
||||
FUN.RDB$VALID_BLR = TRUE;
|
||||
PAD("SYSTEM", FUN.RDB$ENGINE_NAME);
|
||||
|
||||
//// FIXME: RDB$SECURITY_CLASS, RDB$SQL_SECURITY
|
||||
}
|
||||
END_STORE
|
||||
|
||||
SSHORT paramNumber = 0;
|
||||
|
||||
STORE (REQUEST_HANDLE functionReturnHandle) ARG IN RDB$FUNCTION_ARGUMENTS
|
||||
{
|
||||
PAD(systemPackage.name, ARG.RDB$PACKAGE_NAME);
|
||||
PAD(function.name, ARG.RDB$FUNCTION_NAME);
|
||||
|
||||
ARG.RDB$SYSTEM_FLAG = RDB_system;
|
||||
|
||||
ARG.RDB$ARGUMENT_POSITION = paramNumber;
|
||||
ARG.RDB$NULL_FLAG = !function.returnType.nullable;
|
||||
|
||||
PAD(names[gfields[function.returnType.fieldId].gfld_name], ARG.RDB$FIELD_SOURCE);
|
||||
}
|
||||
END_STORE
|
||||
|
||||
for (auto& parameter : function.parameters)
|
||||
{
|
||||
++paramNumber;
|
||||
|
||||
STORE (REQUEST_HANDLE functionArgumentHandle) ARG IN RDB$FUNCTION_ARGUMENTS
|
||||
{
|
||||
PAD(systemPackage.name, ARG.RDB$PACKAGE_NAME);
|
||||
PAD(function.name, ARG.RDB$FUNCTION_NAME);
|
||||
PAD(parameter.name, ARG.RDB$ARGUMENT_NAME);
|
||||
|
||||
ARG.RDB$SYSTEM_FLAG = RDB_system;
|
||||
|
||||
ARG.RDB$ARGUMENT_POSITION = paramNumber;
|
||||
ARG.RDB$NULL_FLAG = !parameter.nullable;
|
||||
|
||||
PAD(names[gfields[parameter.fieldId].gfld_name], ARG.RDB$FIELD_SOURCE);
|
||||
}
|
||||
END_STORE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void store_trigger(thread_db* tdbb, const jrd_trg* trigger, AutoRequest& handle)
|
||||
{
|
||||
/**************************************
|
||||
|
@ -67,6 +67,7 @@ static const TEXT* const names[] =
|
||||
//******************************
|
||||
const USHORT BLOB_SIZE = 8;
|
||||
const USHORT TIMESTAMP_SIZE = 8;
|
||||
const USHORT TIMESTAMP_TZ_SIZE = 12;
|
||||
|
||||
// Pick up global ids
|
||||
|
||||
|
@ -428,6 +428,10 @@ public:
|
||||
|
||||
static Static<EngineFactory> engineFactory;
|
||||
|
||||
|
||||
//-------------------------------
|
||||
|
||||
|
||||
void registerEngine(IPluginManager* iPlugin)
|
||||
{
|
||||
UnloadDetectorHelper* module = getUnloadDetector();
|
||||
|
@ -421,3 +421,7 @@ NAME("MON$WIRE_ENCRYPTED", nam_wire_encrypted)
|
||||
NAME("RDB$TIME_ZONES", nam_time_zones)
|
||||
NAME("RDB$TIME_ZONE_ID", nam_tz_id)
|
||||
NAME("RDB$TIME_ZONE_NAME", nam_tz_name)
|
||||
|
||||
NAME("RDB$TIME_ZONE_OFFSET", nam_tz_offset)
|
||||
NAME("RDB$TIMESTAMP_TZ", nam_timestamp_tz)
|
||||
NAME("RDB$DBTZ_VERSION", nam_tz_db_version)
|
||||
|
Loading…
Reference in New Issue
Block a user