/* * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * Adriano dos Santos Fernandes - refactored from pass1.cpp, gen.cpp, cmp.cpp, par.cpp and evl.cpp */ #include "firebird.h" #include #include #include "../common/classes/FpeControl.h" #include "../common/classes/VaryStr.h" #include "../dsql/ExprNodes.h" #include "../dsql/BoolNodes.h" #include "../dsql/StmtNodes.h" #include "../jrd/align.h" #include "../jrd/blr.h" #include "../jrd/tra.h" #include "../jrd/Function.h" #include "../jrd/SysFunction.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/Optimizer.h" #include "../jrd/recsrc/Cursor.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/cvt_proto.h" #include "../jrd/dpm_proto.h" #include "../common/dsc_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/exe_proto.h" #include "../jrd/fun_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/met_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/par_proto.h" #include "../dsql/ddl_proto.h" #include "../dsql/errd_proto.h" #include "../dsql/gen_proto.h" #include "../dsql/make_proto.h" #include "../dsql/metd_proto.h" #include "../dsql/pass1_proto.h" #include "../dsql/utld_proto.h" #include "../dsql/DSqlDataTypeUtil.h" #include "../jrd/DataTypeUtil.h" #include "../jrd/Collation.h" #include "../jrd/trace/TraceManager.h" #include "../jrd/trace/TraceObjects.h" #include "../jrd/trace/TraceJrdHelpers.h" using namespace Firebird; using namespace Jrd; namespace Jrd { static const long LONG_POS_MAX = 2147483647; static const SINT64 MAX_INT64_LIMIT = MAX_SINT64 / 10; static const SINT64 MIN_INT64_LIMIT = MIN_SINT64 / 10; static const SINT64 SECONDS_PER_DAY = 24 * 60 * 60; static const SINT64 ISC_TICKS_PER_DAY = SECONDS_PER_DAY * ISC_TIME_SECONDS_PRECISION; static const SCHAR DIALECT_3_TIMESTAMP_SCALE = -9; static const SCHAR DIALECT_1_TIMESTAMP_SCALE = 0; static bool couldBeDate(const dsc desc); static SINT64 getDayFraction(const dsc* d); static SINT64 getTimeStampToIscTicks(const dsc* d); static bool isDateAndTime(const dsc& d1, const dsc& d2); static void setParameterInfo(dsql_par* parameter, const dsql_ctx* context); //-------------------- void NodeRef::pass2(thread_db* tdbb, CompilerScratch* csb) { internalPass2(tdbb, csb); ExprNode* node = getExpr(); // Bind values of invariant nodes to top-level RSE (if present) if (node && (node->nodFlags & ExprNode::FLAG_INVARIANT)) { if (csb->csb_current_nodes.hasData()) { RseOrExprNode& topRseNode = csb->csb_current_nodes[0]; fb_assert(topRseNode.rseNode); if (!topRseNode.rseNode->rse_invariants) { topRseNode.rseNode->rse_invariants = FB_NEW(*tdbb->getDefaultPool()) VarInvariantArray(*tdbb->getDefaultPool()); } topRseNode.rseNode->rse_invariants->add(node->impureOffset); } } } //-------------------- void ExprNode::print(string& /*text*/) const { } bool ExprNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const { if (other->type != type) return false; size_t count = dsqlChildNodes.getCount(); if (other->dsqlChildNodes.getCount() != count) return false; const NodeRef* const* j = other->dsqlChildNodes.begin(); for (const NodeRef* const* i = dsqlChildNodes.begin(); i != dsqlChildNodes.end(); ++i, ++j) { if (!**i != !**j || !PASS1_node_match((*i)->getExpr(), (*j)->getExpr(), ignoreMapCast)) return false; } return true; } bool ExprNode::sameAs(const ExprNode* other, bool ignoreStreams) const { if (other->type != type) return false; size_t count = jrdChildNodes.getCount(); if (other->jrdChildNodes.getCount() != count) return false; const NodeRef* const* j = other->jrdChildNodes.begin(); for (const NodeRef* const* i = jrdChildNodes.begin(); i != jrdChildNodes.end(); ++i, ++j) { if (!**i && !**j) continue; if (!**i || !**j || !(*i)->getExpr()->sameAs((*j)->getExpr(), ignoreStreams)) return false; } return true; } bool ExprNode::computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* /*value*/) { for (NodeRef** i = jrdChildNodes.begin(); i != jrdChildNodes.end(); ++i) { if (**i && !(*i)->getExpr()->computable(csb, stream, allowOnlyCurrentStream)) return false; } return true; } void ExprNode::findDependentFromStreams(const OptimizerRetrieval* optRet, SortedStreamList* streamList) { for (NodeRef** i = jrdChildNodes.begin(); i != jrdChildNodes.end(); ++i) { if (**i) (*i)->getExpr()->findDependentFromStreams(optRet, streamList); } } ExprNode* ExprNode::pass1(thread_db* tdbb, CompilerScratch* csb) { for (NodeRef** i = jrdChildNodes.begin(); i != jrdChildNodes.end(); ++i) { if (**i) (*i)->pass1(tdbb, csb); } return this; } ExprNode* ExprNode::pass2(thread_db* tdbb, CompilerScratch* csb) { for (NodeRef** i = jrdChildNodes.begin(); i != jrdChildNodes.end(); ++i) { if (**i) (*i)->pass2(tdbb, csb); } return this; } //-------------------- static RegisterNode regArithmeticNodeAdd(blr_add); static RegisterNode regArithmeticNodeSubtract(blr_subtract); static RegisterNode regArithmeticNodeMultiply(blr_multiply); static RegisterNode regArithmeticNodeDivide(blr_divide); ArithmeticNode::ArithmeticNode(MemoryPool& pool, UCHAR aBlrOp, bool aDialect1, ValueExprNode* aArg1, ValueExprNode* aArg2) : TypedNode(pool), blrOp(aBlrOp), dialect1(aDialect1), label(pool), arg1(aArg1), arg2(aArg2) { switch (blrOp) { case blr_add: dsqlCompatDialectVerb = "add"; break; case blr_subtract: dsqlCompatDialectVerb = "subtract"; break; case blr_multiply: dsqlCompatDialectVerb = "multiply"; break; case blr_divide: dsqlCompatDialectVerb = "divide"; break; default: fb_assert(false); } label = dsqlCompatDialectVerb; label.upper(); addChildNode(arg1, arg1); addChildNode(arg2, arg2); } DmlNode* ArithmeticNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp) { ArithmeticNode* node = FB_NEW(pool) ArithmeticNode( pool, blrOp, (csb->blrVersion == 4)); node->arg1 = PAR_parse_value(tdbb, csb); node->arg2 = PAR_parse_value(tdbb, csb); return node; } void ArithmeticNode::print(string& text) const { text.printf("ArithmeticNode %s (%d)", label.c_str(), (dialect1 ? 1 : 3)); ExprNode::print(text); } void ArithmeticNode::setParameterName(dsql_par* parameter) const { parameter->par_name = parameter->par_alias = label; } bool ArithmeticNode::setParameterType(DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool forceVarChar) { return PASS1_set_parameter_type(dsqlScratch, arg1, desc, forceVarChar) | PASS1_set_parameter_type(dsqlScratch, arg2, desc, forceVarChar); } void ArithmeticNode::genBlr(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->appendUChar(blrOp); GEN_expr(dsqlScratch, arg1); GEN_expr(dsqlScratch, arg2); } void ArithmeticNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) { dsc desc1, desc2; MAKE_desc(dsqlScratch, &desc1, arg1); MAKE_desc(dsqlScratch, &desc2, arg2); if (desc1.isNull()) { desc1 = desc2; desc1.setNull(); } if (desc2.isNull()) { desc2 = desc1; desc2.setNull(); } if (arg1->is() && arg2->is()) { // NULL + NULL = NULL of INT desc->makeLong(0); desc->setNullable(true); } else if (dialect1) makeDialect1(desc, desc1, desc2); else makeDialect3(desc, desc1, desc2); } void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2) { USHORT dtype, dtype1, dtype2; switch (blrOp) { case blr_add: case blr_subtract: dtype1 = desc1.dsc_dtype; if (dtype_int64 == dtype1 || DTYPE_IS_TEXT(dtype1)) dtype1 = dtype_double; dtype2 = desc2.dsc_dtype; if (dtype_int64 == dtype2 || DTYPE_IS_TEXT(dtype2)) dtype2 = dtype_double; dtype = MAX(dtype1, dtype2); if (DTYPE_IS_BLOB(dtype)) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) << Arg::Gds(isc_dsql_no_blob_array)); } desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; switch (dtype) { case dtype_sql_time: case dtype_sql_date: // CVC: I don't see how this case can happen since dialect 1 doesn't accept // DATE or TIME // Forbid +- if (DTYPE_IS_TEXT(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc2.dsc_dtype)) { ERRD_post(Arg::Gds(isc_expression_eval_err) << Arg::Gds(isc_dsql_nodateortime_pm_string)); } // fall into case dtype_timestamp: // Allow +- (historical) if (couldBeDate(desc1) && couldBeDate(desc2)) { if (blrOp == blr_subtract) { // - // Legal permutations are: // - // - // - // - //