8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 21:23:04 +01:00

Initial implementation of time zone support - CORE-694.

TODO:
- Support regions.
- Reevaluate decision of store date/time components in memory in its own time zone instead of normalized to UTC.
- Fix ISQL's SET TIME command.
- Allow usage of "WITHOUT TIME ZONE" with TIME/TIMESTAMP data types.
- Think about compatibility of CURRENT_TIME/CURRENT_TIMEZONE.
- Support AT { LOCAL | TIME ZONE <zone> } expression.
- Support as needed and fix assertions when altering field types.
- Change conversion to string to put a space before the time zone.
- Big endian version of SKD_time_tz and SKD_timestamp_tz.
This commit is contained in:
Adriano dos Santos Fernandes 2017-11-09 15:14:39 +00:00
parent 1824698b96
commit 7100879498
56 changed files with 1964 additions and 346 deletions

View File

@ -1488,11 +1488,21 @@ void put_data(burp_rel* relation)
add_byte(blr, field->fld_type);
break;
case blr_timestamp_tz:
alignment = type_alignments[dtype_timestamp_tz];
add_byte(blr, field->fld_type);
break;
case blr_sql_time:
alignment = type_alignments[dtype_sql_time];
add_byte(blr, field->fld_type);
break;
case blr_sql_time_tz:
alignment = type_alignments[dtype_sql_time_tz];
add_byte(blr, field->fld_type);
break;
case blr_sql_date:
alignment = type_alignments[dtype_sql_date];
add_byte(blr, field->fld_type);

View File

@ -147,6 +147,13 @@ ULONG CAN_encode_decode(burp_rel* relation, lstring* buffer, UCHAR* data, bool d
return FALSE;
break;
case dtype_sql_time_tz:
if (!xdr_long(xdrs, (SLONG*) p))
return FALSE;
if (!xdr_short(xdrs, (SSHORT*) (p + sizeof(SLONG))))
return FALSE;
break;
case dtype_real:
if (!xdr_float(xdrs, (float*) p))
return FALSE;
@ -175,6 +182,15 @@ ULONG CAN_encode_decode(burp_rel* relation, lstring* buffer, UCHAR* data, bool d
return FALSE;
break;
case dtype_timestamp_tz:
if (!xdr_long(xdrs, (SLONG*) p))
return FALSE;
if (!xdr_long(xdrs, &((SLONG*) p)[1]))
return FALSE;
if (!xdr_short(xdrs, (SSHORT*) (p + sizeof(SLONG) + sizeof(SLONG))))
return FALSE;
break;
case dtype_quad:
case dtype_blob:
if (!xdr_quad(xdrs, (SQUAD*) p))

View File

@ -3586,7 +3586,9 @@ rec_type get_data_old(BurpGlobals* tdgbl, burp_rel* relation)
case blr_float:
case blr_double:
case blr_timestamp:
case blr_timestamp_tz:
case blr_sql_time:
case blr_sql_time_tz:
case blr_sql_date:
case blr_dec64:
case blr_dec128:

View File

@ -150,6 +150,11 @@ MetadataFromBlr::MetadataFromBlr(unsigned aBlrLength, const unsigned char* aBlr,
item->length = sizeof(SLONG) * 2;
break;
case blr_timestamp_tz:
item->type = SQL_TIMESTAMP_TZ;
item->length = sizeof(ISC_TIMESTAMP_TZ);
break;
case blr_sql_date:
item->type = SQL_TYPE_DATE;
item->length = sizeof(SLONG);
@ -160,6 +165,11 @@ MetadataFromBlr::MetadataFromBlr(unsigned aBlrLength, const unsigned char* aBlr,
item->length = sizeof(SLONG);
break;
case blr_sql_time_tz:
item->type = SQL_TIME_TZ;
item->length = sizeof(ISC_TIME_TZ);
break;
case blr_blob2:
item->type = SQL_BLOB;
item->length = sizeof(ISC_QUAD);

View File

@ -324,4 +324,40 @@ void NoThrowTimeStamp::decode(struct tm* times, int* fractions) const
decode_timestamp(mValue, times, fractions);
}
bool NoThrowTimeStamp::is_valid_tz_offset(int tzh, unsigned tzm) throw()
{
tzh = tzh < 0 ? -tzh : tzh;
return tzm <= 59 && (tzh < 14 || (tzh == 14 && tzm == 0));
}
ISC_TIME NoThrowTimeStamp::timeTzAtZone(const ISC_TIME_TZ& timeTz, ISC_SHORT zone) throw()
{
SLONG ticks = timeTz.time_time -
(timeTz.time_displacement - zone) * 60 * ISC_TIME_SECONDS_PRECISION;
// Make the result positive
while (ticks < 0)
ticks += ISC_TICKS_PER_DAY;
// And make it in the range of values for a day
ticks %= ISC_TICKS_PER_DAY;
fb_assert(ticks >= 0 && ticks < ISC_TICKS_PER_DAY);
return (ISC_TIME) ticks;
}
ISC_TIMESTAMP NoThrowTimeStamp::timeStampTzAtZone(const ISC_TIMESTAMP_TZ& timeStampTz, ISC_SHORT zone) throw()
{
SINT64 ticks = timeStampTz.timestamp_date * ISC_TICKS_PER_DAY + timeStampTz.timestamp_time -
(timeStampTz.timestamp_displacement - zone) * 60 * ISC_TIME_SECONDS_PRECISION;
ISC_TIMESTAMP ts;
ts.timestamp_date = ticks / ISC_TICKS_PER_DAY;
ts.timestamp_time = ticks % ISC_TICKS_PER_DAY;
return ts;
}
} // namespace

View File

@ -59,6 +59,9 @@ public:
static const ISC_DATE MIN_DATE = -678575; // 01.01.0001
static const ISC_DATE MAX_DATE = 2973483; // 31.12.9999
static const SINT64 SECONDS_PER_DAY = 24 * 60 * 60;
static const SINT64 ISC_TICKS_PER_DAY = SECONDS_PER_DAY * ISC_TIME_SECONDS_PRECISION;
private:
static const ISC_DATE BAD_DATE = MAX_SLONG;
static const ISC_TIME BAD_TIME = MAX_ULONG;
@ -164,6 +167,13 @@ public:
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
// Time zone routines
static bool is_valid_tz_offset(int tzh, unsigned tzm) throw();
static ISC_TIME timeTzAtZone(const ISC_TIME_TZ& timeTz, ISC_SHORT zone) throw();
static ISC_TIMESTAMP timeStampTzAtZone(const ISC_TIMESTAMP_TZ& timeStampTz, ISC_SHORT zone) throw();
private:
ISC_TIMESTAMP mValue;

View File

@ -57,4 +57,17 @@ TimeStamp TimeStamp::getCurrentTimeStamp()
return result;
}
//// FIXME: Windows and others ports.
SSHORT TimeStamp::getCurrentTimeZone()
{
time_t rawtime;
time(&rawtime);
struct tm tm1;
if (!localtime_r(&rawtime, &tm1))
report_error("localtime_r");
return tm1.tm_gmtoff / 60;
}
} // namespace

View File

