mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 06:03:02 +01:00
Refactor EXTRACT
This commit is contained in:
parent
2dcef5c7ce
commit
2e0da292df
@ -3194,6 +3194,350 @@ ValueExprNode* CurrentUserNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/)
|
||||
//--------------------
|
||||
|
||||
|
||||
static RegisterNode<ExtractNode> regExtractNode(blr_extract);
|
||||
|
||||
ExtractNode::ExtractNode(MemoryPool& pool, UCHAR aBlrSubOp, dsql_nod* aArg)
|
||||
: TypedNode<ValueExprNode, ExprNode::TYPE_EXTRACT>(pool),
|
||||
blrSubOp(aBlrSubOp),
|
||||
dsqlArg(aArg),
|
||||
arg(NULL)
|
||||
{
|
||||
addChildNode(dsqlArg, arg);
|
||||
}
|
||||
|
||||
DmlNode* ExtractNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp)
|
||||
{
|
||||
UCHAR blrSubOp = csb->csb_blr_reader.getByte();
|
||||
|
||||
ExtractNode* node = FB_NEW(pool) ExtractNode(pool, blrSubOp);
|
||||
node->arg = PAR_parse_node(tdbb, csb, VALUE);
|
||||
return node;
|
||||
}
|
||||
|
||||
void ExtractNode::print(string& text, Array<dsql_nod*>& nodes) const
|
||||
{
|
||||
text.printf("ExtractNode (%d)", blrSubOp);
|
||||
ExprNode::print(text, nodes);
|
||||
}
|
||||
|
||||
ValueExprNode* ExtractNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
// Figure out the data type of the sub parameter, and make
|
||||
// sure the requested type of information can be extracted
|
||||
|
||||
dsql_nod* sub1 = PASS1_node(dsqlScratch, dsqlArg);
|
||||
MAKE_desc(dsqlScratch, &sub1->nod_desc, sub1, NULL);
|
||||
|
||||
switch (blrSubOp)
|
||||
{
|
||||
case blr_extract_year:
|
||||
case blr_extract_month:
|
||||
case blr_extract_day:
|
||||
case blr_extract_weekday:
|
||||
case blr_extract_yearday:
|
||||
case blr_extract_week:
|
||||
if (sub1->nod_type != Dsql::nod_null &&
|
||||
sub1->nod_desc.dsc_dtype != dtype_sql_date &&
|
||||
sub1->nod_desc.dsc_dtype != dtype_timestamp)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-105) <<
|
||||
Arg::Gds(isc_extract_input_mismatch));
|
||||
}
|
||||
break;
|
||||
|
||||
case blr_extract_hour:
|
||||
case blr_extract_minute:
|
||||
case blr_extract_second:
|
||||
case blr_extract_millisecond:
|
||||
if (sub1->nod_type != Dsql::nod_null &&
|
||||
sub1->nod_desc.dsc_dtype != dtype_sql_time &&
|
||||
sub1->nod_desc.dsc_dtype != dtype_timestamp)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-105) <<
|
||||
Arg::Gds(isc_extract_input_mismatch));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fb_assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return FB_NEW(getPool()) ExtractNode(getPool(), blrSubOp, sub1);
|
||||
}
|
||||
|
||||
void ExtractNode::setParameterName(dsql_par* parameter) const
|
||||
{
|
||||
parameter->par_name = parameter->par_alias = "EXTRACT";
|
||||
}
|
||||
|
||||
bool ExtractNode::setParameterType(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode,
|
||||
dsql_nod* node, bool forceVarChar)
|
||||
{
|
||||
return PASS1_set_parameter_type(dsqlScratch, dsqlArg, node, forceVarChar);
|
||||
}
|
||||
|
||||
void ExtractNode::genBlr(DsqlCompilerScratch* dsqlScratch)
|
||||
{
|
||||
dsqlScratch->appendUChar(blr_extract);
|
||||
dsqlScratch->appendUChar(blrSubOp);
|
||||
GEN_expr(dsqlScratch, dsqlArg);
|
||||
}
|
||||
|
||||
void ExtractNode::make(DsqlCompilerScratch* dsqlScratch, dsql_nod* /*thisNode*/, dsc* desc,
|
||||
dsql_nod* nullReplacement)
|
||||
{
|
||||
dsc desc1;
|
||||
MAKE_desc(dsqlScratch, &desc1, dsqlArg, NULL);
|
||||
|
||||
switch (blrSubOp)
|
||||
{
|
||||
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(ISC_TIME_SECONDS_PRECISION_SCALE + 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
desc->makeShort(0);
|
||||
break;
|
||||
}
|
||||
|
||||
desc->setNullable(desc1.isNullable());
|
||||
}
|
||||
|
||||
void ExtractNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
|
||||
{
|
||||
switch (blrSubOp)
|
||||
{
|
||||
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(ISC_TIME_SECONDS_PRECISION_SCALE + 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
desc->makeShort(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ValueExprNode* ExtractNode::copy(thread_db* tdbb, NodeCopier& copier)
|
||||
{
|
||||
ExtractNode* node = FB_NEW(*tdbb->getDefaultPool()) ExtractNode(*tdbb->getDefaultPool(), blrSubOp);
|
||||
node->arg = copier.copy(tdbb, arg);
|
||||
return node;
|
||||
}
|
||||
|
||||
bool ExtractNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const
|
||||
{
|
||||
if (!ExprNode::dsqlMatch(other, ignoreMapCast))
|
||||
return false;
|
||||
|
||||
const ExtractNode* o = other->as<ExtractNode>();
|
||||
fb_assert(o)
|
||||
|
||||
return blrSubOp == o->blrSubOp;
|
||||
}
|
||||
|
||||
bool ExtractNode::expressionEqual(thread_db* tdbb, CompilerScratch* csb, /*const*/ ExprNode* other,
|
||||
USHORT stream)
|
||||
{
|
||||
if (!ExprNode::expressionEqual(tdbb, csb, other, stream))
|
||||
return false;
|
||||
|
||||
ExtractNode* o = other->as<ExtractNode>();
|
||||
fb_assert(o);
|
||||
|
||||
return blrSubOp == o->blrSubOp;
|
||||
}
|
||||
|
||||
ExprNode* ExtractNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
||||
{
|
||||
ExprNode::pass2(tdbb, csb);
|
||||
|
||||
dsc desc;
|
||||
getDesc(tdbb, csb, &desc);
|
||||
node->nod_impure = CMP_impure(csb, sizeof(impure_value));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// Handles EXTRACT(part FROM date/time/timestamp)
|
||||
dsc* ExtractNode::execute(thread_db* tdbb, jrd_req* request) const
|
||||
{
|
||||
impure_value* const impure = request->getImpure<impure_value>(node->nod_impure);
|
||||
request->req_flags &= ~req_null;
|
||||
|
||||
const dsc* value = EVL_expr(tdbb, arg);
|
||||
|
||||
if (!value || (request->req_flags & req_null))
|
||||
return NULL;
|
||||
|
||||
impure->vlu_desc.makeShort(0, &impure->vlu_misc.vlu_short);
|
||||
|
||||
tm times = {0};
|
||||
int fractions;
|
||||
|
||||
switch (value->dsc_dtype)
|
||||
{
|
||||
case dtype_sql_time:
|
||||
switch (blrSubOp)
|
||||
{
|
||||
case blr_extract_hour:
|
||||
case blr_extract_minute:
|
||||
case blr_extract_second:
|
||||
case blr_extract_millisecond:
|
||||
TimeStamp::decode_time(*(GDS_TIME*) value->dsc_address,
|
||||
×.tm_hour, ×.tm_min, ×.tm_sec, &fractions);
|
||||
break;
|
||||
default:
|
||||
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
||||
Arg::Gds(isc_invalid_extractpart_time));
|
||||
}
|
||||
break;
|
||||
|
||||
case dtype_sql_date:
|
||||
switch (blrSubOp)
|
||||
{
|
||||
case blr_extract_hour:
|
||||
case blr_extract_minute:
|
||||
case blr_extract_second:
|
||||
case blr_extract_millisecond:
|
||||
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
||||
Arg::Gds(isc_invalid_extractpart_date));
|
||||
break;
|
||||
default:
|
||||
TimeStamp::decode_date(*(GDS_DATE*) value->dsc_address, ×);
|
||||
}
|
||||
break;
|
||||
|
||||
case dtype_timestamp:
|
||||
TimeStamp::decode_timestamp(*(GDS_TIMESTAMP*) value->dsc_address, ×, &fractions);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
||||
Arg::Gds(isc_invalidarg_extract));
|
||||
break;
|
||||
}
|
||||
|
||||
USHORT part;
|
||||
|
||||
switch (blrSubOp)
|
||||
{
|
||||
case blr_extract_year:
|
||||
part = times.tm_year + 1900;
|
||||
break;
|
||||
case blr_extract_month:
|
||||
part = times.tm_mon + 1;
|
||||
break;
|
||||
case blr_extract_day:
|
||||
part = times.tm_mday;
|
||||
break;
|
||||
case blr_extract_hour:
|
||||
part = times.tm_hour;
|
||||
break;
|
||||
case blr_extract_minute:
|
||||
part = times.tm_min;
|
||||
break;
|
||||
|
||||
case blr_extract_second:
|
||||
impure->vlu_desc.dsc_dtype = dtype_long;
|
||||
impure->vlu_desc.dsc_length = sizeof(ULONG);
|
||||
impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
||||
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
||||
*(ULONG*) impure->vlu_desc.dsc_address = times.tm_sec * ISC_TIME_SECONDS_PRECISION + fractions;
|
||||
return &impure->vlu_desc;
|
||||
|
||||
case blr_extract_millisecond:
|
||||
impure->vlu_desc.dsc_dtype = dtype_long;
|
||||
impure->vlu_desc.dsc_length = sizeof(ULONG);
|
||||
impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE + 3;
|
||||
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
||||
(*(ULONG*) impure->vlu_desc.dsc_address) = fractions;
|
||||
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
|
||||
|
||||
const int y = times.tm_year + 1900;
|
||||
const int dayOfYearNumber = times.tm_yday + 1;
|
||||
|
||||
// Find the jan1Weekday for y (Monday=1, Sunday=7)
|
||||
const int yy = (y - 1) % 100;
|
||||
const int c = (y - 1) - yy;
|
||||
const int g = yy + yy / 4;
|
||||
const int jan1Weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
|
||||
|
||||
// Find the weekday for y m d
|
||||
const int h = dayOfYearNumber + (jan1Weekday - 1);
|
||||
const 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) &&
|
||||
TimeStamp::isLeapYear(yearNumber))) ? 53 : 52;
|
||||
}
|
||||
else
|
||||
{
|
||||
yearNumber = y;
|
||||
|
||||
// Find if y m d falls in yearNumber y+1, weekNumber 1
|
||||
int i = 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;
|
||||
case blr_extract_weekday:
|
||||
part = times.tm_wday;
|
||||
break;
|
||||
default:
|
||||
fb_assert(false);
|
||||
part = 0;
|
||||
}
|
||||
|
||||
*(USHORT*) impure->vlu_desc.dsc_address = part;
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
||||
//--------------------
|
||||
|
||||
|
||||
static RegisterNode<GenIdNode> regGenIdNode(blr_gen_id);
|
||||
|
||||
GenIdNode::GenIdNode(MemoryPool& pool, bool aDialect1, const MetaName& aName, dsql_nod* aArg)
|
||||
|
@ -246,6 +246,37 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class ExtractNode : public TypedNode<ValueExprNode, ExprNode::TYPE_EXTRACT>
|
||||
{
|
||||
public:
|
||||
ExtractNode(MemoryPool& pool, UCHAR aBlrSubOp, dsql_nod* aArg = NULL);
|
||||
|
||||
static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp);
|
||||
|
||||
virtual void print(Firebird::string& text, Firebird::Array<dsql_nod*>& nodes) const;
|
||||
virtual ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void setParameterName(dsql_par* parameter) const;
|
||||
virtual bool setParameterType(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode,
|
||||
dsql_nod* node, bool forceVarChar);
|
||||
virtual void genBlr(DsqlCompilerScratch* dsqlScratch);
|
||||
virtual void make(DsqlCompilerScratch* dsqlScratch, dsql_nod* thisNode, dsc* desc,
|
||||
dsql_nod* nullReplacement);
|
||||
|
||||
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
|
||||
virtual ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier);
|
||||
virtual bool dsqlMatch(const ExprNode* other, bool ignoreMapCast) const;
|
||||
virtual bool expressionEqual(thread_db* tdbb, CompilerScratch* csb, /*const*/ ExprNode* other,
|
||||
USHORT stream) /*const*/;
|
||||
virtual ExprNode* pass2(thread_db* tdbb, CompilerScratch* csb);
|
||||
virtual dsc* execute(thread_db* tdbb, jrd_req* request) const;
|
||||
|
||||
public:
|
||||
UCHAR blrSubOp;
|
||||
dsql_nod* dsqlArg;
|
||||
NestConst<jrd_nod> arg;
|
||||
};
|
||||
|
||||
|
||||
class GenIdNode : public TypedNode<ValueExprNode, ExprNode::TYPE_GEN_ID>
|
||||
{
|
||||
public:
|
||||
|
@ -264,6 +264,7 @@ public:
|
||||
TYPE_CURRENT_TIMESTAMP,
|
||||
TYPE_CURRENT_ROLE,
|
||||
TYPE_CURRENT_USER,
|
||||
TYPE_EXTRACT,
|
||||
TYPE_GEN_ID,
|
||||
TYPE_INTERNAL_INFO,
|
||||
TYPE_MISSING_BOOL,
|
||||
|
@ -519,7 +519,6 @@ inline bool DsqlNodeVisitor<T, T2>::visitChildren(T node)
|
||||
ret |= visit(&node->nod_arg[1]);
|
||||
break;
|
||||
|
||||
case nod_extract:
|
||||
case nod_simple_case:
|
||||
case nod_searched_case:
|
||||
case nod_list:
|
||||
|
@ -216,12 +216,6 @@ void GEN_expr(DsqlCompilerScratch* dsqlScratch, dsql_nod* node)
|
||||
GEN_expr(dsqlScratch, node->nod_arg[e_derived_field_value]);
|
||||
return;
|
||||
|
||||
case nod_extract:
|
||||
dsqlScratch->appendUChar(blr_extract);
|
||||
dsqlScratch->appendUChar(node->nod_arg[e_extract_part]->getSlong());
|
||||
GEN_expr(dsqlScratch, node->nod_arg[e_extract_value]);
|
||||
return;
|
||||
|
||||
case nod_dbkey:
|
||||
node = node->nod_arg[0];
|
||||
context = (dsql_ctx*) node->nod_arg[e_rel_context];
|
||||
|
@ -521,28 +521,6 @@ void MAKE_desc(DsqlCompilerScratch* dsqlScratch, dsc* desc, dsql_nod* node, dsql
|
||||
}
|
||||
return;
|
||||
|
||||
case nod_extract:
|
||||
MAKE_desc(dsqlScratch, &desc1, node->nod_arg[e_extract_value], NULL);
|
||||
|
||||
switch (node->nod_arg[e_extract_part]->getSlong())
|
||||
{
|
||||
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(ISC_TIME_SECONDS_PRECISION_SCALE + 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
desc->makeShort(0);
|
||||
break;
|
||||
}
|
||||
|
||||
desc->setNullable(desc1.isNullable());
|
||||
return;
|
||||
|
||||
case nod_null:
|
||||
// This occurs when SQL statement specifies a literal NULL, eg:
|
||||
// SELECT NULL FROM TABLE1;
|
||||
@ -1164,9 +1142,6 @@ void MAKE_parameter_names(dsql_par* parameter, const dsql_nod* item)
|
||||
case nod_cast:
|
||||
name_alias = "CAST";
|
||||
break;
|
||||
case nod_extract:
|
||||
name_alias = "EXTRACT";
|
||||
break;
|
||||
case nod_searched_case:
|
||||
case nod_simple_case:
|
||||
name_alias = "CASE";
|
||||
|
@ -181,7 +181,6 @@ enum nod_t
|
||||
nod_role_name,
|
||||
nod_grant_admin,
|
||||
nod_del_role,
|
||||
nod_extract,
|
||||
nod_mod_field_name,
|
||||
nod_mod_field_type,
|
||||
nod_mod_field_pos,
|
||||
@ -649,12 +648,6 @@ enum node_args {
|
||||
e_stat_name = 0,
|
||||
e_stat_count,
|
||||
|
||||
// SQL extract() function
|
||||
|
||||
e_extract_part = 0, // constant representing part to extract
|
||||
e_extract_value, // Must be a time or date value
|
||||
e_extract_count,
|
||||
|
||||
e_mod_fld_name_orig_name = 0, // nod_mod_field_name
|
||||
e_mod_fld_name_new_name,
|
||||
e_mod_fld_name_count,
|
||||
|
@ -797,7 +797,7 @@ inline void check_copy_incr(char*& to, const char ch, const char* const string)
|
||||
%type <legacyNode> table_constraint table_constraint_definition
|
||||
%type <legacyNode> table_list table_lock table_name table_or_alias_list table_primary
|
||||
%type <legacyNode> table_proc table_proc_inputs table_reference table_subquery tbl_reserve_options
|
||||
%type <legacyNode> timestamp_part top tra_misc_options tra_timeout tran_opt tran_opt_list tran_opt_list_m
|
||||
%type <legacyNode> top tra_misc_options tra_timeout tran_opt tran_opt_list tran_opt_list_m
|
||||
%type <legacyNode> trim_function
|
||||
%type <uintVal> time_precision_opt timestamp_precision_opt
|
||||
|
||||
@ -868,7 +868,7 @@ inline void check_copy_incr(char*& to, const char ch, const char* const string)
|
||||
|
||||
%type <sysFuncCallNode> system_function_special_syntax
|
||||
|
||||
%type <blrOp> trim_specification
|
||||
%type <blrOp> timestamp_part trim_specification
|
||||
|
||||
// Predicates
|
||||
%type <boolExprNode> between_predicate comparison_predicate distinct_predicate
|
||||
@ -5553,9 +5553,10 @@ numeric_value_function
|
||||
| length_expression
|
||||
;
|
||||
|
||||
extract_expression : EXTRACT '(' timestamp_part FROM value ')'
|
||||
{ $$ = make_node (nod_extract, (int) e_extract_count, $3, $5); }
|
||||
;
|
||||
extract_expression
|
||||
: EXTRACT '(' timestamp_part FROM value ')'
|
||||
{ $$ = makeClassNode(FB_NEW(getPool()) ExtractNode(getPool(), $3, $5)); }
|
||||
;
|
||||
|
||||
length_expression
|
||||
: bit_length_expression
|
||||
@ -5645,25 +5646,25 @@ system_function_special_syntax
|
||||
: DATEADD '(' value timestamp_part TO value ')'
|
||||
{
|
||||
$$ = FB_NEW(getPool()) SysFuncCallNode(getPool(), toName($1),
|
||||
make_node(nod_list, 3, $3, $4, $6));
|
||||
make_node(nod_list, 3, $3, MAKE_const_slong($4), $6));
|
||||
$$->dsqlSpecialSyntax = true;
|
||||
}
|
||||
| DATEADD '(' timestamp_part ',' value ',' value ')'
|
||||
{
|
||||
$$ = FB_NEW(getPool()) SysFuncCallNode(getPool(), toName($1),
|
||||
make_node(nod_list, 3, $5, $3, $7));
|
||||
make_node(nod_list, 3, $5, MAKE_const_slong($3), $7));
|
||||
$$->dsqlSpecialSyntax = true;
|
||||
}
|
||||
| DATEDIFF '(' timestamp_part FROM value TO value ')'
|
||||
{
|
||||
$$ = FB_NEW(getPool()) SysFuncCallNode(getPool(), toName($1),
|
||||
make_node(nod_list, 3, $3, $5, $7));
|
||||
make_node(nod_list, 3, MAKE_const_slong($3), $5, $7));
|
||||
$$->dsqlSpecialSyntax = true;
|
||||
}
|
||||
| DATEDIFF '(' timestamp_part ',' value ',' value ')'
|
||||
{
|
||||
$$ = FB_NEW(getPool()) SysFuncCallNode(getPool(), toName($1),
|
||||
make_node(nod_list, 3, $3, $5, $7));
|
||||
make_node(nod_list, 3, MAKE_const_slong($3), $5, $7));
|
||||
$$->dsqlSpecialSyntax = true;
|
||||
}
|
||||
| OVERLAY '(' value PLACING value FROM value FOR value ')'
|
||||
@ -5846,27 +5847,18 @@ next_value_expression
|
||||
;
|
||||
|
||||
|
||||
timestamp_part : YEAR
|
||||
{ $$ = MAKE_const_slong (blr_extract_year); }
|
||||
| MONTH
|
||||
{ $$ = MAKE_const_slong (blr_extract_month); }
|
||||
| DAY
|
||||
{ $$ = MAKE_const_slong (blr_extract_day); }
|
||||
| HOUR
|
||||
{ $$ = MAKE_const_slong (blr_extract_hour); }
|
||||
| MINUTE
|
||||
{ $$ = MAKE_const_slong (blr_extract_minute); }
|
||||
| SECOND
|
||||
{ $$ = MAKE_const_slong (blr_extract_second); }
|
||||
| MILLISECOND
|
||||
{ $$ = MAKE_const_slong (blr_extract_millisecond); }
|
||||
| WEEK
|
||||
{ $$ = MAKE_const_slong (blr_extract_week); }
|
||||
| WEEKDAY
|
||||
{ $$ = MAKE_const_slong (blr_extract_weekday); }
|
||||
| YEARDAY
|
||||
{ $$ = MAKE_const_slong (blr_extract_yearday); }
|
||||
;
|
||||
timestamp_part
|
||||
: YEAR { $$ = blr_extract_year; }
|
||||
| MONTH { $$ = blr_extract_month; }
|
||||
| DAY { $$ = blr_extract_day; }
|
||||
| HOUR { $$ = blr_extract_hour; }
|
||||
| MINUTE { $$ = blr_extract_minute; }
|
||||
| SECOND { $$ = blr_extract_second; }
|
||||
| MILLISECOND { $$ = blr_extract_millisecond; }
|
||||
| WEEK { $$ = blr_extract_week; }
|
||||
| WEEKDAY { $$ = blr_extract_weekday; }
|
||||
| YEARDAY { $$ = blr_extract_yearday; }
|
||||
;
|
||||
|
||||
all_noise
|
||||
:
|
||||
|
@ -1292,54 +1292,6 @@ dsql_nod* PASS1_node(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
|
||||
node = pass1_collate(dsqlScratch, sub1, (dsql_str*) input->nod_arg[e_coll_target]);
|
||||
return node;
|
||||
|
||||
case nod_extract:
|
||||
|
||||
// Figure out the data type of the sub parameter, and make
|
||||
// sure the requested type of information can be extracted
|
||||
|
||||
sub1 = PASS1_node(dsqlScratch, input->nod_arg[e_extract_value]);
|
||||
MAKE_desc(dsqlScratch, &sub1->nod_desc, sub1, NULL);
|
||||
|
||||
switch (input->nod_arg[e_extract_part]->getSlong())
|
||||
{
|
||||
case blr_extract_year:
|
||||
case blr_extract_month:
|
||||
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)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-105) <<
|
||||
Arg::Gds(isc_extract_input_mismatch));
|
||||
}
|
||||
break;
|
||||
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)
|
||||
{
|
||||
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-105) <<
|
||||
Arg::Gds(isc_extract_input_mismatch));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fb_assert(false);
|
||||
break;
|
||||
}
|
||||
node = MAKE_node(input->nod_type, e_extract_count);
|
||||
node->nod_arg[e_extract_part] = input->nod_arg[e_extract_part];
|
||||
node->nod_arg[e_extract_value] = sub1;
|
||||
if (sub1->nod_desc.dsc_flags & DSC_nullable) {
|
||||
node->nod_desc.dsc_flags |= DSC_nullable;
|
||||
}
|
||||
return node;
|
||||
|
||||
case nod_delete:
|
||||
case nod_insert:
|
||||
case nod_merge:
|
||||
@ -8515,7 +8467,6 @@ bool PASS1_set_parameter_type(DsqlCompilerScratch* dsqlScratch, dsql_nod* in_nod
|
||||
return false;
|
||||
}
|
||||
|
||||
case nod_extract:
|
||||
case nod_limit:
|
||||
case nod_rows:
|
||||
{
|
||||
@ -8615,6 +8566,7 @@ static void set_parameter_name( dsql_nod* par_node, const dsql_nod* fld_node, co
|
||||
{
|
||||
case ExprNode::TYPE_ARITHMETIC:
|
||||
case ExprNode::TYPE_CONCATENATE:
|
||||
case ExprNode::TYPE_EXTRACT:
|
||||
case ExprNode::TYPE_NEGATE:
|
||||
case ExprNode::TYPE_STR_CASE:
|
||||
case ExprNode::TYPE_STR_LEN:
|
||||
@ -8642,7 +8594,6 @@ static void set_parameter_name( dsql_nod* par_node, const dsql_nod* fld_node, co
|
||||
}
|
||||
return;
|
||||
|
||||
case nod_extract:
|
||||
case nod_limit:
|
||||
case nod_rows:
|
||||
{
|
||||
@ -8882,9 +8833,6 @@ void DSQL_pretty(const dsql_nod* node, int column)
|
||||
case nod_exec_procedure:
|
||||
verb = "execute procedure";
|
||||
break;
|
||||
case nod_extract:
|
||||
verb = "extract";
|
||||
break;
|
||||
case nod_flag:
|
||||
verb = "flag";
|
||||
break;
|
||||
|
@ -338,11 +338,6 @@ bool OPT_expression_equal2(thread_db* tdbb, CompilerScratch* csb,
|
||||
node2->nod_arg[e_cast_source], stream);
|
||||
}
|
||||
|
||||
case nod_extract:
|
||||
return node1->nod_arg[e_extract_part] == node2->nod_arg[e_extract_part] &&
|
||||
OPT_expression_equal2(tdbb, csb, node1->nod_arg[e_extract_value],
|
||||
node2->nod_arg[e_extract_value], stream);
|
||||
|
||||
case nod_list:
|
||||
{
|
||||
jrd_nod* const* ptr1 = node1->nod_arg;
|
||||
|
@ -583,24 +583,6 @@ void CMP_get_desc(thread_db* tdbb, CompilerScratch* csb, jrd_nod* node, DSC* des
|
||||
desc->dsc_flags = 0;
|
||||
return;
|
||||
|
||||
case nod_extract:
|
||||
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_literal:
|
||||
*desc = ((Literal*) node)->lit_desc;
|
||||
|
||||
@ -1226,14 +1208,6 @@ jrd_nod* NodeCopier::copy(thread_db* tdbb, jrd_nod* input)
|
||||
node->nod_arg[e_cast_fmt] = input->nod_arg[e_cast_fmt];
|
||||
return (node);
|
||||
|
||||
case nod_extract:
|
||||
node = PAR_make_node(tdbb, e_extract_length);
|
||||
node->nod_count = input->nod_count;
|
||||
node->nod_type = input->nod_type;
|
||||
node->nod_arg[e_extract_value] = copy(tdbb, input->nod_arg[e_extract_value]);
|
||||
node->nod_arg[e_extract_part] = input->nod_arg[e_extract_part];
|
||||
return (node);
|
||||
|
||||
case nod_count:
|
||||
case nod_max:
|
||||
case nod_min:
|
||||
@ -3079,7 +3053,6 @@ jrd_nod* CMP_pass2(thread_db* tdbb, CompilerScratch* csb, jrd_nod* const node, j
|
||||
case nod_null:
|
||||
case nod_scalar:
|
||||
case nod_cast:
|
||||
case nod_extract:
|
||||
case nod_derived_expr:
|
||||
{
|
||||
dsc descriptor_a;
|
||||
|
183
src/jrd/evl.cpp
183
src/jrd/evl.cpp
@ -121,7 +121,6 @@ using namespace Firebird;
|
||||
static dsc* cast(thread_db*, dsc*, const jrd_nod*, impure_value*);
|
||||
static dsc* dbkey(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* eval_statistical(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* extract(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* record_version(thread_db*, const jrd_nod*, impure_value*);
|
||||
static dsc* scalar(thread_db*, const jrd_nod*, impure_value*);
|
||||
|
||||
@ -471,9 +470,6 @@ dsc* EVL_expr(thread_db* tdbb, const jrd_nod* node)
|
||||
request->req_flags |= req_null;
|
||||
return NULL;
|
||||
|
||||
case nod_extract:
|
||||
return extract(tdbb, node, impure);
|
||||
|
||||
case nod_max:
|
||||
case nod_min:
|
||||
case nod_count:
|
||||
@ -1224,185 +1220,6 @@ static dsc* eval_statistical(thread_db* tdbb, const jrd_nod* node, impure_value*
|
||||
}
|
||||
|
||||
|
||||
// *************
|
||||
// e x t r a c t
|
||||
// *************
|
||||
// Handles EXTRACT(part FROM date/time/timestamp)
|
||||
static dsc* extract(thread_db* tdbb, const jrd_nod* node, impure_value* impure)
|
||||
{
|
||||
SET_TDBB(tdbb);
|
||||
|
||||
DEV_BLKCHK(node, type_nod);
|
||||
|
||||
const ULONG extract_part = (IPTR) node->nod_arg[e_extract_part];
|
||||
const dsc* value = EVL_expr(tdbb, node->nod_arg[e_extract_value]);
|
||||
|
||||
impure->vlu_desc.dsc_dtype = dtype_short;
|
||||
impure->vlu_desc.dsc_scale = 0;
|
||||
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_short);
|
||||
impure->vlu_desc.dsc_length = sizeof(SSHORT);
|
||||
|
||||
jrd_req* request = tdbb->getRequest();
|
||||
// CVC: Borland used special signaling for nulls in outer joins.
|
||||
if (!value || (request->req_flags & req_null))
|
||||
{
|
||||
request->req_flags |= req_null;
|
||||
impure->vlu_misc.vlu_short = 0;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
tm times = {0};
|
||||
int fractions;
|
||||
|
||||
switch (value->dsc_dtype)
|
||||
{
|
||||
case dtype_sql_time:
|
||||
switch (extract_part)
|
||||
{
|
||||
case blr_extract_hour:
|
||||
case blr_extract_minute:
|
||||
case blr_extract_second:
|
||||
case blr_extract_millisecond:
|
||||
Firebird::TimeStamp::decode_time(*(GDS_TIME*) value->dsc_address,
|
||||
×.tm_hour, ×.tm_min, ×.tm_sec, &fractions);
|
||||
break;
|
||||
default:
|
||||
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
||||
Arg::Gds(isc_invalid_extractpart_time));
|
||||
}
|
||||
break;
|
||||
|
||||
case dtype_sql_date:
|
||||
switch (extract_part)
|
||||
{
|
||||
case blr_extract_hour:
|
||||
case blr_extract_minute:
|
||||
case blr_extract_second:
|
||||
case blr_extract_millisecond:
|
||||
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
||||
Arg::Gds(isc_invalid_extractpart_date));
|
||||
break;
|
||||
default:
|
||||
Firebird::TimeStamp::decode_date(*(GDS_DATE*) value->dsc_address, ×);
|
||||
}
|
||||
break;
|
||||
|
||||
case dtype_timestamp:
|
||||
Firebird::TimeStamp::decode_timestamp(*(GDS_TIMESTAMP*) value->dsc_address,
|
||||
×, &fractions);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERR_post(Arg::Gds(isc_expression_eval_err) <<
|
||||
Arg::Gds(isc_invalidarg_extract));
|
||||
break;
|
||||
}
|
||||
|
||||
USHORT part;
|
||||
switch (extract_part)
|
||||
{
|
||||
case blr_extract_year:
|
||||
part = times.tm_year + 1900;
|
||||
break;
|
||||
case blr_extract_month:
|
||||
part = times.tm_mon + 1;
|
||||
break;
|
||||
case blr_extract_day:
|
||||
part = times.tm_mday;
|
||||
break;
|
||||
case blr_extract_hour:
|
||||
part = times.tm_hour;
|
||||
break;
|
||||
case blr_extract_minute:
|
||||
part = times.tm_min;
|
||||
break;
|
||||
|
||||
case blr_extract_second:
|
||||
impure->vlu_desc.dsc_dtype = dtype_long;
|
||||
impure->vlu_desc.dsc_length = sizeof(ULONG);
|
||||
impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
|
||||
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
||||
*(ULONG*) impure->vlu_desc.dsc_address = times.tm_sec * ISC_TIME_SECONDS_PRECISION + fractions;
|
||||
return &impure->vlu_desc;
|
||||
|
||||
case blr_extract_millisecond:
|
||||
impure->vlu_desc.dsc_dtype = dtype_long;
|
||||
impure->vlu_desc.dsc_length = sizeof(ULONG);
|
||||
impure->vlu_desc.dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE + 3;
|
||||
impure->vlu_desc.dsc_address = reinterpret_cast<UCHAR*>(&impure->vlu_misc.vlu_long);
|
||||
(*(ULONG*) impure->vlu_desc.dsc_address) = fractions;
|
||||
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
|
||||
|
||||
const int y = times.tm_year + 1900;
|
||||
const int dayOfYearNumber = times.tm_yday + 1;
|
||||
|
||||
// Find the jan1Weekday for y (Monday=1, Sunday=7)
|
||||
const int yy = (y - 1) % 100;
|
||||
const int c = (y - 1) - yy;
|
||||
const int g = yy + yy / 4;
|
||||
const int jan1Weekday = 1 + (((((c / 100) % 4) * 5) + g) % 7);
|
||||
|
||||
// Find the weekday for y m d
|
||||
const int h = dayOfYearNumber + (jan1Weekday - 1);
|
||||
const 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
|
||||
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;
|
||||
case blr_extract_weekday:
|
||||
part = times.tm_wday;
|
||||
break;
|
||||
default:
|
||||
fb_assert(false);
|
||||
part = 0;
|
||||
}
|
||||
|
||||
*(USHORT*) impure->vlu_desc.dsc_address = part;
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
||||
static dsc* record_version(thread_db* tdbb, const jrd_nod* node, impure_value* impure)
|
||||
{
|
||||
/**************************************
|
||||
|
@ -302,12 +302,6 @@ const int e_cast_fmt = 1;
|
||||
const int e_cast_iteminfo = 2;
|
||||
const int e_cast_length = 3;
|
||||
|
||||
// SQL Date supporting nodes
|
||||
const int e_extract_value = 0; // Node
|
||||
const int e_extract_part = 1; // Integer
|
||||
const int e_extract_count = 1; // Number of nodes
|
||||
const int e_extract_length = 2; // Number of entries in nod_args
|
||||
|
||||
const int e_dcl_cur_rse = 0;
|
||||
const int e_dcl_cur_refs = 1;
|
||||
const int e_dcl_cur_number = 2;
|
||||
|
@ -66,7 +66,6 @@ NODE(nod_scalar, scalar, "")
|
||||
NODE(nod_null, null, "NULL")
|
||||
NODE(nod_cast, cast, "CAST")
|
||||
NODE(nod_rec_version, record_version, "RECORD VERSION")
|
||||
NODE(nod_extract, extract, "EXTRACT")
|
||||
NODE(nod_domain_validation, domain_validation, "")
|
||||
NODE(nod_derived_expr, derived_expr, "derived_expr")
|
||||
|
||||
|
@ -126,10 +126,6 @@ bool JrdNodeVisitor::visitChildren(const JrdNode& node)
|
||||
ret |= visit(jrdNode->nod_arg[e_cast_source]);
|
||||
break;
|
||||
|
||||
case nod_extract:
|
||||
ret |= visit(jrdNode->nod_arg[e_extract_value]);
|
||||
break;
|
||||
|
||||
case nod_derived_expr:
|
||||
{
|
||||
jrd_nod* /*const*/* ptr = jrdNode->nod_arg;
|
||||
|
@ -2650,13 +2650,6 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected)
|
||||
node = par_cast(tdbb, csb);
|
||||
break;
|
||||
|
||||
case blr_extract:
|
||||
// This forced conversion looks strange, but extract_part fits in a byte
|
||||
node->nod_arg[e_extract_part] = (jrd_nod*)(U_IPTR) csb->csb_blr_reader.getByte();
|
||||
node->nod_arg[e_extract_value] = PAR_parse_node(tdbb, csb, sub_type);
|
||||
node->nod_count = e_extract_count;
|
||||
break;
|
||||
|
||||
case blr_dcl_variable:
|
||||
{
|
||||
dsc* desc = (dsc*) (node->nod_arg + e_dcl_desc);
|
||||
|
@ -196,7 +196,7 @@ static const VERB verbs[] =
|
||||
PAIR(nod_class_exprnode_jrd, blr_ansi_all, 1, 0, TYPE_BOOL, TYPE_RSE),
|
||||
|
||||
/* Improved Date Support */
|
||||
PAIR(nod_extract, blr_extract, e_extract_length, e_extract_count, VALUE, VALUE),
|
||||
PAIR(nod_class_exprnode_jrd, blr_extract, 1, 0, VALUE, VALUE),
|
||||
PAIR(nod_class_exprnode_jrd, blr_current_date, 1, 0, VALUE, OTHER),
|
||||
PAIR(nod_class_exprnode_jrd, blr_current_time, 1, 0, VALUE, OTHER),
|
||||
PAIR(nod_class_exprnode_jrd, blr_current_time2, 1, 0, VALUE, OTHER),
|
||||
|
Loading…
Reference in New Issue
Block a user