8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 09:20:39 +01:00

Feature CORE-5620 - Builtin functions FIRST_DAY and LAST_DAY.

This commit is contained in:
Adriano dos Santos Fernandes 2017-11-01 15:30:03 +00:00
parent 2dc72496ea
commit a2556be641
14 changed files with 244 additions and 6 deletions

View File

@ -265,7 +265,7 @@ CEIL | CEILING
--------------
Function:
Returns a value representing the smallest integer that is greater
Returns a value representing the smallest integer that is greater
than or equal to the input argument.
Format:
@ -429,12 +429,32 @@ Example:
select exp(x) from y;
---------
FIRST_DAY
---------
Function:
Returns the first day of the year/month/week of a given date/timestamp value.
Format:
FIRST_DAY( OF { YEAR | MONTH | WEEK } FROM <date_or_timestamp> )
Notes:
1) The first day of the week is considered as Sunday, per the same rules of EXTRACT with WEEKDAY.
2) When a timestamp is passed the return value preserves the time part.
Example:
select first_day(of month from current_date) from rdb$database;
select first_day(of year from current_timestamp) from rdb$database;
select first_day(of week from date '2017-11-01') from rdb$database;
-----
FLOOR
-----
Function:
Returns a value representing the largest integer that is less
Returns a value representing the largest integer that is less
than or equal to the input argument.
Format:
@ -490,6 +510,26 @@ Example:
select hash(x using sha256) from y;
--------
LAST_DAY
--------
Function:
Returns the last day of the year/month/week of a given date/timestamp value.
Format:
LAST_DAY( OF { YEAR | MONTH | WEEK } FROM <date_or_timestamp> )
Notes:
1) The last day of the week is considered as Saturday, per the same rules of EXTRACT with WEEKDAY.
2) When a timestamp is passed the return value preserves the time part.
Example:
select last_day(of month from current_date) from rdb$database;
select last_day(of year from current_timestamp) from rdb$database;
select last_day(of week from date '2017-11-01') from rdb$database;
----
LEFT
----
@ -802,7 +842,7 @@ SIGN
----
Function:
Returns 1, 0, or -1 depending on whether the input value is positive, zero or
Returns 1, 0, or -1 depending on whether the input value is positive, zero or
negative, respectively.
Format:

View File

@ -1722,6 +1722,10 @@ C --
PARAMETER (GDS__decfloat_trap = 335545154)
INTEGER*4 GDS__decfloat_round
PARAMETER (GDS__decfloat_round = 335545155)
INTEGER*4 GDS__sysf_invalid_first_last_part
PARAMETER (GDS__sysf_invalid_first_last_part = 335545156)
INTEGER*4 GDS__sysf_invalid_date_timestamp
PARAMETER (GDS__sysf_invalid_date_timestamp = 335545157)
INTEGER*4 GDS__gfix_db_name
PARAMETER (GDS__gfix_db_name = 335740929)
INTEGER*4 GDS__gfix_invalid_sw

View File

@ -1717,6 +1717,10 @@ const
gds_decfloat_trap = 335545154;
isc_decfloat_round = 335545155;
gds_decfloat_round = 335545155;
isc_sysf_invalid_first_last_part = 335545156;
gds_sysf_invalid_first_last_part = 335545156;
isc_sysf_invalid_date_timestamp = 335545157;
gds_sysf_invalid_date_timestamp = 335545157;
isc_gfix_db_name = 335740929;
gds_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;

View File