@ -63,6 +63,9 @@ public:
// Return current timestamp value
static TimeStamp getCurrentTimeStamp();
// Return current time zone
static SSHORT getCurrentTimeZone();
// Assign current date/time to the timestamp
void validate()
{

View File

@ -667,13 +667,30 @@ extern "C" int remove(const char* path);
/* data type definitions */
#ifndef ISC_TIMESTAMP_DEFINED
typedef SLONG ISC_DATE;
typedef ULONG ISC_TIME;
struct ISC_TIME_TZ
{
ISC_TIME time_time;
ISC_SHORT time_displacement;
};
struct ISC_TIMESTAMP
{
ISC_DATE timestamp_date;
ISC_TIME timestamp_time;
};
struct ISC_TIMESTAMP_TZ
{
ISC_DATE timestamp_date;
ISC_TIME timestamp_time;
ISC_SHORT timestamp_displacement;
};
#define ISC_TIMESTAMP_DEFINED
#endif /* ISC_TIMESTAMP_DEFINED */

View File

@ -128,18 +128,10 @@ using namespace Firebird;
// AP,2012: Looks like there is no need making len zero, but I keep old define for a reference.
// {if (len) {memcpy(to, from, len); from += len; to += len; len = 0;} }
enum EXPECT_DATETIME
{
expect_timestamp,
expect_sql_date,
expect_sql_time
};
static void datetime_to_text(const dsc*, dsc*, Callbacks*);
static void float_to_text(const dsc*, dsc*, Callbacks*);
static void decimal_float_to_text(const dsc*, dsc*, DecimalStatus, Callbacks*);
static void integer_to_text(const dsc*, dsc*, Callbacks*);
static void string_to_datetime(const dsc*, GDS_TIMESTAMP*, const EXPECT_DATETIME, ErrorFunction);
static SINT64 hex_to_value(const char*& string, const char* end);
static void localError(const Firebird::Arg::StatusVector&);
@ -485,9 +477,9 @@ static void integer_to_text(const dsc* from, dsc* to, Callbacks* cb)
}
static void string_to_datetime(const dsc* desc,
GDS_TIMESTAMP* date,
const EXPECT_DATETIME expect_type, ErrorFunction err)
void CVT_string_to_datetime(const dsc* desc,
ISC_TIMESTAMP_TZ* date, bool* timezone_present,
const EXPECT_DATETIME expect_type, Callbacks* cb)
{
/**************************************
*
@ -551,32 +543,36 @@ static void string_to_datetime(const dsc* desc,
const int ENGLISH_MONTH = -1;
const int SPECIAL = -2; // CVC: I see it set, but never tested.
const unsigned int position_tzh = 7;
const unsigned int position_tzm = 8;
unsigned int position_year = 0;
unsigned int position_month = 1;
unsigned int position_day = 2;
bool have_english_month = false;
bool dot_separator_seen = false;
int tz_sign = 1;
VaryStr<100> buffer; // arbitrarily large
const char* p = NULL;
const USHORT length = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), 0, err);
const USHORT length = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), 0, cb->err);
const char* const end = p + length;
USHORT n, components[7];
int description[7];
USHORT n, components[9];
int description[9];
memset(components, 0, sizeof(components));
memset(description, 0, sizeof(description));
// Parse components
// The 7 components are Year, Month, Day, Hours, Minutes, Seconds, Thou
// The 8 components are Year, Month, Day, Hours, Minutes, Seconds, Thou, TZH, TZM
// The first 3 can be in any order
const int start_component = (expect_type == expect_sql_time) ? 3 : 0;
const int start_component = (expect_type == expect_sql_time || expect_type == expect_sql_time_tz) ? 3 : 0;
int parsed_component = -1;
int i;
for (i = start_component; i < 7; i++)
{
for (i = start_component; i < 9; i++)
{
// Skip leading blanks. If we run out of characters, we're done
// with parse.
@ -585,6 +581,8 @@ static void string_to_datetime(const dsc* desc,
if (p == end)
break;
parsed_component = i;
// Handle digit or character strings
TEXT c = UPPER7(*p);
@ -617,7 +615,7 @@ static void string_to_datetime(const dsc* desc,
// Insist on at least 3 characters for month names
if (t - temp < 3)
{
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
return;
}
@ -647,17 +645,17 @@ static void string_to_datetime(const dsc* desc,
while (++p < end)
{
if (*p != ' ' && *p != '\t' && *p != '\0')
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
}
// fetch the current datetime
*date = Firebird::TimeStamp::getCurrentTimeStamp().value();
*(ISC_TIMESTAMP*) date = Firebird::TimeStamp::getCurrentTimeStamp().value();
if (strcmp(temp, NOW) == 0)
return;
if (expect_type == expect_sql_time)
if (expect_type == expect_sql_time || expect_type == expect_sql_time_tz)
{
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
return;
}
date->timestamp_time = 0;
@ -673,7 +671,7 @@ static void string_to_datetime(const dsc* desc,
date->timestamp_date--;
return;
}
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
return;
}
}
@ -685,7 +683,7 @@ static void string_to_datetime(const dsc* desc,
else
{
// Not a digit and not a letter - must be punctuation
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
return;
}
@ -699,11 +697,22 @@ static void string_to_datetime(const dsc* desc,
break;
// Grab a separator character
if (*p == '/' || *p == '-' || *p == ',' || *p == ':')
if (*p == '/' || *p == ',' || *p == ':' || (*p == '-' && i <= 2))
{
p++;
continue;
}
if (i >= 3 && i <= 6 && (*p == '+' || *p == '-'))
{
if (*p == '-')
tz_sign = -1;
i = position_tzh - 1;
p++;
continue;
}
if (*p == '.')
{
if (i <= 1)
@ -716,14 +725,14 @@ static void string_to_datetime(const dsc* desc,
// User must provide at least 2 components
if (i - start_component < 1)
{
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
return;
}
// Dates cannot have a Time portion
if (expect_type == expect_sql_date && i > 2)
{
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
return;
}
@ -732,16 +741,29 @@ static void string_to_datetime(const dsc* desc,
{
if (*p != ' ' && *p != '\t' && *p != '\0')
{
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
return;
}
++p;
}
if (timezone_present)
*timezone_present = parsed_component >= position_tzh && expect_type != expect_sql_date;
if (parsed_component < position_tzh && expect_type != expect_sql_date)
{
int session_tz = cb->getSessionTimeZone();
tz_sign = session_tz < 0 ? -1 : 1;
session_tz = session_tz < 0 ? -session_tz : session_tz;
components[position_tzh] = session_tz / 60;
components[position_tzm] = session_tz % 60;
}
tm times;
memset(&times, 0, sizeof(times));
if (expect_type != expect_sql_time)
if (expect_type != expect_sql_time && expect_type != expect_sql_time_tz)
{
// Figure out what format the user typed the date in
@ -788,7 +810,7 @@ static void string_to_datetime(const dsc* desc,
description[position_month] > 2 || description[position_month] == 0 ||
description[position_day] > 2 || description[position_day] <= 0)
{
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
return;
}
@ -840,7 +862,7 @@ static void string_to_datetime(const dsc* desc,
description[5] > 2 ||
description[6] > -ISC_TIME_SECONDS_PRECISION_SCALE))
{
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
}
// convert day/month/year to Julian and validate result
@ -853,21 +875,23 @@ static void string_to_datetime(const dsc* desc,
switch (expect_type)
{
case expect_sql_date:
err(Arg::Gds(isc_date_range_exceeded));
cb->err(Arg::Gds(isc_date_range_exceeded));
break;
case expect_sql_time:
err(Arg::Gds(isc_time_range_exceeded));
case expect_sql_time_tz:
cb->err(Arg::Gds(isc_time_range_exceeded));
break;
case expect_timestamp:
err(Arg::Gds(isc_datetime_range_exceeded));
case expect_timestamp_tz:
cb->err(Arg::Gds(isc_datetime_range_exceeded));
break;
default: // this should never happen!
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
break;
}
}
if (expect_type != expect_sql_time)
if (expect_type != expect_sql_time && expect_type != expect_sql_time_tz)
{
tm times2;
ts.decode(&times2);
@ -879,17 +903,54 @@ static void string_to_datetime(const dsc* desc,
times.tm_min != times2.tm_min ||
times.tm_sec != times2.tm_sec)
{
CVT_conversion_error(desc, err);
CVT_conversion_error(desc, cb->err);
}
}
*date = ts.value();
*(ISC_TIMESTAMP*) date = ts.value();
// Convert fraction of seconds
while (description[6]++ < -ISC_TIME_SECONDS_PRECISION_SCALE)
components[6] *= 10;
date->timestamp_time += components[6];
if (expect_type != expect_sql_date &&
!TimeStamp::is_valid_tz_offset(components[position_tzh], components[position_tzm]))
{
CVT_conversion_error(desc, cb->err);
}
if (expect_type == expect_sql_time_tz || expect_type == expect_timestamp_tz)
date->timestamp_displacement = tz_sign * (components[position_tzh] * 60 + components[position_tzm]);
else if (expect_type == expect_sql_time)
{
SINT64 ticks = ((ISC_TIMESTAMP*) date)->timestamp_time;
ticks -=
((tz_sign * (components[position_tzh] * 60 + components[position_tzm]) * 60) -
(cb->getSessionTimeZone() * 60)) *
ISC_TIME_SECONDS_PRECISION;
// Make the result positive
while (ticks < 0)
ticks += TimeStamp::ISC_TICKS_PER_DAY;
// And make it in the range of values for a day
ticks %= TimeStamp::ISC_TICKS_PER_DAY;
((ISC_TIMESTAMP*) date)->timestamp_time = (ISC_TIME) ticks;
}
else if (expect_type == expect_timestamp)
{
SINT64 ticks = ((SINT64) date->timestamp_date) * TimeStamp::ISC_TICKS_PER_DAY + (SINT64) date->timestamp_time;
ticks -=
((tz_sign * (components[position_tzh] * 60 + components[position_tzm]) * 60) -
(cb->getSessionTimeZone() * 60)) *
ISC_TIME_SECONDS_PRECISION;
date->timestamp_date = ticks / TimeStamp::ISC_TICKS_PER_DAY;
date->timestamp_time = ticks % TimeStamp::ISC_TICKS_PER_DAY;
}
}
@ -1448,9 +1509,9 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_cstring:
case dtype_text:
{
GDS_TIMESTAMP date;
string_to_datetime(from, &date, expect_timestamp, cb->err);
*((GDS_TIMESTAMP *) to->dsc_address) = date;
ISC_TIMESTAMP_TZ date;
CVT_string_to_datetime(from, &date, NULL, expect_timestamp, cb);
*((GDS_TIMESTAMP*) to->dsc_address) = *(GDS_TIMESTAMP*) &date;
}
return;
@ -1465,6 +1526,79 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
((GDS_TIMESTAMP*) (to->dsc_address))->timestamp_date = cb->getCurDate();
return;
case dtype_sql_time_tz:
{
ISC_TIMESTAMP_TZ tsTz;
tsTz.timestamp_date = cb->getCurDate(); //// TODO: correct?
tsTz.timestamp_time = ((ISC_TIME_TZ*) from->dsc_address)->time_time;
tsTz.timestamp_displacement = ((ISC_TIME_TZ*) from->dsc_address)->time_displacement;
*(ISC_TIMESTAMP*) to->dsc_address =
TimeStamp::timeStampTzAtZone(tsTz, cb->getSessionTimeZone());
return;
}
case dtype_timestamp_tz:
*(ISC_TIMESTAMP*) to->dsc_address =
TimeStamp::timeStampTzAtZone(*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb->getSessionTimeZone());
return;
default:
fb_assert(false); // Fall into ...
case dtype_short:
case dtype_long:
case dtype_int64:
case dtype_dbkey:
case dtype_quad:
case dtype_real:
case dtype_double:
case dtype_boolean:
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
CVT_conversion_error(from, cb->err);
break;
}
break;
case dtype_timestamp_tz:
switch (from->dsc_dtype)
{
case dtype_varying:
case dtype_cstring:
case dtype_text:
{
ISC_TIMESTAMP_TZ date;
CVT_string_to_datetime(from, &date, NULL, expect_timestamp_tz, cb);
*((ISC_TIMESTAMP_TZ*) to->dsc_address) = date;
}
return;
case dtype_sql_time:
// SQL: source => TIMESTAMP WITHOUT TIME ZONE => TIMESTAMP WITH TIME ZONE
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_date = cb->getCurDate();
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_time = ((ISC_TIME_TZ*) from->dsc_address)->time_time;
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_displacement = cb->getSessionTimeZone();
return;
case dtype_sql_time_tz:
// SQL: Copy date fields from CURRENT_DATE and time and time zone fields from the source.
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_date = cb->getCurDate();
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_time = ((ISC_TIME_TZ*) from->dsc_address)->time_time;
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_displacement =
((ISC_TIME_TZ*) from->dsc_address)->time_displacement;
return;
case dtype_sql_date:
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_date = *(GDS_DATE*) from->dsc_address;
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_time = 0;
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_displacement = cb->getSessionTimeZone();
return;
case dtype_timestamp:
*(ISC_TIMESTAMP*) to->dsc_address = *(ISC_TIMESTAMP*) from->dsc_address;
((ISC_TIMESTAMP_TZ*) to->dsc_address)->timestamp_displacement = cb->getSessionTimeZone();
return;
default:
fb_assert(false); // Fall into ...
case dtype_short:
@ -1490,8 +1624,8 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_cstring:
case dtype_text:
{
GDS_TIMESTAMP date;
string_to_datetime(from, &date, expect_sql_date, cb->err);
ISC_TIMESTAMP_TZ date;
CVT_string_to_datetime(from, &date, NULL, expect_sql_date, cb);
*((GDS_DATE *) to->dsc_address) = date.timestamp_date;
}
return;
@ -1500,9 +1634,15 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
*((GDS_DATE *) to->dsc_address) = ((GDS_TIMESTAMP *) from->dsc_address)->timestamp_date;
return;
case dtype_timestamp_tz:
*(GDS_DATE*) to->dsc_address = TimeStamp::timeStampTzAtZone(
*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb->getSessionTimeZone()).timestamp_date;
return;
default:
fb_assert(false); // Fall into ...
case dtype_sql_time:
case dtype_sql_time_tz:
case dtype_short:
case dtype_long:
case dtype_int64:
@ -1526,16 +1666,76 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_cstring:
case dtype_text:
{
GDS_TIMESTAMP date;
string_to_datetime(from, &date, expect_sql_time, cb->err);
ISC_TIMESTAMP_TZ date;
CVT_string_to_datetime(from, &date, NULL, expect_sql_time, cb);
*((GDS_TIME *) to->dsc_address) = date.timestamp_time;
}
return;
case dtype_sql_time_tz:
*(ISC_TIME*) to->dsc_address =
TimeStamp::timeTzAtZone(*(ISC_TIME_TZ*) from->dsc_address, cb->getSessionTimeZone());
return;
case dtype_timestamp:
*((GDS_TIME *) to->dsc_address) = ((GDS_TIMESTAMP *) from->dsc_address)->timestamp_time;
return;
case dtype_timestamp_tz:
*(GDS_TIME*) to->dsc_address = TimeStamp::timeStampTzAtZone(
*(ISC_TIMESTAMP_TZ*) from->dsc_address, cb->getSessionTimeZone()).timestamp_time;
return;
default:
fb_assert(false); // Fall into ...
case dtype_sql_date:
case dtype_short:
case dtype_long:
case dtype_int64:
case dtype_dbkey:
case dtype_quad:
case dtype_real:
case dtype_double:
case dtype_boolean:
case dtype_dec64:
case dtype_dec128:
case dtype_dec_fixed:
CVT_conversion_error(from, cb->err);
break;
}
break;
case dtype_sql_time_tz:
switch (from->dsc_dtype)
{
case dtype_varying:
case dtype_cstring:
case dtype_text:
{
ISC_TIMESTAMP_TZ date;
CVT_string_to_datetime(from, &date, NULL, expect_sql_time_tz, cb);
((ISC_TIME_TZ*) to->dsc_address)->time_time = date.timestamp_time;
((ISC_TIME_TZ*) to->dsc_address)->time_displacement = date.timestamp_displacement;
}
return;
case dtype_sql_time:
*(ISC_TIME*) to->dsc_address = *(ISC_TIME*) from->dsc_address;
((ISC_TIME_TZ*) to->dsc_address)->time_displacement = cb->getSessionTimeZone();
return;
case dtype_timestamp:
((ISC_TIME_TZ*) to->dsc_address)->time_time = ((GDS_TIMESTAMP*) from->dsc_address)->timestamp_time;
((ISC_TIME_TZ*) to->dsc_address)->time_displacement = cb->getSessionTimeZone();
return;
case dtype_timestamp_tz:
// SQL: Copy time and time zone fields from the source.
((ISC_TIME_TZ*) to->dsc_address)->time_time = ((ISC_TIMESTAMP_TZ*) from->dsc_address)->timestamp_time;
((ISC_TIME_TZ*) to->dsc_address)->time_displacement =
((ISC_TIMESTAMP_TZ*) from->dsc_address)->timestamp_displacement;
return;
default:
fb_assert(false); // Fall into ...
case dtype_sql_date:
@ -1745,7 +1945,9 @@ void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* c
case dtype_sql_date:
case dtype_sql_time:
case dtype_sql_time_tz:
case dtype_timestamp:
case dtype_timestamp_tz:
datetime_to_text(from, to, cb);
return;
@ -2000,8 +2202,9 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
switch (from->dsc_dtype)
{
case dtype_sql_time:
Firebird::TimeStamp::decode_time(*(GDS_TIME *) from->dsc_address,
&times.tm_hour, &times.tm_min, &times.tm_sec, &fractions);
case dtype_sql_time_tz:
Firebird::TimeStamp::decode_time(*(GDS_TIME*) from->dsc_address,
&times.tm_hour, &times.tm_min, &times.tm_sec, &fractions);
break;
case dtype_sql_date:
@ -2009,9 +2212,9 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
break;
case dtype_timestamp:
case dtype_timestamp_tz:
cb->isVersion4(version4); // Used in the conversion to text some lines below.
Firebird::TimeStamp::decode_timestamp(*(GDS_TIMESTAMP *) from->dsc_address,
&times, &fractions);
Firebird::TimeStamp::decode_timestamp(*(GDS_TIMESTAMP*) from->dsc_address, &times, &fractions);
break;
default:
@ -2020,14 +2223,28 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
break;
}
ISC_SHORT timezone;
switch (from->dsc_dtype)
{
case dtype_sql_time_tz:
timezone = ((ISC_TIME_TZ*) from->dsc_address)->time_displacement;
break;
case dtype_timestamp_tz:
timezone = ((ISC_TIMESTAMP_TZ*) from->dsc_address)->timestamp_displacement;
break;
}
// Decode the timestamp into human readable terms
TEXT temp[30]; // yyyy-mm-dd hh:mm:ss.tttt OR dd-MMM-yyyy hh:mm:ss.tttt
//// FIXME: Put space before time zone.
TEXT temp[36]; // yyyy-mm-dd hh:mm:ss.tttt+th:tm OR dd-MMM-yyyy hh:mm:ss.tttt+th:tm
TEXT* p = temp;
// Make a textual date for data types that include it
if (from->dsc_dtype != dtype_sql_time)
if (from->dsc_dtype != dtype_sql_time && from->dsc_dtype != dtype_sql_time_tz)
{
if (from->dsc_dtype == dtype_sql_date || !version4)
{
@ -2041,20 +2258,21 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
times.tm_mday,
FB_LONG_MONTHS_UPPER[times.tm_mon], times.tm_year + 1900);
}
while (*p)
p++;
}
// Put in a space to separate date & time components
if (from->dsc_dtype == dtype_timestamp && !version4)
if ((from->dsc_dtype == dtype_timestamp || from->dsc_dtype == dtype_timestamp_tz) && !version4)
*p++ = ' ';
// Add the time part for data types that include it
if (from->dsc_dtype != dtype_sql_date)
{
if (from->dsc_dtype == dtype_sql_time || !version4)
if (from->dsc_dtype == dtype_sql_time || from->dsc_dtype == dtype_sql_time_tz || !version4)
{
sprintf(p, "%2.2d:%2.2d:%2.2d.%4.4d",
times.tm_hour, times.tm_min, times.tm_sec, fractions);
@ -2065,6 +2283,19 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
sprintf(p, " %d:%.2d:%.2d.%.4d",
times.tm_hour, times.tm_min, times.tm_sec, fractions);
}
while (*p)
p++;
}
if (from->dsc_dtype == dtype_sql_time_tz || from->dsc_dtype == dtype_timestamp_tz)
{
*p++ = timezone < 0 ? '-' : '+';
if (timezone < 0)
timezone = -timezone;
sprintf(p, "%2.2d:%2.2d", timezone / 60, timezone % 60);
while (*p)
p++;
}
@ -2073,11 +2304,12 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb)
dsc desc;
MOVE_CLEAR(&desc, sizeof(desc));
desc.dsc_address = (UCHAR *) temp;
desc.dsc_address = (UCHAR*) temp;
desc.dsc_dtype = dtype_text;
desc.dsc_ttype() = ttype_ascii;
desc.dsc_length = (p - temp);
if (from->dsc_dtype == dtype_timestamp && version4)
if ((from->dsc_dtype == dtype_timestamp || from->dsc_dtype == dtype_timestamp_tz) && version4)
{
// Prior to BLR Version5, when a timestamp is converted to a string it
// is silently truncated if the destination string is not large enough
@ -3140,6 +3372,7 @@ namespace
virtual void validateLength(Jrd::CharSet* toCharset, SLONG toLength, const UCHAR* start,
const USHORT to_size);
virtual SLONG getCurDate();
virtual int getSessionTimeZone();
virtual void isVersion4(bool& v4);
};
@ -3172,6 +3405,11 @@ namespace
return TimeStamp::getCurrentTimeStamp().value().timestamp_date;
}
int CommonCallbacks::getSessionTimeZone()
{
return 0;
}
void CommonCallbacks::isVersion4(bool& /*v4*/)
{
}

