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:
parent
1824698b96
commit
7100879498
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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(×, 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(×2);
|
||||
@ -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,
|
||||
×.tm_hour, ×.tm_min, ×.tm_sec, &fractions);
|
||||
case dtype_sql_time_tz:
|
||||
Firebird::TimeStamp::decode_time(*(GDS_TIME*) from->dsc_address,
|
||||
×.tm_hour, ×.tm_min, ×.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,
|
||||
×, &fractions);
|
||||
Firebird::TimeStamp::decode_timestamp(*(GDS_TIMESTAMP*) from->dsc_address, ×, &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*/)
|
||||
{
|
||||
}
|
||||
|
@ -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
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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?
|
||||
|
@ -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:
|
||||
|
@ -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*);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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, ×);
|
||||
|
||||
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, ×);
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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}
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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(×tamp);
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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*) ×tamp1;
|
||||
timestamp2 = TimeStamp::timeStampTzAtZone(*(ISC_TIMESTAMP_TZ*) p2, 0);
|
||||
p2 = (const UCHAR*) ×tamp2;
|
||||
// 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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
|
@ -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));
|
||||
|
@ -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"
|
||||
};
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 !>
|
||||
|
Loading…
Reference in New Issue
Block a user