mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 23:23:04 +01:00
Improvements:
1) CORE-1387 - Add MILLISECONDS handling to EXTRACT, DATEADD and DATEDIFF functions 2) CORE-663 - EXTRACT(WEEK FROM DATE)
This commit is contained in:
parent
2961fe218d
commit
b5a23c12d1
@ -108,6 +108,12 @@ public:
|
||||
static void decode_time(ISC_TIME ntime, int* hours, int* minutes, int* seconds, int* fractions);
|
||||
static ISC_TIME encode_time(int hours, int minutes, int seconds, int fractions);
|
||||
static void round_time(ISC_TIME &ntime, int precision);
|
||||
|
||||
static inline bool isLeapYear(int year)
|
||||
{
|
||||
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||
}
|
||||
|
||||
private:
|
||||
ISC_TIMESTAMP mValue;
|
||||
|
||||
|
@ -227,6 +227,7 @@ static const TOK tokens[] =
|
||||
{MAXVALUE, "MAXVALUE", 2, false},
|
||||
{MAX_SEGMENT, "MAXIMUM_SEGMENT", 1, false},
|
||||
{MERGE, "MERGE", 1, false},
|
||||
{MILLISECOND, "MILLISECOND", 2, false},
|
||||
{MINIMUM, "MIN", 1, false},
|
||||
{MINUTE, "MINUTE", 2, false},
|
||||
{MINVALUE, "MINVALUE", 2, false},
|
||||
@ -367,6 +368,7 @@ static const TOK tokens[] =
|
||||
{VARYING, "VARYING", 1, false},
|
||||
{VIEW, "VIEW", 1, false},
|
||||
{WAIT, "WAIT", 1, false},
|
||||
{WEEK, "WEEK", 2, false},
|
||||
{WEEKDAY, "WEEKDAY", 2, true},
|
||||
{WHEN, "WHEN", 1, false},
|
||||
{WHERE, "WHERE", 1, false},
|
||||
|
@ -1220,20 +1220,24 @@ void MAKE_desc(dsql_req* request, dsc* desc, dsql_nod* node, dsql_nod* null_repl
|
||||
|
||||
case nod_extract:
|
||||
MAKE_desc(request, &desc1, node->nod_arg[e_extract_value], NULL);
|
||||
desc->dsc_sub_type = 0;
|
||||
desc->dsc_scale = 0;
|
||||
desc->dsc_flags = (desc1.dsc_flags & DSC_nullable);
|
||||
if (*(ULONG *) node->nod_arg[e_extract_part]->nod_desc.dsc_address
|
||||
== blr_extract_second)
|
||||
|
||||
switch (*(ULONG *) node->nod_arg[e_extract_part]->nod_desc.dsc_address)
|
||||
{
|
||||
// QUADDATE - maybe this should be DECIMAL(6,4)
|
||||
desc->dsc_dtype = dtype_long;
|
||||
desc->dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
||||
desc->dsc_length = sizeof(ULONG);
|
||||
return;
|
||||
case blr_extract_second:
|
||||
// QUADDATE - maybe this should be DECIMAL(6,4)
|
||||
desc->makeLong(ISC_TIME_SECONDS_PRECISION_SCALE);
|
||||
break;
|
||||
|
||||
case blr_extract_millisecond:
|
||||
desc->makeLong(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
desc->makeShort(0);
|
||||
break;
|
||||
}
|
||||
desc->dsc_dtype = dtype_short;
|
||||
desc->dsc_length = sizeof(SSHORT);
|
||||
|
||||
desc->setNullable(desc1.isNullable());
|
||||
return;
|
||||
|
||||
case nod_strlen:
|
||||
|
@ -549,6 +549,7 @@ static LexerState lex;
|
||||
%token MATCHED
|
||||
%token MATCHING
|
||||
%token MAXVALUE
|
||||
%token MILLISECOND
|
||||
%token MINVALUE
|
||||
%token MOD
|
||||
%token OVERLAY
|
||||
@ -574,6 +575,7 @@ static LexerState lex;
|
||||
%token TANH
|
||||
%token TEMPORARY
|
||||
%token TRUNC
|
||||
%token WEEK
|
||||
|
||||
/* precedence declarations for expression evaluation */
|
||||
|
||||
@ -2315,9 +2317,11 @@ keyword_or_column : valid_symbol_name
|
||||
| DISCONNECT
|
||||
| GLOBAL
|
||||
| INSENSITIVE
|
||||
| MILLISECOND
|
||||
| RECURSIVE
|
||||
| SENSITIVE
|
||||
| START
|
||||
| WEEK
|
||||
;
|
||||
|
||||
col_opt : ALTER
|
||||
@ -4591,6 +4595,10 @@ timestamp_part : YEAR
|
||||
{ $$ = MAKE_constant ((dsql_str*)blr_extract_minute, CONSTANT_SLONG); }
|
||||
| SECOND
|
||||
{ $$ = MAKE_constant ((dsql_str*)blr_extract_second, CONSTANT_SLONG); }
|
||||
| MILLISECOND
|
||||
{ $$ = MAKE_constant ((dsql_str*)blr_extract_millisecond, CONSTANT_SLONG); }
|
||||
| WEEK
|
||||
{ $$ = MAKE_constant ((dsql_str*)blr_extract_week, CONSTANT_SLONG); }
|
||||
| WEEKDAY
|
||||
{ $$ = MAKE_constant ((dsql_str*)blr_extract_weekday, CONSTANT_SLONG); }
|
||||
| YEARDAY
|
||||
|
@ -626,6 +626,7 @@ dsql_nod* PASS1_node(dsql_req* request, dsql_nod* input, bool proc_flag)
|
||||
case blr_extract_day:
|
||||
case blr_extract_weekday:
|
||||
case blr_extract_yearday:
|
||||
case blr_extract_week:
|
||||
if (sub1->nod_type != nod_null &&
|
||||
sub1->nod_desc.dsc_dtype != dtype_sql_date &&
|
||||
sub1->nod_desc.dsc_dtype != dtype_timestamp)
|
||||
@ -637,6 +638,7 @@ dsql_nod* PASS1_node(dsql_req* request, dsql_nod* input, bool proc_flag)
|
||||
case blr_extract_hour:
|
||||
case blr_extract_minute:
|
||||
case blr_extract_second:
|
||||
case blr_extract_millisecond:
|
||||
if (sub1->nod_type != nod_null &&
|
||||
sub1->nod_desc.dsc_dtype != dtype_sql_time &&
|
||||
sub1->nod_desc.dsc_dtype != dtype_timestamp)
|
||||
|
@ -1120,7 +1120,8 @@ static dsc* evlDateAdd(Jrd::thread_db* tdbb, SysFunction* function, Jrd::jrd_nod
|
||||
|
||||
if (part != blr_extract_hour &&
|
||||
part != blr_extract_minute &&
|
||||
part != blr_extract_second)
|
||||
part != blr_extract_second &&
|
||||
part != blr_extract_millisecond)
|
||||
{
|
||||
status_exception::raise(isc_expression_eval_err, 0);
|
||||
}
|
||||
@ -1131,7 +1132,8 @@ static dsc* evlDateAdd(Jrd::thread_db* tdbb, SysFunction* function, Jrd::jrd_nod
|
||||
|
||||
if (part == blr_extract_hour ||
|
||||
part == blr_extract_minute ||
|
||||
part == blr_extract_second)
|
||||
part == blr_extract_second ||
|
||||
part == blr_extract_millisecond)
|
||||
{
|
||||
status_exception::raise(isc_expression_eval_err, 0);
|
||||
}
|
||||
@ -1225,6 +1227,10 @@ static dsc* evlDateAdd(Jrd::thread_db* tdbb, SysFunction* function, Jrd::jrd_nod
|
||||
add10msec(×tamp.value(), quantity, ISC_TIME_SECONDS_PRECISION);
|
||||
break;
|
||||
|
||||
case blr_extract_millisecond:
|
||||
add10msec(×tamp.value(), quantity, ISC_TIME_SECONDS_PRECISION / 1000);
|
||||
break;
|
||||
|
||||
default:
|
||||
status_exception::raise(isc_expression_eval_err, 0);
|
||||
break;
|
||||
@ -1335,6 +1341,7 @@ static dsc* evlDateDiff(Jrd::thread_db* tdbb, SysFunction* function, Jrd::jrd_no
|
||||
case blr_extract_hour:
|
||||
case blr_extract_minute:
|
||||
case blr_extract_second:
|
||||
case blr_extract_millisecond:
|
||||
if (value1Dsc->dsc_dtype == dtype_sql_date || value2Dsc->dsc_dtype == dtype_sql_date)
|
||||
status_exception::raise(isc_expression_eval_err, 0);
|
||||
|
||||
@ -1387,6 +1394,13 @@ static dsc* evlDateDiff(Jrd::thread_db* tdbb, SysFunction* function, Jrd::jrd_no
|
||||
ISC_TIME_SECONDS_PRECISION;
|
||||
break;
|
||||
|
||||
case blr_extract_millisecond:
|
||||
result = oneDay * (timestamp2.value().timestamp_date - timestamp1.value().timestamp_date) * 1000;
|
||||
result += ((SINT64) timestamp2.value().timestamp_time -
|
||||
(SINT64) timestamp1.value().timestamp_time) /
|
||||
(ISC_TIME_SECONDS_PRECISION / 1000);
|
||||
break;
|
||||
|
||||
default:
|
||||
status_exception::raise(isc_expression_eval_err, 0);
|
||||
break;
|
||||
|
@ -268,14 +268,16 @@
|
||||
|
||||
/* sub parameters for blr_extract */
|
||||
|
||||
#define blr_extract_year (unsigned char)0
|
||||
#define blr_extract_month (unsigned char)1
|
||||
#define blr_extract_day (unsigned char)2
|
||||
#define blr_extract_hour (unsigned char)3
|
||||
#define blr_extract_minute (unsigned char)4
|
||||
#define blr_extract_second (unsigned char)5
|
||||
#define blr_extract_weekday (unsigned char)6
|
||||
#define blr_extract_yearday (unsigned char)7
|
||||
#define blr_extract_year (unsigned char)0
|
||||
#define blr_extract_month (unsigned char)1
|
||||
#define blr_extract_day (unsigned char)2
|
||||
#define blr_extract_hour (unsigned char)3
|
||||
#define blr_extract_minute (unsigned char)4
|
||||
#define blr_extract_second (unsigned char)5
|
||||
#define blr_extract_weekday (unsigned char)6
|
||||
#define blr_extract_yearday (unsigned char)7
|
||||
#define blr_extract_millisecond (unsigned char)8
|
||||
#define blr_extract_week (unsigned char)9
|
||||
|
||||
#define blr_current_date (unsigned char)160
|
||||
#define blr_current_timestamp (unsigned char)161
|
||||
|
@ -1645,21 +1645,22 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC * de
|
||||
return;
|
||||
|
||||
case nod_extract:
|
||||
if ((IPTR) node->nod_arg[e_extract_part] == blr_extract_second) {
|
||||
// QUADDATE - SECOND returns a float, or scaled!
|
||||
desc->dsc_dtype = dtype_long;
|
||||
desc->dsc_length = sizeof(ULONG);
|
||||
desc->dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
||||
desc->dsc_sub_type = 0;
|
||||
desc->dsc_flags = 0;
|
||||
}
|
||||
else {
|
||||
desc->dsc_dtype = dtype_short;
|
||||
desc->dsc_length = sizeof(SSHORT);
|
||||
desc->dsc_scale = 0;
|
||||
desc->dsc_sub_type = 0;
|
||||
desc->dsc_flags = 0;
|
||||
switch ((IPTR) node->nod_arg[e_extract_part])
|
||||
{
|
||||
case blr_extract_second:
|
||||
// QUADDATE - SECOND returns a float, or scaled!
|
||||
desc->makeLong(ISC_TIME_SECONDS_PRECISION_SCALE);
|
||||
break;
|
||||
|
||||
case blr_extract_millisecond:
|
||||
desc->makeLong(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
desc->makeShort(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
case nod_strlen:
|
||||
|
@ -3502,7 +3502,8 @@ static dsc* extract(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
||||
timestamp.decode(×);
|
||||
if (extract_part != blr_extract_hour &&
|
||||
extract_part != blr_extract_minute &&
|
||||
extract_part != blr_extract_second)
|
||||
extract_part != blr_extract_second &&
|
||||
extract_part != blr_extract_millisecond)
|
||||
{
|
||||
ERR_post(isc_expression_eval_err, 0);
|
||||
}
|
||||
@ -3512,7 +3513,8 @@ static dsc* extract(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
||||
timestamp.decode(×);
|
||||
if (extract_part == blr_extract_hour ||
|
||||
extract_part == blr_extract_minute ||
|
||||
extract_part == blr_extract_second)
|
||||
extract_part == blr_extract_second ||
|
||||
extract_part == blr_extract_millisecond)
|
||||
{
|
||||
ERR_post(isc_expression_eval_err, 0);
|
||||
}
|
||||
@ -3544,15 +3546,77 @@ static dsc* extract(thread_db* tdbb, jrd_nod* node, impure_value* impure)
|
||||
part = times.tm_min;
|
||||
break;
|
||||
case blr_extract_second:
|
||||
impure->vlu_desc.dsc_dtype = dtype_long;
|
||||
impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
||||
// fall through
|
||||
case blr_extract_millisecond:
|
||||
impure->vlu_desc.dsc_dtype = dtype_long;
|
||||
impure->vlu_desc.dsc_address =
|
||||
reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
||||
impure->vlu_desc.dsc_length = sizeof(ULONG);
|
||||
*(ULONG *) impure->vlu_desc.dsc_address =
|
||||
times.tm_sec * ISC_TIME_SECONDS_PRECISION +
|
||||
(timestamp.value().timestamp_time % ISC_TIME_SECONDS_PRECISION);
|
||||
|
||||
if (extract_part == blr_extract_millisecond)
|
||||
(*(ULONG *) impure->vlu_desc.dsc_address) /= ISC_TIME_SECONDS_PRECISION / 1000;
|
||||
|
||||
return &impure->vlu_desc;
|
||||
|
||||
case blr_extract_week:
|
||||
{
|
||||
// Algorithm for Converting Gregorian Dates to ISO 8601 Week Date by Rick McCarty, 1999
|
||||
// http://personal.ecu.edu/mccartyr/ISOwdALG.txt
|
||||
|
||||
int y = times.tm_year + 1900;
|
||||
int dayOfYearNumber = times.tm_yday + 1;
|
||||
|
||||
// Find the jan1Weekday for y (Monday=1, Sunday=7)
|
||||
int yy = (y - 1) % 100;
|
||||
int c = (y - 1) - yy;
|
||||
int g = yy + yy / 4;
|
||||
int jan1Weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
|
||||
|
||||
// Find the weekday for y m d
|
||||
int h = dayOfYearNumber + (jan1Weekday - 1);
|
||||
int weekday = 1 + ((h - 1) % 7);
|
||||
|
||||
// Find if y m d falls in yearNumber y-1, weekNumber 52 or 53
|
||||
int yearNumber, weekNumber;
|
||||
|
||||
if ((dayOfYearNumber <= (8 - jan1Weekday)) && (jan1Weekday > 4))
|
||||
{
|
||||
yearNumber = y - 1;
|
||||
weekNumber = ((jan1Weekday == 5) || ((jan1Weekday == 6) &&
|
||||
Firebird::TimeStamp::isLeapYear(yearNumber))) ? 53 : 52;
|
||||
}
|
||||
else
|
||||
yearNumber = y;
|
||||
|
||||
// Find if y m d falls in yearNumber y+1, weekNumber 1
|
||||
if (yearNumber == y)
|
||||
{
|
||||
int i = Firebird::TimeStamp::isLeapYear(y) ? 366 : 365;
|
||||
|
||||
if ((i - dayOfYearNumber) < (4 - weekday))
|
||||
{
|
||||
yearNumber = y + 1;
|
||||
weekNumber = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Find if y m d falls in yearNumber y, weekNumber 1 through 53
|
||||
if (yearNumber == y)
|
||||
{
|
||||
int j = dayOfYearNumber + (7 - weekday) + (jan1Weekday - 1);
|
||||
weekNumber = j / 7;
|
||||
if (jan1Weekday > 4)
|
||||
weekNumber--;
|
||||
}
|
||||
|
||||
part = weekNumber;
|
||||
break;
|
||||
}
|
||||
|
||||
case blr_extract_yearday:
|
||||
part = times.tm_yday;
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user