View File

@ -61,12 +61,22 @@ public:
virtual void validateLength(Jrd::CharSet* toCharset, SLONG toLength, const UCHAR* start,
const USHORT to_size) = 0;
virtual SLONG getCurDate() = 0;
virtual int getSessionTimeZone() = 0;
virtual void isVersion4(bool& v4) = 0;
public:
const ErrorFunction err;
};
enum EXPECT_DATETIME
{
expect_timestamp,
expect_timestamp_tz,
expect_sql_date,
expect_sql_time,
expect_sql_time_tz
};
}
@ -87,5 +97,6 @@ USHORT CVT_get_string_ptr(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird:
USHORT CVT_get_string_ptr_common(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, Firebird::Callbacks*);
SINT64 CVT_get_int64(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction);
SQUAD CVT_get_quad(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction);
void CVT_string_to_datetime(const dsc*, ISC_TIMESTAMP_TZ*, bool*, const Firebird::EXPECT_DATETIME, Firebird::Callbacks*);
#endif //COMMON_CVT_H

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,7 @@ inline bool DTYPE_IS_TEXT(UCHAR d)
inline bool DTYPE_IS_DATE(UCHAR t)
{
return t >= dtype_sql_date && t <= dtype_timestamp;
return (t >= dtype_sql_date && t <= dtype_timestamp) || t == dtype_sql_time_tz || t == dtype_timestamp_tz;
}
// DTYPE_IS_BLOB includes both BLOB and ARRAY since array's are implemented over blobs.
@ -158,7 +158,28 @@ typedef struct dsc
bool isDateTime() const
{
return dsc_dtype >= dtype_sql_date && dsc_dtype <= dtype_timestamp;
return (dsc_dtype >= dtype_sql_date && dsc_dtype <= dtype_timestamp) ||
dsc_dtype == dtype_sql_time_tz || dsc_dtype == dtype_timestamp_tz;
}
bool isDateTimeTz() const
{
return dsc_dtype == dtype_sql_time_tz || dsc_dtype == dtype_timestamp_tz;
}
bool isDate() const
{
return dsc_dtype == dtype_sql_date;
}
bool isTime() const
{
return dsc_dtype == dtype_sql_time || dsc_dtype == dtype_sql_time_tz;
}
bool isTimeStamp() const
{
return dsc_dtype == dtype_timestamp || dsc_dtype == dtype_timestamp_tz;
}
bool isDecFloat() const
@ -373,6 +394,15 @@ typedef struct dsc
dsc_address = (UCHAR*) address;
}
void makeTimeTz(ISC_TIME_TZ* address = NULL)
{
clear();
dsc_dtype = dtype_sql_time_tz;
dsc_length = sizeof(ISC_TIME_TZ);
dsc_scale = 0;
dsc_address = (UCHAR*) address;
}
void makeTimestamp(GDS_TIMESTAMP* address = NULL)
{
clear();
@ -382,6 +412,15 @@ typedef struct dsc
dsc_address = (UCHAR*) address;
}
void makeTimestampTz(ISC_TIMESTAMP_TZ* address = NULL)
{
clear();
dsc_dtype = dtype_timestamp_tz;
dsc_length = sizeof(ISC_TIMESTAMP_TZ);
dsc_scale = 0;
dsc_address = (UCHAR*) address;
}
void makeVarying(USHORT length, USHORT ttype, UCHAR* address = NULL)
{
clear();

View File

@ -64,7 +64,9 @@
#define dtype_dec64 22
#define dtype_dec128 23
#define dtype_dec_fixed 24
#define DTYPE_TYPE_MAX 25
#define dtype_sql_time_tz 25
#define dtype_timestamp_tz 26
#define DTYPE_TYPE_MAX 27
#define ISC_TIME_SECONDS_PRECISION 10000
#define ISC_TIME_SECONDS_PRECISION_SCALE (-4)

View File

@ -1524,6 +1524,10 @@ UCHAR sqlTypeToDscType(SSHORT sqlType)
return dtype_dec128;
case SQL_DEC_FIXED:
return dtype_dec_fixed;
case SQL_TIME_TZ:
return dtype_sql_time_tz;
case SQL_TIMESTAMP_TZ:
return dtype_timestamp_tz;
default:
return dtype_unknown;
}

