8
0
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:
asfernandes 2007-07-31 01:22:59 +00:00
parent 2961fe218d
commit b5a23c12d1
9 changed files with 142 additions and 39 deletions

View File

@ -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;

View File

@ -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},

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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(&timestamp.value(), quantity, ISC_TIME_SECONDS_PRECISION);
break;
case blr_extract_millisecond:
add10msec(&timestamp.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;

View File

@ -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

View File

@ -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:

View File

@ -3502,7 +3502,8 @@ static dsc* extract(thread_db* tdbb, jrd_nod* node, impure_value* impure)
timestamp.decode(&times);
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(&times);
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;