@ -599,9 +599,11 @@ using namespace Firebird;
%token <metaNamePtr> DECFLOAT
%token <metaNamePtr> DEFINER
%token <metaNamePtr> EXCLUDE
%token <metaNamePtr> FIRST_DAY
%token <metaNamePtr> FOLLOWING
%token <metaNamePtr> IDLE
%token <metaNamePtr> INVOKER
%token <metaNamePtr> LAST_DAY
%token <metaNamePtr> MESSAGE
%token <metaNamePtr> NATIVE
%token <metaNamePtr> NORMALIZE_DECFLOAT
@ -7778,6 +7780,12 @@ system_function_special_syntax
newNode<ValueListNode>(MAKE_const_slong($3))->add($5)->add($7));
$$->dsqlSpecialSyntax = true;
}
| FIRST_DAY '(' of_first_last_day_part FROM value ')'
{
$$ = newNode<SysFuncCallNode>(*$1,
newNode<ValueListNode>(MAKE_const_slong($3))->add($5));
$$->dsqlSpecialSyntax = true;
}
| HASH '(' value ')'
{ $$ = newNode<SysFuncCallNode>(*$1, newNode<ValueListNode>($3)); }
| HASH '(' value USING valid_symbol_name ')'
@ -7786,6 +7794,12 @@ system_function_special_syntax
newNode<ValueListNode>($3)->add(MAKE_str_constant(newIntlString($5->c_str()), CS_ASCII)));
$$->dsqlSpecialSyntax = true;
}
| LAST_DAY '(' of_first_last_day_part FROM value ')'
{
$$ = newNode<SysFuncCallNode>(*$1,
newNode<ValueListNode>(MAKE_const_slong($3))->add($5));
$$->dsqlSpecialSyntax = true;
}
| OVERLAY '(' value PLACING value FROM value FOR value ')'
{
$$ = newNode<SysFuncCallNode>(*$1,
@ -7812,6 +7826,13 @@ system_function_special_syntax
}
;
%type <blrOp> of_first_last_day_part
of_first_last_day_part
: OF YEAR { $$ = blr_extract_year; }
| OF MONTH { $$ = blr_extract_month; }
| OF WEEK { $$ = blr_extract_week; }
;
%type <valueExprNode> string_value_function
string_value_function
: substring_function
@ -8462,9 +8483,11 @@ non_reserved_word
| DECFLOAT
| DEFINER
| EXCLUDE
| FIRST_DAY
| FOLLOWING
| IDLE
| INVOKER
| LAST_DAY
| MESSAGE
| NATIVE
| NORMALIZE_DECFLOAT

View File

@ -857,6 +857,8 @@ static const struct {
{"expression_eval_index", 335545153},
{"decfloat_trap", 335545154},
{"decfloat_round", 335545155},
{"sysf_invalid_first_last_part", 335545156},
{"sysf_invalid_date_timestamp", 335545157},
{"gfix_db_name", 335740929},
{"gfix_invalid_sw", 335740930},
{"gfix_incmp_sw", 335740932},

View File

@ -891,6 +891,8 @@ const ISC_STATUS isc_sysf_invalid_hash_algorithm = 335545152L;
const ISC_STATUS isc_expression_eval_index = 335545153L;
const ISC_STATUS isc_decfloat_trap = 335545154L;
const ISC_STATUS isc_decfloat_round = 335545155L;
const ISC_STATUS isc_sysf_invalid_first_last_part = 335545156L;
const ISC_STATUS isc_sysf_invalid_date_timestamp = 335545157L;
const ISC_STATUS isc_gfix_db_name = 335740929L;
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
@ -1365,7 +1367,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
const ISC_STATUS isc_err_max = 1309;
const ISC_STATUS isc_err_max = 1311;
#else /* c definitions */
@ -2226,6 +2228,8 @@ const ISC_STATUS isc_err_max = 1309;
#define isc_expression_eval_index 335545153L
#define isc_decfloat_trap 335545154L
#define isc_decfloat_round 335545155L
#define isc_sysf_invalid_first_last_part 335545156L
#define isc_sysf_invalid_date_timestamp 335545157L
#define isc_gfix_db_name 335740929L
#define isc_gfix_invalid_sw 335740930L
#define isc_gfix_incmp_sw 335740932L
@ -2700,7 +2704,7 @@ const ISC_STATUS isc_err_max = 1309;
#define isc_trace_switch_param_miss 337182758L
#define isc_trace_param_act_notcompat 337182759L
#define isc_trace_mandatory_switch_miss 337182760L
#define isc_err_max 1309
#define isc_err_max 1311
#endif

View File

@ -860,6 +860,8 @@ Data source : @4"}, /* eds_statement */
{335545153, "Expression evaluation error for index \"@1\" on table \"@2\""}, /* expression_eval_index */
{335545154, "Invalid decfloat trap state @1"}, /* decfloat_trap */
{335545155, "Invalid decfloat rounding mode @1"}, /* decfloat_round */
{335545156, "Invalid part @1 to calculate the @1 of a DATE/TIMESTAMP"}, /* sysf_invalid_first_last_part */
{335545157, "Expected DATE/TIMESTAMP value in @1"}, /* sysf_invalid_date_timestamp */
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */

View File

@ -856,6 +856,8 @@ static const struct {
{335545153, -901}, /* 833 expression_eval_index */
{335545154, -901}, /* 834 decfloat_trap */
{335545155, -901}, /* 835 decfloat_round */
{335545156, -833}, /* 836 sysf_invalid_first_last_part */
{335545157, -833}, /* 837 sysf_invalid_date_timestamp */
{335740929, -901}, /* 1 gfix_db_name */
{335740930, -901}, /* 2 gfix_invalid_sw */
{335740932, -901}, /* 4 gfix_incmp_sw */

View File

@ -856,6 +856,8 @@ static const struct {
{335545153, "42000"}, // 833 expression_eval_index
{335545154, "42000"}, // 834 decfloat_trap
{335545155, "42000"}, // 835 decfloat_round
{335545156, "42000"}, // 836 sysf_invalid_first_last_part
{335545157, "42000"}, // 837 sysf_invalid_date_timestamp
{335740929, "00000"}, // 1 gfix_db_name
{335740930, "00000"}, // 2 gfix_invalid_sw
{335740932, "00000"}, // 4 gfix_incmp_sw

View File

@ -72,6 +72,8 @@ enum Function
funBinShrRot,
funBinXor,
funBinNot,
funFirstDay,
funLastDay,
funMaxValue,
funMinValue,
funLPad,
@ -173,6 +175,7 @@ void setParamsAsciiVal(DataTypeUtilBase* dataTypeUtil, const SysFunction* functi
void setParamsCharToUuid(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
void setParamsDateAdd(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
void setParamsDateDiff(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
void setParamsFirstLastDay(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
void setParamsGetSetContext(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
void setParamsOverlay(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
void setParamsPosition(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
@ -196,6 +199,7 @@ void makeBin(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* r
void makeBinShift(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeCeilFloor(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeDateAdd(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeFirstLastDayResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeGetSetContext(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeHash(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeLeftRight(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
@ -225,6 +229,7 @@ dsc* evlCharToUuid(thread_db* tdbb, const SysFunction* function, const NestValue
dsc* evlDateAdd(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
dsc* evlExp(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
dsc* evlFirstLastDay(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
dsc* evlFloor(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
dsc* evlGenUuid(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
dsc* evlGetContext(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
@ -520,6 +525,16 @@ void setParamsDateDiff(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc
}
void setParamsFirstLastDay(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args)
{
if (argsCount >= 2)
{
if (args[1]->isUnknown())
args[1]->makeTimestamp();
}
}
void setParamsGetSetContext(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args)
{
if (argsCount >= 1 && args[0]->isUnknown())
@ -939,6 +954,22 @@ void makeDateAdd(DataTypeUtilBase*, const SysFunction*, dsc* result, int argsCou
}
void makeFirstLastDayResult(DataTypeUtilBase*, const SysFunction*, dsc* result,
int argsCount, const dsc** args)
{
bool isNullable;
if (initResult(result, argsCount, args, &isNullable))
return;
result->makeDate();
if (argsCount >= 2 && args[1]->dsc_dtype == dtype_timestamp)
result->makeTimestamp();
result->setNullable(isNullable);
}
void makeGetSetContext(DataTypeUtilBase* /*dataTypeUtil*/, const SysFunction* function, dsc* result,
int argsCount, const dsc** /*args*/)
{
@ -2307,6 +2338,122 @@ dsc* evlExp(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
}
dsc* evlFirstLastDay(thread_db* tdbb, const SysFunction* function, const NestValueArray& args,
impure_value* impure)
{
fb_assert(args.getCount() >= 2);
jrd_req* request = tdbb->getRequest();
const dsc* partDsc = EVL_expr(tdbb, request, args[0]);
if (request->req_flags & req_null) // return NULL if partDsc is NULL
return NULL;
const dsc* valueDsc = EVL_expr(tdbb, request, args[1]);
if (request->req_flags & req_null) // return NULL if valueDsc is NULL
return NULL;
TimeStamp timestamp;
tm times = {0};
int fractions = 0;
switch (valueDsc->dsc_dtype)
{
case dtype_sql_date:
timestamp.value().timestamp_date = *(GDS_DATE*) valueDsc->dsc_address;
timestamp.value().timestamp_time = 0;
timestamp.decode(&times, &fractions);
break;
case dtype_timestamp:
timestamp.value() = *(GDS_TIMESTAMP*) valueDsc->dsc_address;
timestamp.decode(&times, &fractions);
break;
default:
status_exception::raise(
Arg::Gds(isc_expression_eval_err) <<
Arg::Gds(isc_sysf_invalid_date_timestamp) <<
Arg::Str(function->name));
break;
}
const SLONG part = MOV_get_long(tdbb, partDsc, 0);
switch (part)
{
case blr_extract_year:
times.tm_mon = 0;
// fall through
case blr_extract_month:
times.tm_mday = 1;
break;
case blr_extract_week:
break;
default:
status_exception::raise(
Arg::Gds(isc_expression_eval_err) <<
Arg::Gds(isc_sysf_invalid_first_last_part) <<
Arg::Str(function->name));
break;
}
const bool last = (Function)(IPTR) function->misc == funLastDay;
int adjust = 0;
if (last)
{
switch (part)
{
case blr_extract_year:
++times.tm_year;
adjust = -1;
break;
case blr_extract_month:
if (++times.tm_mon == 12)
{
times.tm_mon = 0;
++times.tm_year;
}
adjust = -1;
break;
case blr_extract_week:
adjust = 6 - times.tm_wday;
break;
}
}
else if (part == blr_extract_week)
adjust = -times.tm_wday;
timestamp.encode(&times, fractions);
timestamp.value().timestamp_date += adjust;
if (!TimeStamp::isValidTimeStamp(timestamp.value()))
status_exception::raise(Arg::Gds(isc_datetime_range_exceeded));
EVL_make_value(tdbb, valueDsc, impure);
switch (impure->vlu_desc.dsc_dtype)
{
case dtype_sql_date:
impure->vlu_misc.vlu_sql_date = timestamp.value().timestamp_date;
break;
case dtype_timestamp:
impure->vlu_misc.vlu_timestamp = timestamp.value();
break;
}
return &impure->vlu_desc;
}
dsc* evlFloor(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
impure_value* impure)
{
@ -4362,9 +4509,11 @@ const SysFunction SysFunction::functions[] =
{"DATEADD", 3, 3, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL},
{"DATEDIFF", 3, 3, setParamsDateDiff, makeInt64Result, evlDateDiff, NULL},
{"EXP", 1, 1, setParamsDblDec, makeDblDecResult, evlExp, NULL},
{"FIRST_DAY", 2, 2, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay},
{"FLOOR", 1, 1, setParamsDblDec, makeCeilFloor, evlFloor, NULL},
{"GEN_UUID", 0, 0, NULL, makeUuid, evlGenUuid, NULL},
{"HASH", 1, 2, NULL, makeHash, evlHash, NULL},
{"LAST_DAY", 2, 2, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funLastDay},
{"LEFT", 2, 2, setParamsSecondInteger, makeLeftRight, evlLeft, NULL},
{"LN", 1, 1, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat},
{"LOG", 2, 2, setParamsDblDec, makeDblDecResult, evlLog, NULL},

View File

@ -1,7 +1,7 @@
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
--
('2017-10-23 16:30:00', 'JRD', 0, 836)
('2017-11-01 12:40:00', 'JRD', 0, 838)
('2015-03-17 18:33:00', 'QLI', 1, 533)
('2015-01-07 18:01:51', 'GFIX', 3, 134)
('1996-11-07 13:39:40', 'GPRE', 4, 1)

View File

@ -943,6 +943,8 @@ Data source : @4', NULL, NULL)
('expression_eval_index', NULL, 'btr.cpp', NULL, 0, 833, NULL, 'Expression evaluation error for index "@1" on table "@2"', NULL, NULL);
('decfloat_trap', NULL, 'StmtNodes.cpp', NULL, 0, 834, NULL, 'Invalid decfloat trap state @1', NULL, NULL);
('decfloat_round', NULL, 'StmtNodes.cpp', NULL, 0, 835, NULL, 'Invalid decfloat rounding mode @1', NULL, NULL);
('sysf_invalid_first_last_part', 'evlFirstLastDay', 'SysFunction.cpp', NULL, 0, 836, NULL, 'Invalid part @1 to calculate the @1 of a DATE/TIMESTAMP', NULL, NULL)
('sysf_invalid_date_timestamp', 'evlFirstLastDay', 'SysFunction.cpp', NULL, 0, 837, NULL, 'Expected DATE/TIMESTAMP value in @1', NULL, NULL)
-- QLI
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);

View File

@ -842,6 +842,8 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-901, '42', '000', 0, 833, 'expression_eval_index', NULL, NULL)
(-901, '42', '000', 0, 834, 'decfloat_trap', NULL, NULL)
(-901, '42', '000', 0, 835, 'decfloat_round', NULL, NULL)
(-833, '42', '000', 0, 836, 'sysf_invalid_first_last_part', NULL, NULL)
(-833, '42', '000', 0, 837, 'sysf_invalid_date_timestamp', NULL, NULL)
-- GFIX
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)

View File

@ -200,6 +200,7 @@ static const TOK tokens[] =
{TOK_FILE, "FILE", true},
{TOK_FILTER, "FILTER", false},
{TOK_FIRST, "FIRST", true},
{TOK_FIRST_DAY, "FIRST_DAY", true},
{TOK_FIRST_VALUE, "FIRST_VALUE", true},
{TOK_FIRSTNAME, "FIRSTNAME", true},
{TOK_FLOAT, "FLOAT", false},
@ -247,6 +248,7 @@ static const TOK tokens[] =
{TOK_KEY, "KEY", true},
{TOK_LAG, "LAG", true},
{TOK_LAST, "LAST", true},
{TOK_LAST_DAY, "LAST_DAY", true},
{TOK_LAST_VALUE, "LAST_VALUE", true},
{TOK_LASTNAME, "LASTNAME", true},
{TOK_LEAD, "LEAD", true},