View File

@ -241,6 +241,14 @@ bool_t xdr_datum( XDR* xdrs, const dsc* desc, UCHAR* buffer)
return FALSE;
break;
case dtype_sql_time_tz:
fb_assert(desc->dsc_length >= sizeof(SLONG) + sizeof(SSHORT));
if (!xdr_long(xdrs, reinterpret_cast<SLONG*>(p)))
return FALSE;
if (!xdr_short(xdrs, reinterpret_cast<SSHORT*>(p + sizeof(SLONG))))
return FALSE;
break;
case dtype_real:
fb_assert(desc->dsc_length >= sizeof(float));
if (!xdr_float(xdrs, reinterpret_cast<float*>(p)))
@ -274,6 +282,16 @@ bool_t xdr_datum( XDR* xdrs, const dsc* desc, UCHAR* buffer)
return FALSE;
break;
case dtype_timestamp_tz:
fb_assert(desc->dsc_length >= 2 * sizeof(SLONG) + 1);
if (!xdr_long(xdrs, &((SLONG*) p)[0]))
return FALSE;
if (!xdr_long(xdrs, &((SLONG*) p)[1]))
return FALSE;
if (!xdr_short(xdrs, reinterpret_cast<SSHORT*>(p + 2 * sizeof(SLONG))))
return FALSE;
break;
case dtype_int64:
fb_assert(desc->dsc_length >= sizeof(SINT64));
if (!xdr_hyper(xdrs, reinterpret_cast<SINT64*>(p)))

File diff suppressed because it is too large Load Diff

View File

@ -1039,6 +1039,60 @@ public:
};
class LocalTimeNode : public TypedNode<ValueExprNode, ExprNode::TYPE_LOCAL_TIME>
{
public:
LocalTimeNode(MemoryPool& pool, unsigned aPrecision)
: TypedNode<ValueExprNode, ExprNode::TYPE_LOCAL_TIME>(pool),
precision(aPrecision)
{
}
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
virtual void setParameterName(dsql_par* parameter) const;
virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
public:
unsigned precision;
};
class LocalTimeStampNode : public TypedNode<ValueExprNode, ExprNode::TYPE_LOCAL_TIMESTAMP>
{
public:
LocalTimeStampNode(MemoryPool& pool, unsigned aPrecision)
: TypedNode<ValueExprNode, ExprNode::TYPE_LOCAL_TIMESTAMP>(pool),
precision(aPrecision)
{
}
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
virtual Firebird::string internalPrint(NodePrinter& printer) const;
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
virtual void setParameterName(dsql_par* parameter) const;
virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
virtual void make(DsqlCompilerScratch* dsqlScratch, dsc* desc);
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const;
virtual ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb);
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
public:
unsigned precision;
};
class NegateNode : public TypedNode<ValueExprNode, ExprNode::TYPE_NEGATE>
{
public:

View File

@ -492,6 +492,8 @@ public:
TYPE_GEN_ID,
TYPE_INTERNAL_INFO,
TYPE_LITERAL,
TYPE_LOCAL_TIME,
TYPE_LOCAL_TIMESTAMP,
TYPE_MAP,
TYPE_NEGATE,
TYPE_NULL,

View File

@ -8392,6 +8392,76 @@ void SetSessionNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*tra
//--------------------
void SetTimeZoneNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** /*traHandle*/) const
{
Attachment* const attachment = tdbb->getAttachment();
if (local)
attachment->att_current_timezone = attachment->att_original_timezone;
else
{
const char* end = str.end();
const char* p = str.begin();
skipSpaces(p, end);
int sign = 1;
if (*p == '-' || *p == '+')
{
sign = *p == '-' ? -1 : 1;
++p;
skipSpaces(p, end);
}
int tzh = parseNumber(p, end);
skipSpaces(p, end);
unsigned tzm = 0;
if (*p == ':')
{
++p;
skipSpaces(p, end);
tzm = (unsigned) parseNumber(p, end);
skipSpaces(p, end);
}
if (p != end)
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone offset"); //// TODO:
if (!TimeStamp::is_valid_tz_offset(tzh * sign, tzm))
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone offset"); //// TODO:
attachment->att_current_timezone = (tzh * 60 + tzm) * sign;
}
}
void SetTimeZoneNode::skipSpaces(const char*& p, const char* end) const
{
while (p < end && (*p == ' ' || *p == '\t'))
++p;
}
int SetTimeZoneNode::parseNumber(const char*& p, const char* end) const
{
const char* start = p;
int n = 0;
while (p < end && *p >= '0' && *p <= '9')
n = n * 10 + *p++ - '0';
if (p == start)
status_exception::raise(Arg::Gds(isc_random) << "Invalid time zone offset"); //// TODO:
return n;
}
//--------------------
StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
{
thread_db* tdbb = JRD_get_thread_data(); // necessary?

View File

@ -1771,6 +1771,46 @@ public:
};
class SetTimeZoneNode : public SessionManagementNode
{
public:
explicit SetTimeZoneNode(MemoryPool& pool, const Firebird::string& aStr)
: SessionManagementNode(pool),
str(pool, aStr),
local(false)
{
}
explicit SetTimeZoneNode(MemoryPool& pool)
: SessionManagementNode(pool),
str(pool),
local(true)
{
}
public:
virtual Firebird::string internalPrint(NodePrinter& printer) const
{
SessionManagementNode::internalPrint(printer);
NODE_PRINT(printer, str);
NODE_PRINT(printer, local);
return "SetTimeZoneNode";
}
virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** traHandle) const;
private:
void skipSpaces(const char*& p, const char* end) const;
int parseNumber(const char*& p, const char* end) const;
public:
Firebird::string str;
bool local;
};
class UpdateOrInsertNode : public TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_UPDATE_OR_INSERT>
{
public:

View File

@ -60,7 +60,9 @@ const USHORT blr_dtypes[] = {
blr_bool, // dtype_boolean
blr_dec64, // dtype_dec64
blr_dec128, // dtype_dec128
blr_dec_fixed // dtype_dec_fixed
blr_dec_fixed, // dtype_dec_fixed
blr_sql_time_tz, // dtype_sql_time_tz
blr_timestamp_tz // dtype_timestamp_tz
};
bool DDL_ids(const Jrd::DsqlCompilerScratch*);

View File

@ -408,10 +408,18 @@ void GEN_descriptor( DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool tex
dsqlScratch->appendUChar(blr_sql_time);
break;
case dtype_sql_time_tz:
dsqlScratch->appendUChar(blr_sql_time_tz);
break;
case dtype_timestamp:
dsqlScratch->appendUChar(blr_timestamp);
break;
case dtype_timestamp_tz:
dsqlScratch->appendUChar(blr_timestamp_tz);
break;
case dtype_array:
dsqlScratch->appendUChar(blr_quad);
dsqlScratch->appendUChar(0);

View File

@ -53,9 +53,9 @@
#include "../jrd/jrd.h"
#include "../jrd/ods.h"
#include "../jrd/ini.h"
#include "../jrd/cvt_proto.h"
#include "../jrd/scl_proto.h"
#include "../common/dsc_proto.h"
#include "../common/cvt.h"
#include "../yvalve/why_proto.h"
#include "../common/config/config.h"
#include "../common/StatusArg.h"
@ -145,23 +145,26 @@ ValueExprNode* MAKE_constant(const char* str, dsql_constant_type numeric_flag)
{
// Setup the constant's descriptor
EXPECT_DATETIME expect;
switch (numeric_flag)
{
case CONSTANT_DATE:
literal->litDesc.dsc_dtype = dtype_sql_date;
break;
case CONSTANT_TIME:
literal->litDesc.dsc_dtype = dtype_sql_time;
break;
case CONSTANT_TIMESTAMP:
literal->litDesc.dsc_dtype = dtype_timestamp;
break;
case CONSTANT_DATE:
expect = expect_sql_date;
break;
case CONSTANT_TIME:
expect = expect_sql_time_tz;
break;
case CONSTANT_TIMESTAMP:
expect = expect_timestamp_tz;
break;
default:
fb_assert(false);
return NULL;
}
literal->litDesc.dsc_sub_type = 0;
literal->litDesc.dsc_scale = 0;
literal->litDesc.dsc_length = type_lengths[literal->litDesc.dsc_dtype];
literal->litDesc.dsc_address =
FB_NEW_POOL(*tdbb->getDefaultPool()) UCHAR[literal->litDesc.dsc_length];
// Set up a descriptor to point to the string
@ -175,7 +178,63 @@ ValueExprNode* MAKE_constant(const char* str, dsql_constant_type numeric_flag)
// Now invoke the string_to_date/time/timestamp routines
CVT_move(&tmp, &literal->litDesc, tdbb->getAttachment()->att_dec_status, ERRD_post);
ISC_TIMESTAMP_TZ ts;
bool tz;
CVT_string_to_datetime(&tmp, &ts, &tz, expect, &EngineCallbacks::instance);
switch (numeric_flag)
{
case CONSTANT_DATE:
literal->litDesc.dsc_dtype = dtype_sql_date;
break;
case CONSTANT_TIME:
literal->litDesc.dsc_dtype = tz ? dtype_sql_time_tz : dtype_sql_time;
break;
case CONSTANT_TIMESTAMP:
literal->litDesc.dsc_dtype = tz ? dtype_timestamp_tz : dtype_timestamp;
break;
}
literal->litDesc.dsc_sub_type = 0;
literal->litDesc.dsc_scale = 0;
literal->litDesc.dsc_length = type_lengths[literal->litDesc.dsc_dtype];
literal->litDesc.dsc_address =
FB_NEW_POOL(*tdbb->getDefaultPool()) UCHAR[literal->litDesc.dsc_length];
switch (numeric_flag)
{
case CONSTANT_DATE:
*(ISC_DATE*) literal->litDesc.dsc_address = ts.timestamp_date;
break;
case CONSTANT_TIME:
if (tz)
{
((ISC_TIME_TZ*) literal->litDesc.dsc_address)->time_time = ts.timestamp_time;
((ISC_TIME_TZ*) literal->litDesc.dsc_address)->time_displacement = ts.timestamp_displacement;
}
else
*(ISC_TIME*) literal->litDesc.dsc_address = ts.timestamp_time;
break;
case CONSTANT_TIMESTAMP:
if (tz)
{
((ISC_TIMESTAMP_TZ*) literal->litDesc.dsc_address)->timestamp_date = ts.timestamp_date;
((ISC_TIMESTAMP_TZ*) literal->litDesc.dsc_address)->timestamp_time = ts.timestamp_time;
((ISC_TIMESTAMP_TZ*) literal->litDesc.dsc_address)->timestamp_displacement =
ts.timestamp_displacement;
}
else
{
((ISC_TIMESTAMP*) literal->litDesc.dsc_address)->timestamp_date = ts.timestamp_date;
((ISC_TIMESTAMP*) literal->litDesc.dsc_address)->timestamp_time = ts.timestamp_time;
}
break;
}
break;
}

View File

@ -604,6 +604,9 @@ using namespace Firebird;
%token <metaNamePtr> IDLE
%token <metaNamePtr> INVOKER
%token <metaNamePtr> LAST_DAY
%token <metaNamePtr> LOCAL
%token <metaNamePtr> LOCALTIME
%token <metaNamePtr> LOCALTIMESTAMP
%token <metaNamePtr> MESSAGE
%token <metaNamePtr> NATIVE
%token <metaNamePtr> NORMALIZE_DECFLOAT
@ -624,11 +627,14 @@ using namespace Firebird;
%token <metaNamePtr> SQL
%token <metaNamePtr> SYSTEM
%token <metaNamePtr> TIES
%token <metaNamePtr> TIMEZONE_HOUR
%token <metaNamePtr> TIMEZONE_MINUTE
%token <metaNamePtr> TOTALORDER
%token <metaNamePtr> TRAPS
%token <metaNamePtr> UNBOUNDED
%token <metaNamePtr> VARBINARY
%token <metaNamePtr> WINDOW
%token <metaNamePtr> ZONE
%token <metaNamePtr> CONSISTENCY
%token <metaNamePtr> RDB_GET_TRANSACTION_CN
@ -851,6 +857,7 @@ mng_statement
| session_statement { $$ = $1; }
| set_role { $$ = $1; }
| session_reset { $$ = $1; }
| set_time_zone { $$ = $1; }
;
@ -4173,6 +4180,11 @@ keyword_or_column
| VAR_SAMP
| VAR_POP
| DECFLOAT // added in FB 4.0
| LOCAL
| LOCALTIME
| LOCALTIMESTAMP
| TIMEZONE_HOUR
| TIMEZONE_MINUTE
| UNBOUNDED
| WINDOW
;
@ -4641,12 +4653,37 @@ non_charset_simple_type
$$->dtype = dtype_sql_time;
$$->length = sizeof(SLONG);
}
| TIME WITH TIME ZONE
{
$$ = newNode<dsql_fld>();
if (client_dialect < SQL_DIALECT_V6_TRANSITION)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_sql_dialect_datatype_unsupport) << Arg::Num(client_dialect) <<
Arg::Str("TIME"));
}
if (db_dialect < SQL_DIALECT_V6_TRANSITION)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_sql_db_dialect_dtype_unsupport) << Arg::Num(db_dialect) <<
Arg::Str("TIME"));
}
$$->dtype = dtype_sql_time_tz;
$$->length = sizeof(ISC_TIME_TZ);
}
| TIMESTAMP
{
$$ = newNode<dsql_fld>();
$$->dtype = dtype_timestamp;
$$->length = sizeof(GDS_TIMESTAMP);
}
| TIMESTAMP WITH TIME ZONE
{
$$ = newNode<dsql_fld>();
$$->dtype = dtype_timestamp_tz;
$$->length = sizeof(ISC_TIMESTAMP_TZ);
}
| BOOLEAN
{
$$ = newNode<dsql_fld>();
@ -5246,6 +5283,14 @@ timepart_ses_stmt_tout
| MILLISECOND { $$ = blr_extract_millisecond; }
;
%type <mngNode> set_time_zone
set_time_zone
: SET TIME ZONE sql_string
{ $$ = newNode<SetTimeZoneNode>($4->getString()); }
| SET TIME ZONE LOCAL
{ $$ = newNode<SetTimeZoneNode>(); }
;
%type tran_option_list_opt(<setTransactionNode>)
tran_option_list_opt($setTransactionNode)
: // nothing
@ -7189,6 +7234,24 @@ datetime_value_expression
$$ = newNode<CurrentDateNode>();
}
| LOCALTIME time_precision_opt
{
if (client_dialect < SQL_DIALECT_V6_TRANSITION)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_sql_dialect_datatype_unsupport) << Arg::Num(client_dialect) <<
Arg::Str("TIME"));
}
if (db_dialect < SQL_DIALECT_V6_TRANSITION)
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_sql_db_dialect_dtype_unsupport) << Arg::Num(db_dialect) <<
Arg::Str("TIME"));
}
$$ = newNode<LocalTimeNode>($2);
}
| CURRENT_TIME time_precision_opt
{
if (client_dialect < SQL_DIALECT_V6_TRANSITION)
@ -7207,6 +7270,8 @@ datetime_value_expression
$$ = newNode<CurrentTimeNode>($2);
}
| LOCALTIMESTAMP timestamp_precision_opt
{ $$ = newNode<LocalTimeStampNode>($2); }
| CURRENT_TIMESTAMP timestamp_precision_opt
{ $$ = newNode<CurrentTimeStampNode>($2); }
;
@ -8153,6 +8218,8 @@ timestamp_part
| MINUTE { $$ = blr_extract_minute; }
| SECOND { $$ = blr_extract_second; }
| MILLISECOND { $$ = blr_extract_millisecond; }
| TIMEZONE_HOUR { $$ = blr_extract_timezone_hour; }
| TIMEZONE_MINUTE { $$ = blr_extract_timezone_minute; }
| WEEK { $$ = blr_extract_week; }
| WEEKDAY { $$ = blr_extract_weekday; }
| YEARDAY { $$ = blr_extract_yearday; }
@ -8551,7 +8618,10 @@ non_reserved_word
| INCREMENT
| TRUSTED
| BIND // added in FB 4.0
| CLEAR
| COMPARE_DECFLOAT
| CONNECTIONS
| CONSISTENCY
| CUME_DIST
| DEFINER
| EXCLUDE
@ -8560,13 +8630,16 @@ non_reserved_word
| IDLE
| INVOKER
| LAST_DAY
| LIFETIME
| MESSAGE
| NATIVE
| NORMALIZE_DECFLOAT
| NTILE
| OLDEST
| OTHERS
| OVERRIDING
| PERCENT_RANK
| POOL
| PRECEDING
| PRIVILEGE
| QUANTIZE
@ -8579,12 +8652,7 @@ non_reserved_word
| TIES
| TOTALORDER
| TRAPS
| CONNECTIONS // external connections pool management
| POOL
| LIFETIME
| CLEAR
| OLDEST
| CONSISTENCY
| ZONE
;
%%

View File

@ -78,6 +78,8 @@ typedef struct
#define SQL_TYPE_TIME 560
#define SQL_TYPE_DATE 570
#define SQL_INT64 580
#define SQL_TIMESTAMP_TZ 32754
#define SQL_TIME_TZ 32756
#define SQL_DEC_FIXED 32758
#define SQL_DEC16 32760
#define SQL_DEC34 32762

View File

@ -151,13 +151,30 @@ typedef unsigned long long int ISC_UINT64;
/*******************************************************************/
#ifndef ISC_TIMESTAMP_DEFINED
typedef int ISC_DATE;
typedef unsigned int ISC_TIME;
typedef struct
{
ISC_TIME time_time;
ISC_SHORT time_displacement;
} ISC_TIME_TZ;
typedef struct
{
ISC_DATE timestamp_date;
ISC_TIME timestamp_time;
} ISC_TIMESTAMP;
typedef struct
{
ISC_DATE timestamp_date;
ISC_TIME timestamp_time;
ISC_SHORT timestamp_displacement;
} ISC_TIMESTAMP_TZ;
#define ISC_TIMESTAMP_DEFINED
#endif /* ISC_TIMESTAMP_DEFINED */

View File

@ -155,7 +155,9 @@ const int FLOAT_LEN = 14; // -1.2345678E+38
const int DOUBLE_LEN = 23; // -1.234567890123456E+300
const int DATE_LEN = 11; // 11 for date only
const int DATETIME_LEN = 25; // 25 for date-time
const int DATETIME_TZ_LEN = 30; // 30 for date-time-tz
const int TIME_ONLY_LEN = 13; // 13 for time only
const int TIME_TZ_ONLY_LEN = 19; // 19 for time-tz only
const int DATE_ONLY_LEN = 11;
const int BOOLEAN_LEN = 7; // <false>
const int UNKNOWN_LEN = 20; // Unknown type: %d
@ -4900,7 +4902,7 @@ static processing_state frontend_set(const char* cmd, const char* const* parms,
{SetOptions::transaction, "TRANSACTION", 5},
{SetOptions::terminator, "TERMINATOR", 4},
{SetOptions::names, "NAMES", 0},
{SetOptions::time, "TIME", 0},
//// FIXME: {SetOptions::time, "TIME", 0},
//#ifdef DEV_BUILD
{SetOptions::sqlda_display, "SQLDA_DISPLAY", 0},
//#endif
@ -7395,6 +7397,7 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length)
}
case SQL_TIMESTAMP:
case SQL_TIMESTAMP_TZ:
isc_decode_timestamp(var->value.asDateTime, &times);
if (isqlGlob.SQL_dialect > SQL_DIALECT_V5)
@ -7418,21 +7421,37 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length)
alpha_months[times.tm_mon], times.tm_year + 1900);
}
sprintf(p, "%-*.*s ", length, length, d);
if (setValues.List) {
isqlGlob.printf("%s%s", d, NEWLINE);
if (dtype == SQL_TIMESTAMP_TZ)
{
sprintf(d + strlen(d), "%+03d:%02d", var->value.asDateTimeTz->timestamp_displacement / 60,
abs((int) var->value.asDateTimeTz->timestamp_displacement) % 60);
}
sprintf(p, "%-*.*s ", length, length, d);
if (setValues.List)
isqlGlob.printf("%s%s", d, NEWLINE);
break;
case SQL_TYPE_TIME:
case SQL_TIME_TZ:
isc_decode_sql_time(var->value.asTime, &times);
sprintf(d, "%2.2d:%2.2d:%2.2d.%4.4" ULONGFORMAT,
times.tm_hour, times.tm_min, times.tm_sec,
(*var->value.asTime) % ISC_TIME_SECONDS_PRECISION);
sprintf(p, "%-*.*s ", length, length, d);
if (setValues.List) {
isqlGlob.printf("%s%s", d, NEWLINE);
if (dtype == SQL_TIME_TZ)
{
sprintf(d + strlen(d), "%+03d:%02d", var->value.asTimeTz->time_displacement / 60,
abs((int) var->value.asTimeTz->time_displacement) % 60);
}
sprintf(p, "%-*.*s ", length, length, d);
if (setValues.List)
isqlGlob.printf("%s%s", d, NEWLINE);
break;
case SQL_TYPE_DATE:
@ -8089,9 +8108,15 @@ static unsigned process_message_display(Firebird::IMessageMetadata* message, uns
else
disp_length = DATE_ONLY_LEN;
break;
case SQL_TIMESTAMP_TZ:
disp_length = DATETIME_TZ_LEN;
break;
case SQL_TYPE_TIME:
disp_length = TIME_ONLY_LEN;
break;
case SQL_TIME_TZ:
disp_length = TIME_TZ_ONLY_LEN;
break;
case SQL_TYPE_DATE:
disp_length = DATE_ONLY_LEN;
break;

View File

@ -318,6 +318,8 @@ static const sqltypes Column_types[] = {
{DEC64_TYPE, "DECFLOAT(16)"},
{DEC128_TYPE, "DECFLOAT(34)"},
{DEC_FIXED_TYPE, "<Should not be shown>"},
{blr_sql_time_tz, "TIME WITH TIME ZONE"}, // keyword
{blr_timestamp_tz, "TIMESTAMP WITH TIME ZONE"}, // keyword
{0, ""}
};
@ -452,7 +454,9 @@ struct IsqlVar
union TypeMix
{
ISC_TIMESTAMP* asDateTime;
ISC_TIMESTAMP_TZ* asDateTimeTz;
ISC_TIME* asTime;
ISC_TIME_TZ* asTimeTz;
ISC_DATE* asDate;
SSHORT* asSmallint;
SLONG* asInteger;

View File

@ -227,6 +227,8 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb)
att_ext_parent(NULL),
att_ext_call_depth(0),
att_trace_manager(FB_NEW_POOL(*att_pool) TraceManager(this)),
att_original_timezone(TimeStamp::getCurrentTimeZone()),
att_current_timezone(att_original_timezone),
att_utility(UTIL_NONE),
att_procedures(*pool),
att_functions(*pool),

View File

@ -361,6 +361,9 @@ public:
ULONG att_ext_call_depth; // external connection call depth, 0 for user attachment
TraceManager* att_trace_manager; // Trace API manager
SSHORT att_original_timezone;
SSHORT att_current_timezone;
enum UtilType { UTIL_NONE, UTIL_GBAK, UTIL_GFIX, UTIL_GSTAT };
UtilType att_utility;

View File

@ -72,7 +72,9 @@ static const USHORT gds_cvt_blr_dtype[DTYPE_BLR_MAX + 1] =
dtype_dec128, /* blr_dec128 == 25 */
dtype_dec_fixed, /* blr_dec_fixed == 26 */
dtype_double, /* blr_double == 27 */
0, 0, 0, 0, 0, 0, 0,
dtype_sql_time_tz, /* blr_sql_time_tz == 28 */
dtype_timestamp_tz, /* blr_timestamp_tz == 29 */
0, 0, 0, 0, 0,
dtype_timestamp, /* blr_timestamp == 35 */
0,
dtype_varying, /* blr_varying == 37 */
@ -109,7 +111,9 @@ static const USHORT type_alignments[DTYPE_TYPE_MAX] =
sizeof(UCHAR), /* dtype_boolean */
sizeof(Firebird::Decimal64),/* dtype_dec64 */
sizeof(Firebird::Decimal64),/* dtype_dec128 */
sizeof(Firebird::Decimal64) /* dtype_dec_fixed */
sizeof(Firebird::Decimal64),/* dtype_dec_fixed */
sizeof(GDS_TIME), /* dtype_sql_time_tz */
sizeof(GDS_DATE) /* dtype_timestamp_tz */
};
static const USHORT type_lengths[DTYPE_TYPE_MAX] =
@ -138,7 +142,9 @@ static const USHORT type_lengths[DTYPE_TYPE_MAX] =
sizeof(UCHAR), /* dtype_boolean */
sizeof(Firebird::Decimal64),/* dtype_dec64 */
sizeof(Firebird::Decimal128),/*dtype_dec128 */
sizeof(Firebird::DecimalFixed) /* dtype_dec_fixed */
sizeof(Firebird::DecimalFixed), /* dtype_dec_fixed */
sizeof(ISC_TIME_TZ), /* dtype_sql_time_tz */
sizeof(ISC_TIMESTAMP_TZ) /* dtype_timestamp_tz */
};
@ -170,7 +176,9 @@ static const USHORT type_significant_bits[DTYPE_TYPE_MAX] =
0, // dtype_boolean
0, // dtype_dec64
0, // dtype_dec128
0 // dtype_dec_fixed
0, // dtype_dec_fixed
0, // dtype_sql_time_tz
0 // dtype_timestamp_tz
};
#endif /* JRD_ALIGN_H */

View File

@ -244,5 +244,7 @@ static const struct
{"window_win", window_win},
{"default", relation_field},
{"store3", store3},
{"local_timestamp", byte_line},
{"local_time", byte_line},
{0, 0}
};

View File

@ -70,6 +70,8 @@
#define blr_dec64 (unsigned char)24
#define blr_dec128 (unsigned char)25
#define blr_dec_fixed (unsigned char)26
#define blr_sql_time_tz (unsigned char)28
#define blr_timestamp_tz (unsigned char)29
// first sub parameter for blr_domain_name[2]
#define blr_domain_type_of (unsigned char)0
@ -281,6 +283,8 @@
#define blr_extract_yearday (unsigned char)7
#define blr_extract_millisecond (unsigned char)8
#define blr_extract_week (unsigned char)9
#define blr_extract_timezone_hour (unsigned char)10
#define blr_extract_timezone_minute (unsigned char)11
#define blr_current_date (unsigned char)160
#define blr_current_timestamp (unsigned char)161
@ -427,4 +431,7 @@
#define blr_default (unsigned char) 212
#define blr_store3 (unsigned char) 213
#define blr_local_timestamp (unsigned char) 214
#define blr_local_time (unsigned char) 215
#endif // JRD_BLR_H

View File

@ -1378,6 +1378,7 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx)
break;
case idx_sql_time:
case idx_sql_time_tz:
length = sizeof(ULONG);
break;
@ -1386,6 +1387,7 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx)
break;
case idx_timestamp:
case idx_timestamp_tz:
length = sizeof(SINT64);
break;
@ -1439,12 +1441,14 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx)
length = sizeof(double);
break;
case idx_sql_time:
case idx_sql_time_tz:
length = sizeof(ULONG);
break;
case idx_sql_date:
length = sizeof(ULONG);
break;
case idx_timestamp:
case idx_timestamp_tz:
length = sizeof(SINT64);
break;
case idx_numeric2:
@ -2561,10 +2565,9 @@ static void compress(thread_db* tdbb,
else if (itype == idx_timestamp)
{
GDS_TIMESTAMP timestamp;
timestamp = MOV_get_timestamp(desc);
const ULONG SECONDS_PER_DAY = 24 * 60 * 60;
timestamp = MOV_get_timestamp(desc); //// FIXME: wrong for TZ
temp.temp_sint64 = ((SINT64) (timestamp.timestamp_date) *
(SINT64) (SECONDS_PER_DAY * ISC_TIME_SECONDS_PRECISION)) +
(SINT64) (NoThrowTimeStamp::SECONDS_PER_DAY * ISC_TIME_SECONDS_PRECISION)) +
(SINT64) (timestamp.timestamp_time);
temp_copy_length = sizeof(SINT64);
@ -2595,7 +2598,17 @@ static void compress(thread_db* tdbb,
#ifdef DEBUG_INDEXKEY
fprintf(stderr, "TIME %u ", temp.temp_ulong);
#endif
}
else if (itype == idx_sql_time_tz)
{
ISC_TIME_TZ timeTz = MOV_get_sql_time_tz(desc);
temp.temp_ulong = TimeStamp::timeTzAtZone(timeTz, 0);
temp_copy_length = sizeof(ULONG);
temp_is_negative = false;
#ifdef DEBUG_INDEXKEY
fprintf(stderr, "TIME TZ %u ", temp.temp_ulong);
#endif
}
else if (desc->dsc_dtype == dtype_timestamp)
{
@ -2609,7 +2622,21 @@ static void compress(thread_db* tdbb,
#ifdef DEBUG_INDEXKEY
fprintf(stderr, "TIMESTAMP1 special %lg ", temp.temp_double);
#endif
}
else if (desc->dsc_dtype == dtype_timestamp_tz)
{
ISC_TIMESTAMP_TZ timestampTz = MOV_get_timestamp_tz(desc);
ISC_TIMESTAMP timestamp = TimeStamp::timeStampTzAtZone(timestampTz, 0);
dsc descTimestamp;
descTimestamp.makeTimestamp(&timestamp);
temp.temp_double = MOV_date_to_double(&descTimestamp);
temp_is_negative = (temp.temp_double < 0);
#ifdef DEBUG_INDEXKEY
fprintf(stderr, "TIMESTAMP1 special %lg ", temp.temp_double);
#endif
}
else if (itype == idx_boolean)
{

View File

@ -100,8 +100,10 @@ const int idx_timestamp = 7;
const int idx_numeric2 = 8; // Introduced for 64-bit Integer support
const int idx_boolean = 9;
const int idx_decimal = 10;
const int idx_sql_time_tz = 11;
const int idx_timestamp_tz = 12;
// idx_itype space for future expansion
// idx_itype space for future expansion
const int idx_first_intl_string = 64; // .. MAX (short) Range of computed key strings
const int idx_offset_intl_range = (0x7FFF + idx_first_intl_string);

View File

@ -354,6 +354,31 @@ GDS_TIME CVT_get_sql_time(const dsc* desc)
}
ISC_TIME_TZ CVT_get_sql_time_tz(const dsc* desc)
{
/**************************************
*
* C V T _ g e t _ s q l _ t i m e _ t z
*
**************************************
*
* Functional description
* Convert something arbitrary to a SQL time with time zone value
*
**************************************/
if (desc->dsc_dtype == dtype_sql_time_tz)
return *((ISC_TIME_TZ*) desc->dsc_address);
DSC temp_desc;
ISC_TIME_TZ value;
memset(&temp_desc, 0, sizeof(temp_desc));
temp_desc.dsc_dtype = dtype_sql_time_tz;
temp_desc.dsc_address = (UCHAR*) &value;
CVT_move(desc, &temp_desc, 0);
return value;
}
GDS_TIMESTAMP CVT_get_timestamp(const dsc* desc)
{
/**************************************
@ -379,6 +404,31 @@ GDS_TIMESTAMP CVT_get_timestamp(const dsc* desc)
}
ISC_TIMESTAMP_TZ CVT_get_timestamp_tz(const dsc* desc)
{
/**************************************
*
* C V T _ g e t _ t i m e s t a m p _ t z
*
**************************************
*
* Functional description
* Convert something arbitrary to a SQL timestamp with time zone
*
**************************************/
if (desc->dsc_dtype == dtype_timestamp_tz)
return *((ISC_TIMESTAMP_TZ*) desc->dsc_address);
DSC temp_desc;
ISC_TIMESTAMP_TZ value;
memset(&temp_desc, 0, sizeof(temp_desc));
temp_desc.dsc_dtype = dtype_timestamp_tz;
temp_desc.dsc_address = (UCHAR*) &value;
CVT_move(desc, &temp_desc, 0);
return value;
}
Firebird::GlobalPtr<EngineCallbacks> EngineCallbacks::instance;
@ -470,6 +520,17 @@ SLONG EngineCallbacks::getCurDate()
}
int EngineCallbacks::getSessionTimeZone()
{
thread_db* tdbb = JRD_get_thread_data();
if (tdbb && (tdbb->getType() == ThreadData::tddDBB) && tdbb->getAttachment())
return tdbb->getAttachment()->att_current_timezone;
return 0;
}
void EngineCallbacks::isVersion4(bool& v4)
{
thread_db* tdbb = JRD_get_thread_data();

View File

@ -220,6 +220,9 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
// Handle the simple (matched) ones first
ISC_TIME time1, time2;
ISC_TIMESTAMP timestamp1, timestamp2;
if (arg1->dsc_dtype == arg2->dsc_dtype && arg1->dsc_scale == arg2->dsc_scale)
{
const UCHAR* p1 = arg1->dsc_address;
@ -234,6 +237,13 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
return 1;
return -1;
case dtype_sql_time_tz:
time1 = TimeStamp::timeTzAtZone(*(ISC_TIME_TZ*) p1, 0);
p1 = (const UCHAR*) &time1;
time2 = TimeStamp::timeTzAtZone(*(ISC_TIME_TZ*) p2, 0);
p2 = (const UCHAR*) &time2;
// fall into
case dtype_sql_time:
if (*(ULONG *) p1 == *(ULONG *) p2)
return 0;
@ -271,6 +281,13 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
return (arg1->dsc_length > l) ? 1 : (arg2->dsc_length > l) ? -1 : 0;
}
case dtype_timestamp_tz:
timestamp1 = TimeStamp::timeStampTzAtZone(*(ISC_TIMESTAMP_TZ*) p1, 0);
p1 = (const UCHAR*) &timestamp1;
timestamp2 = TimeStamp::timeStampTzAtZone(*(ISC_TIMESTAMP_TZ*) p2, 0);
p2 = (const UCHAR*) &timestamp2;
// fall into
case dtype_timestamp:
if (((SLONG *) p1)[0] > ((SLONG *) p2)[0])
return 1;
@ -419,6 +436,18 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
switch (arg1->dsc_dtype)
{
case dtype_timestamp_tz:
{
DSC desc;
MOVE_CLEAR(&desc, sizeof(desc));
desc.dsc_dtype = dtype_timestamp_tz;
ISC_TIMESTAMP_TZ datetime;
desc.dsc_length = sizeof(datetime);
desc.dsc_address = (UCHAR*) &datetime;
CVT_move(arg2, &desc, 0);
return CVT2_compare(arg1, &desc, 0);
}
case dtype_timestamp:
{
DSC desc;
@ -431,6 +460,18 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt
return CVT2_compare(arg1, &desc, 0);
}
case dtype_sql_time_tz:
{
DSC desc;
MOVE_CLEAR(&desc, sizeof(desc));
desc.dsc_dtype = dtype_sql_time_tz;
ISC_TIME_TZ atime;
desc.dsc_length = sizeof(atime);
desc.dsc_address = (UCHAR*) &atime;
CVT_move(arg2, &desc, 0);
return CVT2_compare(arg1, &desc, 0);
}
case dtype_sql_time:
{
DSC desc;

View File

@ -33,7 +33,9 @@ void CVT_double_to_date(double, SLONG[2]);
UCHAR CVT_get_numeric(const UCHAR*, const USHORT, SSHORT*, void*);
GDS_DATE CVT_get_sql_date(const dsc*);
GDS_TIME CVT_get_sql_time(const dsc*);
ISC_TIME_TZ CVT_get_sql_time_tz(const dsc*);
GDS_TIMESTAMP CVT_get_timestamp(const dsc*);
ISC_TIMESTAMP_TZ CVT_get_timestamp_tz(const dsc*);
namespace Jrd
{
@ -58,6 +60,7 @@ namespace Jrd
virtual void validateLength(CharSet* toCharset, SLONG toLength, const UCHAR* start,
const USHORT to_size);
virtual SLONG getCurDate();
virtual int getSessionTimeZone();
virtual void isVersion4(bool& v4);
public:

View File

@ -1287,10 +1287,14 @@ USHORT DFW_assign_index_type(thread_db* tdbb, const Firebird::MetaName& name, SS
{
case dtype_timestamp:
return idx_timestamp;
case dtype_timestamp_tz:
return idx_timestamp_tz;
case dtype_sql_date:
return idx_sql_date;
case dtype_sql_time:
return idx_sql_time;
case dtype_sql_time_tz:
return idx_sql_time_tz;
// idx_numeric2 used for 64-bit Integer support
case dtype_int64:
return idx_numeric2;

View File

@ -100,6 +100,69 @@ namespace
return lock.release();
}
class UtcConversor
{
public:
UtcConversor(record_param* aRpb)
: rpb(aRpb)
{
convert(rpb, -1);
}
~UtcConversor()
{
convert(rpb, 1);
}
public:
static void convert(record_param* rpb, int direction)
{
fb_assert(rpb && rpb->rpb_record && rpb->rpb_record->getFormat());
const Format* format = rpb->rpb_record->getFormat();
UCHAR* data = rpb->rpb_record->getData();
tm times = {0};
int fractions;
for (USHORT i = 0; i < format->fmt_count; ++i)
{
const dsc& desc = format->fmt_desc[i];
switch (desc.dsc_dtype)
{
case dtype_sql_time_tz:
{
ISC_TIME_TZ* valueTz = reinterpret_cast<ISC_TIME_TZ*>(data + (IPTR) desc.dsc_address);
ISC_TIME* value = (ISC_TIME*) valueTz;
SINT64 ticks = (SINT64) *value;
ticks += valueTz->time_displacement * 60 * ISC_TIME_SECONDS_PRECISION * direction;
//// FIXME: ticks?
break;
}
case dtype_timestamp_tz:
{
ISC_TIMESTAMP_TZ* valueTz = reinterpret_cast<ISC_TIMESTAMP_TZ*>(data + (IPTR) desc.dsc_address);
ISC_TIMESTAMP* value = (ISC_TIMESTAMP*) valueTz;
SINT64 ticks = ((SINT64) value->timestamp_date) * TimeStamp::ISC_TICKS_PER_DAY +
(SINT64) value->timestamp_time;
ticks += valueTz->timestamp_displacement * 60 * ISC_TIME_SECONDS_PRECISION * direction;
//// FIXME: ticks?
break;
}
}
}
}
private:
record_param* rpb;
};
}
@ -365,6 +428,11 @@ bool DPM_chain( thread_db* tdbb, record_param* org_rpb, record_param* new_rpb)
#endif
record_param temp = *org_rpb;
// This function is currently called only by VIO_erase and new_rpb does not have a record.
///UtcConversor utcConversor(new_rpb);
fb_assert(new_rpb->rpb_length == 0);
const Compressor dcc(*tdbb->getDefaultPool(), new_rpb->rpb_length, new_rpb->rpb_address);
const ULONG size = (ULONG) dcc.getPackedLength();
@ -2078,6 +2146,7 @@ void DPM_store( thread_db* tdbb, record_param* rpb, PageStack& stack, const Jrd:
rpb->rpb_f_line, rpb->rpb_flags);
#endif
UtcConversor utcConversor(rpb);
const Compressor dcc(*tdbb->getDefaultPool(), rpb->rpb_length, rpb->rpb_address);
const ULONG size = (ULONG) dcc.getPackedLength();
@ -2302,6 +2371,8 @@ void DPM_update( thread_db* tdbb, record_param* rpb, PageStack* stack, const jrd
CCH_tra_precedence(tdbb, &rpb->getWindow(tdbb), rpb->rpb_transaction_nr);
CCH_MARK(tdbb, &rpb->getWindow(tdbb));
data_page* page = (data_page*) rpb->getWindow(tdbb).win_buffer;
///UtcConversor utcConversor(rpb);
const Compressor dcc(*tdbb->getDefaultPool(), rpb->rpb_length, rpb->rpb_address);
const ULONG size = (ULONG) dcc.getPackedLength();
@ -3723,6 +3794,7 @@ static void store_big_record(thread_db* tdbb,
// What's left fits on a page. Luckily, we don't have to store it ourselves.
// rpb is already converted to UTC
const Compressor dcc(*tdbb->getDefaultPool(), in - rpb->rpb_address, rpb->rpb_address);
size = (ULONG) dcc.getPackedLength();
rhdf* header = (rhdf*) locate_space(tdbb, rpb, (SSHORT) (RHDF_SIZE + size), stack, NULL, type);

View File

@ -413,8 +413,6 @@ void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value, Memor
case dtype_long:
case dtype_real:
case dtype_sql_time:
case dtype_sql_date:
value->vlu_misc.vlu_long = *((SLONG*) from.dsc_address);
return;
@ -438,7 +436,26 @@ void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value, Memor
value->vlu_misc.vlu_dec_fixed = *((DecimalFixed*) from.dsc_address);
return;
case dtype_sql_time:
value->vlu_misc.vlu_sql_time = *(GDS_TIME*) from.dsc_address;
return;
case dtype_sql_time_tz:
value->vlu_misc.vlu_sql_time_tz = *(ISC_TIME_TZ*) from.dsc_address;
return;
case dtype_sql_date:
value->vlu_misc.vlu_sql_date = *(GDS_DATE*) from.dsc_address;
return;
case dtype_timestamp:
value->vlu_misc.vlu_timestamp = *(GDS_TIMESTAMP*) from.dsc_address;
return;
case dtype_timestamp_tz:
value->vlu_misc.vlu_timestamp_tz = *(ISC_TIMESTAMP_TZ*) from.dsc_address;
return;
case dtype_quad:
value->vlu_misc.vlu_dbkey[0] = ((SLONG*) from.dsc_address)[0];
value->vlu_misc.vlu_dbkey[1] = ((SLONG*) from.dsc_address)[1];

View File

@ -372,6 +372,7 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo
break;
case dtype_sql_time:
case dtype_sql_time_tz:
if (!Firebird::TimeStamp::isValidTime(*(GDS_TIME*) from_desc->dsc_address))
{
ERR_post(Arg::Gds(isc_time_range_exceeded));
@ -379,6 +380,7 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo
break;
case dtype_timestamp:
case dtype_timestamp_tz:
if (!Firebird::TimeStamp::isValidTimeStamp(*(GDS_TIMESTAMP*) from_desc->dsc_address))
{
ERR_post(Arg::Gds(isc_datetime_range_exceeded));

View File

@ -145,7 +145,9 @@ static const TEXT dtypes[DTYPE_TYPE_MAX][36] =
"BOOLEAN",
"DECFLOAT(16)",
"DECFLOAT(34)",
"DECIMAL(34)"
"DECIMAL(34)",
"TIME WITH TIME ZONE",
"TIMESTAMP WITH TIME ZONE"
};

View File

@ -277,6 +277,23 @@ GDS_TIME MOV_get_sql_time(const dsc* desc)
}
ISC_TIME_TZ MOV_get_sql_time_tz(const dsc* desc)
{
/**************************************
*
* M O V _ g e t _ s q l _ t i m e _ t z
*
**************************************
*
* Functional description
* Convert something arbitrary to a SQL time with time zone
*
**************************************/
return CVT_get_sql_time_tz(desc);
}
GDS_TIMESTAMP MOV_get_timestamp(const dsc* desc)
{
/**************************************
@ -294,6 +311,23 @@ GDS_TIMESTAMP MOV_get_timestamp(const dsc* desc)
}
ISC_TIMESTAMP_TZ MOV_get_timestamp_tz(const dsc* desc)
{
/**************************************
*
* M O V _ g e t _ t i m e s t a m p _ t z
*
**************************************
*
* Functional description
* Convert something arbitrary to a timestamp with time zone
*
**************************************/
return CVT_get_timestamp_tz(desc);
}
int MOV_make_string(Jrd::thread_db* tdbb,
const dsc* desc,
USHORT ttype,

View File

@ -44,7 +44,9 @@ int MOV_get_string_ptr(Jrd::thread_db*, const dsc*, USHORT*, UCHAR**, vary*, US
int MOV_get_string(Jrd::thread_db*, const dsc*, UCHAR**, vary*, USHORT);
GDS_DATE MOV_get_sql_date(const dsc*);
GDS_TIME MOV_get_sql_time(const dsc*);
ISC_TIME_TZ MOV_get_sql_time_tz(const dsc*);
GDS_TIMESTAMP MOV_get_timestamp(const dsc*);
ISC_TIMESTAMP_TZ MOV_get_timestamp_tz(const dsc*);
int MOV_make_string(Jrd::thread_db*, const dsc*, USHORT, const char**, vary*, USHORT);
int MOV_make_string2(Jrd::thread_db*, const dsc*, USHORT, UCHAR**, Jrd::MoveBuffer&, bool = true);
Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT ttype,

View File

@ -397,7 +397,9 @@ static const UCHAR sort_dtypes[] =
SKD_bytes, // dtype_boolean
SKD_dec64, // dtype_dec64
SKD_dec128, // dtype_dec128
SKD_dec128 // dtype_dec_fixed
SKD_dec128, // dtype_dec_fixed
SKD_sql_time_tz, // dtype_sql_time_tz
SKD_timestamp_tz // dtype_timestamp_tz
};
@ -2459,7 +2461,8 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi
// International type text has a computed key
// Different decimal float values sometimes have same keys
if (IS_INTL_DATA(desc) || desc->isDecFloat())
// ASF: Date/time with time zones too.
if (IS_INTL_DATA(desc) || desc->isDecFloat() || desc->isDateTimeTz())
break;
--items;

View File

@ -369,6 +369,11 @@ USHORT PAR_datatype(BlrReader& blrReader, dsc* desc)
desc->dsc_length = sizeof(ISC_QUAD);
break;
case blr_timestamp_tz:
desc->dsc_dtype = dtype_timestamp_tz;
desc->dsc_length = sizeof(ISC_TIMESTAMP_TZ);
break;
case blr_sql_date:
desc->dsc_dtype = dtype_sql_date;
desc->dsc_length = type_lengths[dtype_sql_date];
@ -379,6 +384,11 @@ USHORT PAR_datatype(BlrReader& blrReader, dsc* desc)
desc->dsc_length = type_lengths[dtype_sql_time];
break;
case blr_sql_time_tz:
desc->dsc_dtype = dtype_sql_time_tz;
desc->dsc_length = type_lengths[dtype_sql_time_tz];
break;
case blr_double:
case blr_d_float:
desc->dsc_dtype = dtype_double;

View File

@ -797,6 +797,8 @@ void Sort::diddleKey(UCHAR* record, bool direction)
complement = !complement;
break;
//// FIXME: SKD_time_tz and SKD_timestamp_tz
case SKD_long:
case SKD_short:
case SKD_quad:
@ -926,6 +928,24 @@ void Sort::diddleKey(UCHAR* record, bool direction)
switch (key->skd_dtype)
{
case SKD_sql_time_tz:
if (direction)
{
*(ISC_TIME*) p = TimeStamp::timeTzAtZone(*(ISC_TIME_TZ*) p, 0);
((ISC_TIME_TZ*) p)->time_displacement = 0;
}
p[3] ^= 1 << 7;
break;
case SKD_timestamp_tz:
if (direction)
{
*(ISC_TIMESTAMP*) p = TimeStamp::timeStampTzAtZone(*(ISC_TIMESTAMP_TZ*) p, 0);
((ISC_TIMESTAMP_TZ*) p)->timestamp_displacement = 0;
}
p[3] ^= 1 << 7;
break;
case SKD_timestamp:
case SKD_sql_time:
case SKD_sql_date:

View File

@ -143,6 +143,8 @@ const int SKD_sql_date = 14;
const int SKD_int64 = 15;
const int SKD_dec64 = 16;
const int SKD_dec128 = 17;
const int SKD_sql_time_tz = 18;
const int SKD_timestamp_tz = 19;
// skd_flags
const UCHAR SKD_ascending = 0; // default initializer

View File

@ -43,6 +43,8 @@ TYPE("INT64", blr_int64, nam_f_type)
TYPE("BOOLEAN", blr_bool, nam_f_type)
TYPE("DECFLOAT(16)", blr_dec64, nam_f_type)
TYPE("DECFLOAT(34)", blr_dec128, nam_f_type)
TYPE("TIMESTAMP WITH TIMEZONE", blr_timestamp_tz, nam_f_type)
TYPE("TIME WITH TIMEZONE", blr_sql_time_tz, nam_f_type)
TYPE("BINARY", 0, nam_f_sub_type)
TYPE("TEXT", 1, nam_f_sub_type)

View File

@ -87,7 +87,9 @@ struct impure_value
Firebird::Decimal128 vlu_dec128;
Firebird::DecimalFixed vlu_dec_fixed;
GDS_TIMESTAMP vlu_timestamp;
ISC_TIMESTAMP_TZ vlu_timestamp_tz;
GDS_TIME vlu_sql_time;
ISC_TIME_TZ vlu_sql_time_tz;
GDS_DATE vlu_sql_date;
bid vlu_bid;

View File

@ -158,11 +158,21 @@ void BlrFromMessage::buildBlr(IMessageMetadata* metadata)
dtype = dtype_sql_time;
break;
case SQL_TIME_TZ:
appendUChar(blr_sql_time_tz);
dtype = dtype_sql_time_tz;
break;
case SQL_TIMESTAMP:
appendUChar(blr_timestamp);
dtype = dtype_timestamp;
break;
case SQL_TIMESTAMP_TZ:
appendUChar(blr_timestamp_tz);
dtype = dtype_timestamp_tz;
break;
case SQL_BLOB:
if (protocol >= PROTOCOL_VERSION12)
{

View File

@ -340,6 +340,12 @@ static rem_fmt* parse_format(const UCHAR*& blr, size_t& blr_length)
align = type_alignments[dtype_timestamp];
break;
case blr_timestamp_tz:
desc->dsc_dtype = dtype_timestamp_tz;
desc->dsc_length = sizeof(ISC_TIMESTAMP_TZ);
align = type_alignments[dtype_timestamp_tz];
break;
case blr_sql_date:
desc->dsc_dtype = dtype_sql_date;
desc->dsc_length = sizeof(SLONG);
@ -352,6 +358,12 @@ static rem_fmt* parse_format(const UCHAR*& blr, size_t& blr_length)
align = type_alignments[dtype_sql_time];
break;
case blr_sql_time_tz:
desc->dsc_dtype = dtype_sql_time_tz;
desc->dsc_length = sizeof(ISC_TIME_TZ);
align = type_alignments[dtype_sql_time_tz];
break;
case blr_bool:
desc->dsc_dtype = dtype_boolean;
desc->dsc_length = sizeof(UCHAR);

View File

@ -2948,11 +2948,21 @@ static int blr_print_dtype(gds_ctl* control)
length = 8;
break;
case blr_timestamp_tz:
string = "timestamp_tz";
length = 10;
break;
case blr_sql_time:
string = "sql_time";
length = 4;
break;
case blr_sql_time_tz:
string = "sql_time_tz";
length = 6;
break;
case blr_sql_date:
string = "sql_date";
length = 4;

View File

@ -266,6 +266,9 @@ static const TOK tokens[] =
{TOK_LINGER, "LINGER", true},
{TOK_LIST, "LIST", true},
{TOK_LN, "LN", true},
{TOK_LOCAL, "LOCAL", true},
{TOK_LOCALTIME, "LOCALTIME", true},
{TOK_LOCALTIMESTAMP, "LOCALTIMESTAMP", true},
{TOK_LOCK, "LOCK", true},
{TOK_LOG, "LOG", true},
{TOK_LOG10, "LOG10", true},
@ -455,6 +458,8 @@ static const TOK tokens[] =
{TOK_TIME, "TIME", false},
{TOK_TIMESTAMP, "TIMESTAMP", false},
{TOK_TIMEOUT, "TIMEOUT", true},
{TOK_TIMEZONE_HOUR, "TIMEZONE_HOUR", true},
{TOK_TIMEZONE_MINUTE, "TIMEZONE_MINUTE", true},
{TOK_TO, "TO", false},
{TOK_TOTALORDER, "TOTALORDER", true},
{TOK_TRAILING, "TRAILING", false},
@ -501,6 +506,7 @@ static const TOK tokens[] =
{TOK_WRITE, "WRITE", true},
{TOK_YEAR, "YEAR", false},
{TOK_YEARDAY, "YEARDAY", true},
{TOK_ZONE, "ZONE", true},
{TOK_NOT_LSS, "^<", false}, // Alias of !<
{TOK_NEQ, "^=", false}, // Alias of !=
{TOK_NOT_GTR, "^>", false}, // Alias of !>