From: <asf...@us...> - 2010-09-04 21:36:50
|
Revision: 51523 http://firebird.svn.sourceforge.net/firebird/?rev=51523&view=rev Author: asfernandes Date: 2010-09-04 21:36:41 +0000 (Sat, 04 Sep 2010) Log Message: ----------- Refactor a number of expression nodes: nod_add, nod_divide, nod_multiply, nod_negate, nod_user_name, nod_subtract, nod_current_date, nod_current_time, nod_current_timestamp, nod_add2, nod_subtract2, nod_multiply2, nod_divide2, nod_current_role, nod_internal_info Modified Paths: -------------- firebird/trunk/builds/posix/make.shared.variables firebird/trunk/builds/win32/msvc10/dsql_server.vcxproj firebird/trunk/builds/win32/msvc10/dsql_server.vcxproj.filters firebird/trunk/builds/win32/msvc10/dsql_server_classic.vcxproj firebird/trunk/builds/win32/msvc10/dsql_server_classic.vcxproj.filters firebird/trunk/builds/win32/msvc10/engine.vcxproj firebird/trunk/builds/win32/msvc10/engine.vcxproj.filters firebird/trunk/builds/win32/msvc10/engine_classic.vcxproj firebird/trunk/builds/win32/msvc10/engine_classic.vcxproj.filters firebird/trunk/builds/win32/msvc10/engine_embed.vcxproj firebird/trunk/builds/win32/msvc10/engine_embed.vcxproj.filters firebird/trunk/builds/win32/msvc9/dsql_server.vcproj firebird/trunk/builds/win32/msvc9/dsql_server_classic.vcproj firebird/trunk/builds/win32/msvc9/engine.vcproj firebird/trunk/builds/win32/msvc9/engine_classic.vcproj firebird/trunk/builds/win32/msvc9/engine_embed.vcproj firebird/trunk/src/dsql/AggNodes.cpp firebird/trunk/src/dsql/ExprNodes.cpp firebird/trunk/src/dsql/ExprNodes.h firebird/trunk/src/dsql/Nodes.h firebird/trunk/src/dsql/Visitors.h firebird/trunk/src/dsql/gen.cpp firebird/trunk/src/dsql/gen_proto.h firebird/trunk/src/dsql/make.cpp firebird/trunk/src/dsql/node.h firebird/trunk/src/dsql/parse.y firebird/trunk/src/dsql/pass1.cpp firebird/trunk/src/gpre/cme.cpp firebird/trunk/src/jrd/Optimizer.cpp firebird/trunk/src/jrd/cmp.cpp firebird/trunk/src/jrd/evl.cpp firebird/trunk/src/jrd/evl_proto.h firebird/trunk/src/jrd/exe.h firebird/trunk/src/jrd/nod.h firebird/trunk/src/jrd/opt.cpp firebird/trunk/src/jrd/par.cpp firebird/trunk/src/misc/blrtable.cpp Removed Paths: ------------- firebird/trunk/src/dsql/misc_func.cpp firebird/trunk/src/dsql/misc_func.h firebird/trunk/src/jrd/misc_func_ids.h Modified: firebird/trunk/builds/posix/make.shared.variables =================================================================== --- firebird/trunk/builds/posix/make.shared.variables 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/posix/make.shared.variables 2010-09-04 21:36:41 UTC (rev 51523) @@ -80,7 +80,7 @@ DSQL_ServerFiles= metd.epp DSqlDataTypeUtil.cpp \ ddl.cpp dsql.cpp errd.cpp gen.cpp hsh.cpp make.cpp \ - movd.cpp parse.cpp Parser.cpp pass1.cpp misc_func.cpp \ + movd.cpp parse.cpp Parser.cpp pass1.cpp \ DdlNodes.epp PackageNodes.epp AggNodes.cpp BlrWriter.cpp DsqlCompilerScratch.cpp \ ExprNodes.cpp StmtNodes.cpp WinNodes.cpp Modified: firebird/trunk/builds/win32/msvc10/dsql_server.vcxproj =================================================================== --- firebird/trunk/builds/win32/msvc10/dsql_server.vcxproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/dsql_server.vcxproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -150,7 +150,6 @@ <ClCompile Include="..\..\..\src\dsql\hsh.cpp" /> <ClCompile Include="..\..\..\src\dsql\keywords.cpp" /> <ClCompile Include="..\..\..\src\dsql\make.cpp" /> - <ClCompile Include="..\..\..\src\dsql\misc_func.cpp" /> <ClCompile Include="..\..\..\src\dsql\movd.cpp" /> <ClCompile Include="..\..\..\src\dsql\parse.cpp" /> <ClCompile Include="..\..\..\src\dsql\Parser.cpp" /> @@ -194,7 +193,6 @@ <ClInclude Include="..\..\..\src\dsql\keywords.h" /> <ClInclude Include="..\..\..\src\dsql\make_proto.h" /> <ClInclude Include="..\..\..\src\dsql\metd_proto.h" /> - <ClInclude Include="..\..\..\src\dsql\misc_func.h" /> <ClInclude Include="..\..\..\src\dsql\movd_proto.h" /> <ClInclude Include="..\..\..\src\dsql\node.h" /> <ClInclude Include="..\..\..\src\dsql\Nodes.h" /> @@ -214,4 +212,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/dsql_server.vcxproj.filters =================================================================== --- firebird/trunk/builds/win32/msvc10/dsql_server.vcxproj.filters 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/dsql_server.vcxproj.filters 2010-09-04 21:36:41 UTC (rev 51523) @@ -58,9 +58,6 @@ <ClCompile Include="..\..\..\src\dsql\make.cpp"> <Filter>DSQL files</Filter> </ClCompile> - <ClCompile Include="..\..\..\src\dsql\misc_func.cpp"> - <Filter>DSQL files</Filter> - </ClCompile> <ClCompile Include="..\..\..\src\dsql\movd.cpp"> <Filter>DSQL files</Filter> </ClCompile> @@ -180,9 +177,6 @@ <ClInclude Include="..\..\..\src\dsql\metd_proto.h"> <Filter>Header files</Filter> </ClInclude> - <ClInclude Include="..\..\..\src\dsql\misc_func.h"> - <Filter>Header files</Filter> - </ClInclude> <ClInclude Include="..\..\..\src\dsql\movd_proto.h"> <Filter>Header files</Filter> </ClInclude> @@ -229,4 +223,4 @@ <Filter>Header files</Filter> </ClInclude> </ItemGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/dsql_server_classic.vcxproj =================================================================== --- firebird/trunk/builds/win32/msvc10/dsql_server_classic.vcxproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/dsql_server_classic.vcxproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -150,7 +150,6 @@ <ClCompile Include="..\..\..\src\dsql\hsh.cpp" /> <ClCompile Include="..\..\..\src\dsql\keywords.cpp" /> <ClCompile Include="..\..\..\src\dsql\make.cpp" /> - <ClCompile Include="..\..\..\src\dsql\misc_func.cpp" /> <ClCompile Include="..\..\..\src\dsql\movd.cpp" /> <ClCompile Include="..\..\..\src\dsql\parse.cpp" /> <ClCompile Include="..\..\..\src\dsql\Parser.cpp" /> @@ -194,7 +193,6 @@ <ClInclude Include="..\..\..\src\dsql\keywords.h" /> <ClInclude Include="..\..\..\src\dsql\make_proto.h" /> <ClInclude Include="..\..\..\src\dsql\metd_proto.h" /> - <ClInclude Include="..\..\..\src\dsql\misc_func.h" /> <ClInclude Include="..\..\..\src\dsql\movd_proto.h" /> <ClInclude Include="..\..\..\src\dsql\node.h" /> <ClInclude Include="..\..\..\src\dsql\Nodes.h" /> @@ -214,4 +212,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/dsql_server_classic.vcxproj.filters =================================================================== --- firebird/trunk/builds/win32/msvc10/dsql_server_classic.vcxproj.filters 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/dsql_server_classic.vcxproj.filters 2010-09-04 21:36:41 UTC (rev 51523) @@ -58,9 +58,6 @@ <ClCompile Include="..\..\..\src\dsql\make.cpp"> <Filter>DSQL files</Filter> </ClCompile> - <ClCompile Include="..\..\..\src\dsql\misc_func.cpp"> - <Filter>DSQL files</Filter> - </ClCompile> <ClCompile Include="..\..\..\src\dsql\movd.cpp"> <Filter>DSQL files</Filter> </ClCompile> @@ -180,9 +177,6 @@ <ClInclude Include="..\..\..\src\dsql\metd_proto.h"> <Filter>Header files</Filter> </ClInclude> - <ClInclude Include="..\..\..\src\dsql\misc_func.h"> - <Filter>Header files</Filter> - </ClInclude> <ClInclude Include="..\..\..\src\dsql\movd_proto.h"> <Filter>Header files</Filter> </ClInclude> @@ -229,4 +223,4 @@ <Filter>Header files</Filter> </ClInclude> </ItemGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/engine.vcxproj =================================================================== --- firebird/trunk/builds/win32/msvc10/engine.vcxproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/engine.vcxproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -398,7 +398,6 @@ <ClInclude Include="..\..\..\src\jrd\lls.h" /> <ClInclude Include="..\..\..\src\jrd\met.h" /> <ClInclude Include="..\..\..\src\jrd\met_proto.h" /> - <ClInclude Include="..\..\..\src\jrd\misc_func_ids.h" /> <ClInclude Include="..\..\..\src\jrd\mov_proto.h" /> <ClInclude Include="..\..\..\src\jrd\msg.h" /> <ClInclude Include="..\..\..\src\jrd\msg_encode.h" /> @@ -486,4 +485,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/engine.vcxproj.filters =================================================================== --- firebird/trunk/builds/win32/msvc10/engine.vcxproj.filters 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/engine.vcxproj.filters 2010-09-04 21:36:41 UTC (rev 51523) @@ -767,9 +767,6 @@ <ClInclude Include="..\..\..\src\jrd\met_proto.h"> <Filter>Header files</Filter> </ClInclude> - <ClInclude Include="..\..\..\src\jrd\misc_func_ids.h"> - <Filter>Header files</Filter> - </ClInclude> <ClInclude Include="..\..\..\src\jrd\mov_proto.h"> <Filter>Header files</Filter> </ClInclude> @@ -1020,4 +1017,4 @@ <Filter>Header files</Filter> </ClInclude> </ItemGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/engine_classic.vcxproj =================================================================== --- firebird/trunk/builds/win32/msvc10/engine_classic.vcxproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/engine_classic.vcxproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -398,7 +398,6 @@ <ClInclude Include="..\..\..\src\jrd\lls.h" /> <ClInclude Include="..\..\..\src\jrd\met.h" /> <ClInclude Include="..\..\..\src\jrd\met_proto.h" /> - <ClInclude Include="..\..\..\src\jrd\misc_func_ids.h" /> <ClInclude Include="..\..\..\src\jrd\mov_proto.h" /> <ClInclude Include="..\..\..\src\jrd\msg.h" /> <ClInclude Include="..\..\..\src\jrd\msg_encode.h" /> @@ -485,4 +484,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/engine_classic.vcxproj.filters =================================================================== --- firebird/trunk/builds/win32/msvc10/engine_classic.vcxproj.filters 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/engine_classic.vcxproj.filters 2010-09-04 21:36:41 UTC (rev 51523) @@ -767,9 +767,6 @@ <ClInclude Include="..\..\..\src\jrd\met_proto.h"> <Filter>Header files</Filter> </ClInclude> - <ClInclude Include="..\..\..\src\jrd\misc_func_ids.h"> - <Filter>Header files</Filter> - </ClInclude> <ClInclude Include="..\..\..\src\jrd\mov_proto.h"> <Filter>Header files</Filter> </ClInclude> @@ -1017,4 +1014,4 @@ <Filter>Header files</Filter> </ClInclude> </ItemGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/engine_embed.vcxproj =================================================================== --- firebird/trunk/builds/win32/msvc10/engine_embed.vcxproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/engine_embed.vcxproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -398,7 +398,6 @@ <ClInclude Include="..\..\..\src\jrd\lls.h" /> <ClInclude Include="..\..\..\src\jrd\met.h" /> <ClInclude Include="..\..\..\src\jrd\met_proto.h" /> - <ClInclude Include="..\..\..\src\jrd\misc_func_ids.h" /> <ClInclude Include="..\..\..\src\jrd\mov_proto.h" /> <ClInclude Include="..\..\..\src\jrd\msg.h" /> <ClInclude Include="..\..\..\src\jrd\msg_encode.h" /> @@ -485,4 +484,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc10/engine_embed.vcxproj.filters =================================================================== --- firebird/trunk/builds/win32/msvc10/engine_embed.vcxproj.filters 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc10/engine_embed.vcxproj.filters 2010-09-04 21:36:41 UTC (rev 51523) @@ -767,9 +767,6 @@ <ClInclude Include="..\..\..\src\jrd\met_proto.h"> <Filter>Header files</Filter> </ClInclude> - <ClInclude Include="..\..\..\src\jrd\misc_func_ids.h"> - <Filter>Header files</Filter> - </ClInclude> <ClInclude Include="..\..\..\src\jrd\mov_proto.h"> <Filter>Header files</Filter> </ClInclude> @@ -1017,4 +1014,4 @@ <Filter>Header files</Filter> </ClInclude> </ItemGroup> -</Project> \ No newline at end of file +</Project> Modified: firebird/trunk/builds/win32/msvc9/dsql_server.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc9/dsql_server.vcproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc9/dsql_server.vcproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -315,10 +315,6 @@ > </File> <File - RelativePath="..\..\..\src\dsql\misc_func.cpp" - > - </File> - <File RelativePath="..\..\..\src\dsql\movd.cpp" > </File> @@ -486,10 +482,6 @@ > </File> <File - RelativePath="..\..\..\src\dsql\misc_func.h" - > - </File> - <File RelativePath="..\..\..\src\dsql\movd_proto.h" > </File> Modified: firebird/trunk/builds/win32/msvc9/dsql_server_classic.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc9/dsql_server_classic.vcproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc9/dsql_server_classic.vcproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -315,10 +315,6 @@ > </File> <File - RelativePath="..\..\..\src\dsql\misc_func.cpp" - > - </File> - <File RelativePath="..\..\..\src\dsql\movd.cpp" > </File> @@ -486,10 +482,6 @@ > </File> <File - RelativePath="..\..\..\src\dsql\misc_func.h" - > - </File> - <File RelativePath="..\..\..\src\dsql\movd_proto.h" > </File> Modified: firebird/trunk/builds/win32/msvc9/engine.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc9/engine.vcproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc9/engine.vcproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -1276,10 +1276,6 @@ > </File> <File - RelativePath="..\..\..\src\jrd\misc_func_ids.h" - > - </File> - <File RelativePath="..\..\..\src\jrd\mov_proto.h" > </File> Modified: firebird/trunk/builds/win32/msvc9/engine_classic.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc9/engine_classic.vcproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc9/engine_classic.vcproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -1276,10 +1276,6 @@ > </File> <File - RelativePath="..\..\..\src\jrd\misc_func_ids.h" - > - </File> - <File RelativePath="..\..\..\src\jrd\mov_proto.h" > </File> Modified: firebird/trunk/builds/win32/msvc9/engine_embed.vcproj =================================================================== --- firebird/trunk/builds/win32/msvc9/engine_embed.vcproj 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/builds/win32/msvc9/engine_embed.vcproj 2010-09-04 21:36:41 UTC (rev 51523) @@ -1276,10 +1276,6 @@ > </File> <File - RelativePath="..\..\..\src\jrd\misc_func_ids.h" - > - </File> - <File RelativePath="..\..\..\src\jrd\mov_proto.h" > </File> Modified: firebird/trunk/src/dsql/AggNodes.cpp =================================================================== --- firebird/trunk/src/dsql/AggNodes.cpp 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/src/dsql/AggNodes.cpp 2010-09-04 21:36:41 UTC (rev 51523) @@ -21,6 +21,7 @@ #include "firebird.h" #include "../jrd/common.h" #include "../dsql/AggNodes.h" +#include "../dsql/ExprNodes.h" #include "../dsql/node.h" #include "../jrd/jrd.h" #include "../jrd/blr.h" @@ -586,7 +587,7 @@ else { // Initialize the result area as an int64. If the field being aggregated is approximate - // numeric, the first call to EVL_add2 will convert the descriptor to double. + // numeric, the first call to add will convert the descriptor to double. impure->make_int64(0, node->nod_scale); } } @@ -597,9 +598,9 @@ ++impure->vlux_count; if (dialect1) - EVL_add(desc, node, impure); + ArithmeticNode::add(desc, impure, node, blr_add); else - EVL_add2(desc, node, impure); + ArithmeticNode::add2(desc, impure, node, blr_add); } dsc* AvgAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const @@ -1059,7 +1060,7 @@ else { // Initialize the result area as an int64. If the field being aggregated is approximate - // numeric, the first call to EVL_add2 will convert the descriptor to double. + // numeric, the first call to add will convert the descriptor to double. impure->make_int64(0, node->nod_scale); } } @@ -1070,9 +1071,9 @@ ++impure->vlux_count; if (dialect1) - EVL_add(desc, node, impure); + ArithmeticNode::add(desc, impure, node, blr_add); else - EVL_add2(desc, node, impure); + ArithmeticNode::add2(desc, impure, node, blr_add); } dsc* SumAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const Modified: firebird/trunk/src/dsql/ExprNodes.cpp =================================================================== --- firebird/trunk/src/dsql/ExprNodes.cpp 2010-09-04 03:20:44 UTC (rev 51522) +++ firebird/trunk/src/dsql/ExprNodes.cpp 2010-09-04 21:36:41 UTC (rev 51523) @@ -19,19 +19,26 @@ */ #include "firebird.h" +#include <math.h> #include "../jrd/common.h" +#include "../common/classes/FpeControl.h" #include "../dsql/ExprNodes.h" #include "../dsql/node.h" +#include "../jrd/align.h" #include "../jrd/blr.h" +#include "../jrd/quad.h" #include "../jrd/tra.h" #include "../jrd/Function.h" #include "../jrd/SysFunction.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" +#include "../jrd/cvt_proto.h" +#include "../jrd/dsc_proto.h" #include "../jrd/evl_proto.h" #include "../jrd/intl_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" @@ -51,6 +58,22 @@ namespace Jrd { +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); + + +//-------------------- + + ExprNode* ExprNode::fromLegacy(const dsql_nod* node) { return node->nod_type == Dsql::nod_class_exprnode ? @@ -143,6 +166,2159 @@ //-------------------- +static RegisterNode<ArithmeticNode> regArithmeticNodeAdd(blr_add); +static RegisterNode<ArithmeticNode> regArithmeticNodeSubtract(blr_subtract); +static RegisterNode<ArithmeticNode> regArithmeticNodeMultiply(blr_multiply); +static RegisterNode<ArithmeticNode> regArithmeticNodeDivide(blr_divide); + +ArithmeticNode::ArithmeticNode(MemoryPool& pool, UCHAR aBlrOp, bool aDialect1, + dsql_nod* aArg1, dsql_nod* aArg2) + : TypedNode<ExprNode, ExprNode::TYPE_ARITHMETIC>(pool), + blrOp(aBlrOp), + dialect1(aDialect1), + label(pool), + dsqlArg1(aArg1), + dsqlArg2(aArg2), + arg1(NULL), + arg2(NULL) +{ + 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(dsqlArg1, arg1); + addChildNode(dsqlArg2, arg2); +} + +DmlNode* ArithmeticNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp) +{ + ArithmeticNode* node = FB_NEW(pool) ArithmeticNode( + pool, blrOp, (csb->csb_g_flags & csb_blr_version4)); + node->arg1 = PAR_parse_node(tdbb, csb, VALUE); + node->arg2 = PAR_parse_node(tdbb, csb, VALUE); + return node; +} + +void ArithmeticNode::print(string& text, Array<dsql_nod*>& nodes) const +{ + text.printf("ArithmeticNode %s (%d)", label.c_str(), (dialect1 ? 1 : 3)); + ExprNode::print(text, nodes); +} + +void ArithmeticNode::setParameterName(dsql_par* parameter) const +{ + parameter->par_name = parameter->par_alias = label; +} + +bool ArithmeticNode::setParameterType(DsqlCompilerScratch* dsqlScratch, + dsql_nod* node, bool forceVarChar) const +{ + return PASS1_set_parameter_type(dsqlScratch, dsqlArg1, node, forceVarChar) | + PASS1_set_parameter_type(dsqlScratch, dsqlArg2, node, forceVarChar); +} + +void ArithmeticNode::genBlr() +{ + dsqlScratch->appendUChar(blrOp); + GEN_expr(dsqlScratch, dsqlArg1); + GEN_expr(dsqlScratch, dsqlArg2); +} + +void ArithmeticNode::make(dsc* desc, dsql_nod* /*nullReplacement*/) +{ + dsc desc1, desc2; + + MAKE_desc(dsqlScratch, &desc1, dsqlArg1, dsqlArg2); + MAKE_desc(dsqlScratch, &desc2, dsqlArg2, dsqlArg1); + + if (dsqlArg1->nod_type == Dsql::nod_null && dsqlArg2->nod_type == Dsql::nod_null) + { + // 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 <date/time> +- <string> + 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 <timestamp> +- <string> (historical) + if (couldBeDate(desc1) && couldBeDate(desc2)) + { + if (blrOp == blr_subtract) + { + // <any date> - <any date> + + // Legal permutations are: + // <timestamp> - <timestamp> + // <timestamp> - <date> + // <date> - <date> + // <date> - <timestamp> + // <time> - <time> + // <timestamp> - <string> + // <string> - <timestamp> + // <string> - <string> + + if (DTYPE_IS_TEXT(desc1.dsc_dtype)) + dtype = dtype_timestamp; + else if (DTYPE_IS_TEXT(desc2.dsc_dtype)) + dtype = dtype_timestamp; + else if (desc1.dsc_dtype == desc2.dsc_dtype) + dtype = desc1.dsc_dtype; + else if (desc1.dsc_dtype == dtype_timestamp && + desc2.dsc_dtype == dtype_sql_date) + { + dtype = dtype_timestamp; + } + else if (desc2.dsc_dtype == dtype_timestamp && + desc1.dsc_dtype == dtype_sql_date) + { + dtype = dtype_timestamp; + } + else + { + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_datetime_subtract)); + } + + if (dtype == dtype_sql_date) + { + desc->dsc_dtype = dtype_long; + desc->dsc_length = sizeof(SLONG); + desc->dsc_scale = 0; + } + else if (dtype == dtype_sql_time) + { + desc->dsc_dtype = dtype_long; + desc->dsc_length = sizeof(SLONG); + desc->dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE; + desc->dsc_sub_type = dsc_num_type_numeric; + } + else + { + fb_assert(dtype == dtype_timestamp); + desc->dsc_dtype = dtype_double; + desc->dsc_length = sizeof(double); + desc->dsc_scale = 0; + } + } + else if (isDateAndTime(desc1, desc2)) + { + // <date> + <time> + // <time> + <date> + desc->dsc_dtype = dtype_timestamp; + desc->dsc_length = type_lengths[dtype_timestamp]; + desc->dsc_scale = 0; + } + else + { + // <date> + <date> + // <time> + <time> + // CVC: Hard to see it, since we are in dialect 1. + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_dateortime_add)); + } + } + else if (DTYPE_IS_DATE(desc1.dsc_dtype) || blrOp == blr_add) + { + // <date> +/- <non-date> + // <non-date> + <date> + desc->dsc_dtype = desc1.dsc_dtype; + if (!DTYPE_IS_DATE(desc->dsc_dtype)) + desc->dsc_dtype = desc2.dsc_dtype; + fb_assert(DTYPE_IS_DATE(desc->dsc_dtype)); + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + } + else + { + // <non-date> - <date> + fb_assert(blrOp == blr_subtract); + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_type_minus_date)); + } + break; + + case dtype_varying: + case dtype_cstring: + case dtype_text: + case dtype_double: + case dtype_real: + desc->dsc_dtype = dtype_double; + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_length = sizeof(double); + break; + + default: + desc->dsc_dtype = dtype_long; + desc->dsc_sub_type = 0; + desc->dsc_length = sizeof(SLONG); + desc->dsc_scale = MIN(NUMERIC_SCALE(desc1), NUMERIC_SCALE(desc2)); + break; + } + + break; + + case blr_multiply: + // Arrays and blobs can never partipate in multiplication + if (DTYPE_IS_BLOB(desc1.dsc_dtype) || DTYPE_IS_BLOB(desc2.dsc_dtype)) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_no_blob_array)); + } + + dtype = DSC_multiply_blr4_result[desc1.dsc_dtype][desc2.dsc_dtype]; + desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; + + switch (dtype) + { + case dtype_double: + desc->dsc_dtype = dtype_double; + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_length = sizeof(double); + break; + + case dtype_long: + desc->dsc_dtype = dtype_long; + desc->dsc_sub_type = 0; + desc->dsc_length = sizeof(SLONG); + desc->dsc_scale = NUMERIC_SCALE(desc1) + NUMERIC_SCALE(desc2); + break; + + default: + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_type_multip_dial1)); + } + + break; + + case blr_divide: + // Arrays and blobs can never partipate in division + if (DTYPE_IS_BLOB(desc1.dsc_dtype) || DTYPE_IS_BLOB(desc2.dsc_dtype)) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_no_blob_array)); + } + + 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_NUMERIC(dtype)) + { + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_mustuse_numeric_div_dial1)); + } + + desc->dsc_dtype = dtype_double; + desc->dsc_length = sizeof(double); + desc->dsc_scale = 0; + desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; + break; + } +} + +void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) +{ + USHORT dtype, dtype1, dtype2; + + switch (blrOp) + { + case blr_add: + case blr_subtract: + dtype1 = desc1.dsc_dtype; + dtype2 = desc2.dsc_dtype; + + // Arrays and blobs can never partipate in addition/subtraction + if (DTYPE_IS_BLOB(dtype1) || DTYPE_IS_BLOB(dtype2)) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_no_blob_array)); + } + + // In Dialect 2 or 3, strings can never partipate in addition / sub + // (use a specific cast instead) + if (DTYPE_IS_TEXT(dtype1) || DTYPE_IS_TEXT(dtype2)) + { + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_nostring_addsub_dial3)); + } + + // Determine the TYPE of arithmetic to perform, store it + // in dtype. Note: this is different from the result of + // the operation, as <timestamp>-<timestamp> uses + // <timestamp> arithmetic, but returns a <double> + if (DTYPE_IS_EXACT(dtype1) && DTYPE_IS_EXACT(dtype2)) + dtype = dtype_int64; + else if (DTYPE_IS_NUMERIC(dtype1) && DTYPE_IS_NUMERIC(dtype2)) + { + fb_assert(DTYPE_IS_APPROX(dtype1) || DTYPE_IS_APPROX(dtype2)); + dtype = dtype_double; + } + else + { + // mixed numeric and non-numeric: + + // The MAX(dtype) rule doesn't apply with dtype_int64 + + if (dtype_int64 == dtype1) + dtype1 = dtype_double; + if (dtype_int64 == dtype2) + dtype2 = dtype_double; + + dtype = MAX(dtype1, dtype2); + } + + desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; + + switch (dtype) + { + case dtype_sql_time: + case dtype_sql_date: + case dtype_timestamp: + if ((DTYPE_IS_DATE(dtype1) || dtype1 == dtype_unknown) && + (DTYPE_IS_DATE(dtype2) || dtype2 == dtype_unknown)) + { + if (blrOp == blr_subtract) + { + // <any date> - <any date> + // Legal permutations are: + // <timestamp> - <timestamp> + // <timestamp> - <date> + // <date> - <date> + // <date> - <timestamp> + // <time> - <time> + + if (dtype1 == dtype2) + dtype = dtype1; + else if (dtype1 == dtype_timestamp && dtype2 == dtype_sql_date) + dtype = dtype_timestamp; + else if (dtype2 == dtype_timestamp && dtype1 == dtype_sql_date) + dtype = dtype_timestamp; + else + { + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_datetime_subtract)); + } + + if (dtype == dtype_sql_date) + { + desc->dsc_dtype = dtype_long; + desc->dsc_length = sizeof(SLONG); + desc->dsc_scale = 0; + } + else if (dtype == dtype_sql_time) + { + desc->dsc_dtype = dtype_long; + desc->dsc_length = sizeof(SLONG); + desc->dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE; + desc->dsc_sub_type = dsc_num_type_numeric; + } + else + { + fb_assert(dtype == dtype_timestamp); + desc->dsc_dtype = dtype_int64; + desc->dsc_length = sizeof(SINT64); + desc->dsc_scale = -9; + desc->dsc_sub_type = dsc_num_type_numeric; + } + } + else if (isDateAndTime(desc1, desc2)) + { + // <date> + <time> + // <time> + <date> + desc->dsc_dtype = dtype_timestamp; + desc->dsc_length = type_lengths[dtype_timestamp]; + desc->dsc_scale = 0; + } + else + { + // <date> + <date> + // <time> + <time> + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_dateortime_add)); + } + } + else if (DTYPE_IS_DATE(desc1.dsc_dtype) || blrOp == blr_add) + { + // <date> +/- <non-date> + // <non-date> + <date> + desc->dsc_dtype = desc1.dsc_dtype; + if (!DTYPE_IS_DATE(desc->dsc_dtype)) + desc->dsc_dtype = desc2.dsc_dtype; + fb_assert(DTYPE_IS_DATE(desc->dsc_dtype)); + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + } + else + { + // <non-date> - <date> + fb_assert(blrOp == blr_subtract); + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_type_minus_date)); + } + break; + + case dtype_varying: + case dtype_cstring: + case dtype_text: + case dtype_double: + case dtype_real: + desc->dsc_dtype = dtype_double; + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_length = sizeof(double); + break; + + case dtype_short: + case dtype_long: + case dtype_int64: + desc->dsc_dtype = dtype_int64; + desc->dsc_sub_type = 0; + desc->dsc_length = sizeof(SINT64); + + // The result type is int64 because both operands are + // exact numeric: hence we don't need the NUMERIC_SCALE + // macro here. + fb_assert(desc1.dsc_dtype == dtype_unknown || DTYPE_IS_EXACT(desc1.dsc_dtype)); + fb_assert(desc2.dsc_dtype == dtype_unknown || DTYPE_IS_EXACT(desc2.dsc_dtype)); + + desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale); + break; + + default: + // a type which cannot participate in an add or subtract + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_type_addsub_dial3)); + } + + break; + + case blr_multiply: + // In Dialect 2 or 3, strings can never partipate in multiplication + // (use a specific cast instead) + 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_nostring_multip_dial3)); + } + + // Arrays and blobs can never partipate in multiplication + if (DTYPE_IS_BLOB(desc1.dsc_dtype) || DTYPE_IS_BLOB(desc2.dsc_dtype)) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_no_blob_array)); + } + + dtype = DSC_multiply_result[desc1.dsc_dtype][desc2.dsc_dtype]; + desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; + + switch (dtype) + { + case dtype_double: + desc->dsc_dtype = dtype_double; + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_length = sizeof(double); + break; + + case dtype_int64: + desc->dsc_dtype = dtype_int64; + desc->dsc_sub_type = 0; + desc->dsc_length = sizeof(SINT64); + desc->dsc_scale = NUMERIC_SCALE(desc1) + NUMERIC_SCALE(desc2); + break; + + default: + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_type_multip_dial3)); + } + + break; + + case blr_divide: + // In Dialect 2 or 3, strings can never partipate in division + // (use a specific cast instead) + 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_nostring_div_dial3)); + } + + // Arrays and blobs can never partipate in division + if (DTYPE_IS_BLOB(desc1.dsc_dtype) || DTYPE_IS_BLOB(desc2.dsc_dtype)) + { + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_no_blob_array)); + } + + dtype = DSC_multiply_result[desc1.dsc_dtype][desc2.dsc_dtype]; + desc->dsc_dtype = static_cast<UCHAR>(dtype); + desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; + + switch (dtype) + { + case dtype_int64: + desc->dsc_length = sizeof(SINT64); + desc->dsc_scale = NUMERIC_SCALE(desc1) + NUMERIC_SCALE(desc2); + break; + + case dtype_double: + desc->dsc_length = sizeof(double); + desc->dsc_scale = 0; + break; + + default: + ERRD_post(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_dsql_invalid_type_div_dial3)); + } + + break; + } +} + +void ArithmeticNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) +{ + dsc desc1, desc2; + + CMP_get_desc(tdbb, csb, arg1, &desc1); + CMP_get_desc(tdbb, csb, arg2, &desc2); + + if (dialect1) + getDescDialect1(tdbb, desc, desc1, desc2); + else + getDescDialect3(tdbb, desc, desc1, desc2); +} + +void ArithmeticNode::getDescDialect1(thread_db* tdbb, dsc* desc, dsc& desc1, dsc& desc2) +{ + USHORT dtype; + + switch (blrOp) + { + case blr_add: + case blr_subtract: + { + /* 92/05/29 DAVES - don't understand why this is done for ONLY + dtype_text (eg: not dtype_cstring or dtype_varying) Doesn't + appear to hurt. + + 94/04/04 DAVES - NOW I understand it! QLI will pass floating + point values to the engine as text. All other numeric constants + it turns into either integers or longs (with scale). */ + + USHORT dtype1 = desc1.dsc_dtype; + if (dtype_int64 == dtype1) + dtype1 = dtype_double; + + USHORT dtype2 = desc2.dsc_dtype; + if (dtype_int64 == dtype2) + dtype2 = dtype_double; + + if (dtype1 == dtype_text || dtype2 == dtype_text) + dtype = MAX(MAX(dtype1, dtype2), (UCHAR) DEFAULT_DOUBLE); + else + dtype = MAX(dtype1, dtype2); + + switch (dtype) + { + case dtype_short: + desc->dsc_dtype = dtype_long; + desc->dsc_length = sizeof(SLONG); + if (DTYPE_IS_TEXT(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc2.dsc_dtype)) + desc->dsc_scale = 0; + else + desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale); + + node->nod_scale = desc->dsc_scale; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + case dtype_sql_date: + case dtype_sql_time: + if (DTYPE_IS_TEXT(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc2.dsc_dtype)) + ERR_post(Arg::Gds(isc_expression_eval_err)); + // fall into + + case dtype_timestamp: + node->nod_flags |= nod_date; + + fb_assert(DTYPE_IS_DATE(desc1.dsc_dtype) || DTYPE_IS_DATE(desc2.dsc_dtype)); + + if (couldBeDate(desc1) && couldBeDate(desc2)) + { + if (blrOp == blr_subtract) + { + // <any date> - <any date> + + /* Legal permutations are: + <timestamp> - <timestamp> + <timestamp> - <date> + <date> - <date> + <date> - <timestamp> + <time> - <time> + <timestamp> - <string> + <string> - <timestamp> + <string> - <string> */ + + if (DTYPE_IS_TEXT(dtype1)) + dtype = dtype_timestamp; + else if (DTYPE_IS_TEXT(dtype2)) + dtype = dtype_timestamp; + else if (dtype1 == dtype2) + dtype = dtype1; + else if (dtype1 == dtype_timestamp && dtype2 == dtype_sql_date) + dtype = dtype_timestamp; + else if (dtype2 == dtype_timestamp && dtype1 == dtype_sql_date) + dtype = dtype_timestamp; + else + ERR_post(Arg::Gds(isc_expression_eval_err)); + + if (dtype == dtype_sql_date) + { + desc->dsc_dtype = dtype_long; + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + else if (dtype == dtype_sql_time) + { + desc->dsc_dtype = dtype_long; + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + else + { + fb_assert(dtype == dtype_timestamp); + desc->dsc_dtype = DEFAULT_DOUBLE; + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + } + else if (isDateAndTime(desc1, desc2)) + { + // <date> + <time> + // <time> + <date> + desc->dsc_dtype = dtype_timestamp; + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + else + { + // <date> + <date> + ERR_post(Arg::Gds(isc_expression_eval_err)); + } + } + else if (DTYPE_IS_DATE(desc1.dsc_dtype) || blrOp == blr_add) + { + // <date> +/- <non-date> || <non-date> + <date> + desc->dsc_dtype = desc1.dsc_dtype; + if (!DTYPE_IS_DATE(desc->dsc_dtype)) + desc->dsc_dtype = desc2.dsc_dtype; + + fb_assert(DTYPE_IS_DATE(desc->dsc_dtype)); + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + else + { + // <non-date> - <date> + ERR_post(Arg::Gds(isc_expression_eval_err)); + } + return; + + case dtype_text: + case dtype_cstring: + case dtype_varying: + case dtype_long: + case dtype_real: + case dtype_double: + node->nod_flags |= nod_double; + desc->dsc_dtype = DEFAULT_DOUBLE; + desc->dsc_length = sizeof(double); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + case dtype_unknown: + desc->dsc_dtype = dtype_unknown; + desc->dsc_length = 0; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + case dtype_quad: + node->nod_flags |= nod_quad; + desc->dsc_dtype = dtype_quad; + desc->dsc_length = sizeof(SQUAD); + if (DTYPE_IS_TEXT(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc2.dsc_dtype)) + desc->dsc_scale = 0; + else + desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale); + node->nod_scale = desc->dsc_scale; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; +#ifdef NATIVE_QUAD + return; +#endif + default: + fb_assert(false); + // FALLINTO + + case dtype_blob: + case dtype_array: + break; + } + + break; + } + + case blr_multiply: + dtype = DSC_multiply_blr4_result[desc1.dsc_dtype][desc2.dsc_dtype]; + + switch (dtype) + { + case dtype_long: + desc->dsc_dtype = dtype_long; + desc->dsc_length = sizeof(SLONG); + desc->dsc_scale = node->nod_scale = NUMERIC_SCALE(desc1) + NUMERIC_SCALE(desc2); + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + case dtype_double: + node->nod_flags |= nod_double; + desc->dsc_dtype = DEFAULT_DOUBLE; + desc->dsc_length = sizeof(double); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + case dtype_unknown: + desc->dsc_dtype = dtype_unknown; + desc->dsc_length = 0; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + default: + fb_assert(false); + // FALLINTO + + case DTYPE_CANNOT: + // break to error reporting code + break; + } + + break; + + case blr_divide: + // for compatibility with older versions of the product, we accept + // text types for division in blr_version4 (dialect <= 1) only + if (!(DTYPE_IS_NUMERIC(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc1.dsc_dtype))) + { + if (desc1.dsc_dtype != dtype_unknown) + break; // error, dtype not supported by arithmetic + } + + if (!(DTYPE_IS_NUMERIC(desc2.dsc_dtype) || DTYPE_IS_TEXT(desc2.dsc_dtype))) + { + if (desc2.dsc_dtype != dtype_unknown) + break; // error, dtype not supported by arithmetic + } + + desc->dsc_dtype = DEFAULT_DOUBLE; + desc->dsc_length = sizeof(double); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + break; + } +} + +void ArithmeticNode::getDescDialect3(thread_db* tdbb, dsc* desc, dsc& desc1, dsc& desc2) +{ + USHORT dtype; + + switch (blrOp) + { + case blr_add: + case blr_subtract: + { + USHORT dtype1 = desc1.dsc_dtype; + USHORT dtype2 = desc2.dsc_dtype; + + // In Dialect 2 or 3, strings can never partipate in addition / sub + // (use a specific cast instead) + if (DTYPE_IS_TEXT(dtype1) || DTYPE_IS_TEXT(dtype2)) + ERR_post(Arg::Gds(isc_expression_eval_err)); + + // Because dtype_int64 > dtype_double, we cannot just use the MAX macro to set + // the result dtype. The rule is that two exact numeric operands yield an int64 + // result, while an approximate numeric and anything yield a double result. + + if (DTYPE_IS_EXACT(desc1.dsc_dtype) && DTYPE_IS_EXACT(desc2.dsc_dtype)) + dtype = dtype_int64; + else if (DTYPE_IS_NUMERIC(desc1.dsc_dtype) && DTYPE_IS_NUMERIC(desc2.dsc_dtype)) + dtype = dtype_double; + else + { + // mixed numeric and non-numeric: + + fb_assert(couldBeDate(desc1) || couldBeDate(desc2)); + + // the MAX(dtype) rule doesn't apply with dtype_int64 + + if (dtype_int64 == dtype1) + dtype1 = dtype_double; + + if (dtype_int64 == dtype2) + dtype2 = dtype_double; + + dtype = MAX(dtype1, dtype2); + } + + switch (dtype) + { + case dtype_timestamp: + case dtype_sql_date: + case dtype_sql_time: + node->nod_flags |= nod_date; + + fb_assert(DTYPE_IS_DATE(desc1.dsc_dtype) || DTYPE_IS_DATE(desc2.dsc_dtype)); + + if ((DTYPE_IS_DATE(dtype1) || dtype1 == dtype_unknown) && + (DTYPE_IS_DATE(dtype2) || dtype2 == dtype_unknown)) + { + if (blrOp == blr_subtract) + { + // <any date> - <any date> + + /* Legal permutations are: + <timestamp> - <timestamp> + <timestamp> - <date> + <date> - <date> + <date> - <timestamp> + <time> - <time> */ + + if (dtype1 == dtype_unknown) + dtype1 = dtype2; + else if (dtype2 == dtype_unknown) + dtype2 = dtype1; + + if (dtype1 == dtype2) + dtype = dtype1; + else if ((dtype1 == dtype_timestamp) && (dtype2 == dtype_sql_date)) + dtype = dtype_timestamp; + else if ((dtype2 == dtype_timestamp) && (dtype1 == dtype_sql_date)) + dtype = dtype_timestamp; + else + ERR_post(Arg::Gds(isc_expression_eval_err)); + + if (dtype == dtype_sql_date) + { + desc->dsc_dtype = dtype_long; + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + else if (dtype == dtype_sql_time) + { + desc->dsc_dtype = dtype_long; + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = ISC_TIME_SECONDS_PRECISION_SCALE; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + else + { + fb_assert(dtype == dtype_timestamp || dtype == dtype_unknown); + desc->dsc_dtype = DEFAULT_DOUBLE; + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + } + else if (isDateAndTime(desc1, desc2)) + { + // <date> + <time> + // <time> + <date> + desc->dsc_dtype = dtype_timestamp; + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + else + { + // <date> + <date> + ERR_post(Arg::Gds(isc_expression_eval_err)); + } + } + else if (DTYPE_IS_DATE(desc1.dsc_dtype) || blrOp == blr_add) + { + // <date> +/- <non-date> || <non-date> + <date> + desc->dsc_dtype = desc1.dsc_dtype; + if (!DTYPE_IS_DATE(desc->dsc_dtype)) + desc->dsc_dtype = desc2.dsc_dtype; + fb_assert(DTYPE_IS_DATE(desc->dsc_dtype)); + desc->dsc_length = type_lengths[desc->dsc_dtype]; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + } + else + { + // <non-date> - <date> + ERR_post(Arg::Gds(isc_expression_eval_err)); + } + return; + + case dtype_text: + case dtype_cstring: + case dtype_varying: + case dtype_real: + case dtype_double: + node->nod_flags |= nod_double; + desc->dsc_dtype = DEFAULT_DOUBLE; + desc->dsc_length = sizeof(double); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + case dtype_short: + case dtype_long: + case dtype_int64: + desc->dsc_dtype = dtype_int64; + desc->dsc_length = sizeof(SINT64); + if (DTYPE_IS_TEXT(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc2.dsc_dtype)) + desc->dsc_scale = 0; + else + desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale); + node->nod_scale = desc->dsc_scale; + desc->dsc_sub_type = MAX(desc1.dsc_sub_type, desc2.dsc_sub_type); + desc->dsc_flags = 0; + return; + + case dtype_unknown: + desc->dsc_dtype = dtype_unknown; + desc->dsc_length = 0; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + case dtype_quad: + node->nod_flags |= nod_quad; + desc->dsc_dtype = dtype_quad; + desc->dsc_length = sizeof(SQUAD); + if (DTYPE_IS_TEXT(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc2.dsc_dtype)) + desc->dsc_scale = 0; + else + desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale); + node->nod_scale = desc->dsc_scale; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; +#ifdef NATIVE_QUAD + return; +#endif + default: + fb_assert(false); + // FALLINTO + + case dtype_blob: + case dtype_array: + break; + } + + break; + } + + case blr_multiply: + case blr_divide: + dtype = DSC_multiply_result[desc1.dsc_dtype][desc2.dsc_dtype]; + + switch (dtype) + { + case dtype_double: + node->nod_flags |= nod_double; + desc->dsc_dtype = DEFAULT_DOUBLE; + desc->dsc_length = sizeof(double); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + case dtype_int64: + desc->dsc_dtype = dtype_int64; + desc->dsc_length = sizeof(SINT64); + desc->dsc_scale = node->nod_scale = NUMERIC_SCALE(desc1) + NUMERIC_SCALE(desc2); + desc->dsc_sub_type = MAX(desc1.dsc_sub_type, desc2.dsc_sub_type); + desc->dsc_flags = 0; + return; + + case dtype_unknown: + desc->dsc_dtype = dtype_unknown; + desc->dsc_length = 0; + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + + default: + fb_assert(false); + // FALLINTO + + case DTYPE_CANNOT: + // break to error reporting code + break; + } + + break; + } +} + +ExprNode* ArithmeticNode::copy(thread_db* tdbb, NodeCopier& copier) +{ + ArithmeticNode* node = FB_NEW(*tdbb->getDefaultPool()) ArithmeticNode(*tdbb->getDefaultPool(), + blrOp, dialect1); + node->arg1 = copier.copy(tdbb, arg1); + node->arg2 = copier.copy(tdbb, arg2); + return node; +} + +bool ArithmeticNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const +{ + if (!ExprNode::dsqlMatch(other, ignoreMapCast)) + return false; + + const ArithmeticNode* o = other->as<ArithmeticNode>(); + fb_assert(o) + + return dialect1 == o->dialect1 && blrOp == o->blrOp; +} + +ExprNode* ArithmeticNode::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; +} + +dsc* ArithmeticNode::execute(thread_db* tdbb, jrd_req* request) const +{ + impure_value* const impure = request->getImpure<impure_value>(node->nod_impure); + + request->req_flags &= ~req_null; + + // Evaluate arguments. If either is null, result is null, but in + // any case, evaluate both, since some expressions may later depend + // on mappings which are developed here + + const dsc* desc1 = EVL_expr(tdbb, arg1); + const ULONG flags = request->req_flags; + request->req_flags &= ~req_null; + + const dsc* desc2 = EVL_expr(tdbb, arg2); + + // restore saved NULL state + + if (flags & req_null) + request->req_flags |= req_null; + + if (request->req_flags & req_null) + return NULL; + + EVL_make_value(tdbb, desc1, impure); + + if (dialect1) // dialect-1 semantics + { + switch (blrOp) + { + case blr_add: + case blr_subtract: + return add(desc2, impure, node, blrOp); + + case blr_divide: + { + const double divisor = MOV_get_double(desc2); + + if (divisor == 0) + { + ERR_post(Arg::Gds(isc_arith_except) << + Arg::Gds(isc_exception_float_divide_by_zero)); + } + + impure->vlu_misc.vlu_double = MOV_get_double(desc1) / divisor; + + if (isinf(impure->vlu_misc.vlu_double)) + { + ERR_post(Arg::Gds(isc_arith_except) << + Arg::Gds(isc_exception_float_overflow)); + } + + impure->vlu_desc.dsc_dtype = DEFAULT_DOUBLE; + impure->vlu_desc.dsc_length = sizeof(double); + impure->vlu_desc.dsc_address = (UCHAR*) &impure->vlu_misc; + + return &impure->vlu_desc; + } + + case blr_multiply: + return multiply(desc2, impure); + } + } + else // with dialect-3 semantics + { + switch (blrOp) + { + case blr_add: + case blr_subtract: + return add2(desc2, impure, node, blrOp); + + case blr_multiply: + return multiply2(desc2, impure); + + case blr_divide: + return divide2(desc2, impure); + } + } + + BUGCHECK(232); // msg 232 EVL_expr: invalid operation + return NULL; +} + +// Add (or subtract) the contents of a descriptor to value block, with dialect-1 semantics. + // This function can be removed when dialect-3 becomes the lowest supported dialect. (Version 7.0?) +dsc* ArithmeticNode::add(const dsc* desc, impure_value* value, const jrd_nod* node, UCHAR blrOp) +{ + DEV_BLKCHK(node, type_nod); + + const ArithmeticNode* arithmeticNode = ExprNode::as<ArithmeticNode>(node); + + fb_assert( + (arithmeticNode && arithmeticNode->dialect1 && + (arithmeticNode->blrOp == blr_add || arithmeticNode->blrOp == blr_subtract)) || + ExprNode::is<AggNode>(node) || node->nod_type == nod_total || node->nod_type == nod_average); + + dsc* const result = &value->vlu_desc; + + // Handle date arithmetic + + if (node->nod_flags & nod_date) + { + fb_assert(arithmeticNode); + return arithmeticNode->addDateTime(desc, value); + } + + // Handle floating arithmetic + + if (node->nod_flags & nod_double) + { + const double d1 = MOV_get_double(desc); + const double d2 = MOV_get_double(&value->vlu_desc); + + value->vlu_misc.vlu_double = (blrOp == blr_subtract) ? d2 - d1 : d1 + d2; + + if (isinf(value->vlu_misc.vlu_double)) + ERR_post(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_overflow)); + + result->dsc_dtype = DEFAULT_DOUBLE; + result->dsc_length = sizeof(double); + result->dsc_scale = 0; + result->dsc_sub_type = 0; + result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_double; + + return result; + } + + // Handle (oh, ugh) quad arithmetic + + if (node->nod_flags & nod_quad) + { + const SQUAD q1 = MOV_get_quad(desc, node->nod_scale); + const SQUAD q2 = MOV_get_quad(&value->vlu_desc, node->nod_scale); + + result->dsc_dtype = dtype_quad; + result->dsc_length = sizeof(SQUAD); + result->dsc_scale = node->nod_scale; + value->vlu_misc.vlu_quad = (blrOp == blr_subtract) ? + QUAD_SUBTRACT(q2, q1, ERR_post) : QUAD_ADD(q1, q2, ERR_post); + result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_quad; + + return result; + } + + // Everything else defaults to longword + + // CVC: Maybe we should upgrade the sum to double if it doesn't fit? + // This is what was done for multiplicaton in dialect 1. + + const SLONG l1 = MOV_get_long(desc, node->nod_scale); + const SINT64 l2 = MOV_get_long(&value->vlu_desc, node->nod_scale); + SINT64 rc = (blrOp == blr_subtract) ? l2 - l1 : l2 + l1; + + if (rc < MIN_SLONG || rc > MAX_SLONG) + ERR_post(Arg::Gds(isc_exception_integer_overflow)); + + value->make_long(rc, node->nod_scale); + + return result; +} + +// Add (or subtract) the contents of a descriptor to value block, with dialect-3 semantics, as in +// the blr_add, blr_subtract, and blr_agg_total verbs following a blr_version5. +dsc* ArithmeticNode::add2(const dsc* desc, impure_value* value, const jrd_nod* node, UCHAR blrOp) +{ + DEV_BLKCHK(node, type_nod); + + const ArithmeticNode* arithmeticNode = ExprNode::as<ArithmeticNode>(node); + + fb_assert( + (arithmeticNode && !arithmeticNode->dialect1 && + (arithmeticNode->blrOp == blr_add || arithmeticNode->blrOp == blr_subtract)) || + ExprNode::is<AggNode>(node) || node->nod_type == nod_average2); + + dsc* result = &value->vlu_desc; + + // Handle date arithmetic + + if (node->nod_flags & nod_date) + { + fb_assert(arithmeticNode); + return arithmeticNode->addDateTime(desc, value); + } + + // Handle floating arithmetic + + if (node->nod_flags & nod_double) + { + const double d1 = MOV_get_double(desc); + const double d2 = MOV_get_double(&value->vlu_desc); + + value->vlu_misc.vlu_double = (blrOp == blr_subtract) ? d2 - d1 : d1 + d2; + + if (isinf(value->vlu_misc.vlu_double)) + ERR_post(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_overflow)); + + result->dsc_dtype = DEFAULT_DOUBLE; + result->dsc_length = sizeof(double); + result->dsc_scale = 0; + result->dsc_sub_type = 0; + result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_double; + + return result; + } + + // Handle (oh, ugh) quad arithmetic + + if (node->nod_flags & nod_quad) + { + const SQUAD q1 = MOV_get_quad(desc, node->nod_scale); + const SQUAD q2 = MOV_get_quad(&value->vlu_desc, node->nod_scale); + + result->dsc_dtype = dtype_quad; + result->dsc_length = sizeof(SQUAD); + result->dsc_scale = node->nod_scale; + value->vlu_misc.vlu_quad = (blrOp == blr_subtract) ? + QUAD_SUBTRACT(q2, q1, ERR_post) : QUAD_ADD(q1, q2, ERR_post); + result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_quad; + + return result; + } + + // Everything else defaults to int64 + + SINT64 i1 = MOV_get_int64(desc, node->nod_scale); + const SINT64 i2 = MOV_get_int64(&value->vlu_desc, node->nod_scale); + + result->dsc_dtype = dtype_int64; + result->dsc_length = sizeof(SINT64); + result->dsc_scale = node->nod_scale; + value->vlu_misc.vlu_int64 = (blrOp == blr_subtract) ? i2 - i1 : i1 + i2; + result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_int64; + result->dsc_sub_type = MAX(desc->dsc_sub_type, value->vlu_desc.dsc_sub_type); + + /* If the operands of an addition have the same sign, and their sum has + the opposite sign, then overflow occurred. If the two addends have + opposite signs, then the result will lie between the two addends, and + overflow cannot occur. + If this is a subtraction, note that we invert the sign bit, rather than + negating the argument, so that subtraction of MIN_SINT64, which is + unchanged by negation, will be correctly treated like the addition of + a positive number for the purposes of this test. + + Test cases for a Gedankenexperiment, considering the sign bits of the + operands and result after the inversion below: L Rt Sum + + MIN_SINT64 - MIN_SINT64 == 0, with no overflow 1 0 0 + -MAX_SINT64 - MIN_SINT64 == 1, with no overflow 1 0 0 + 1 - MIN_SINT64 == overflow 0 0 1 + -1 - MIN_SINT64 == MAX_SINT64, no overflow 1 0 0 + */ + + if (blrOp == blr_subtract) + i1 ^= MIN_SINT64; // invert the sign bit + + if ((i1 ^ i2) >= 0 && (i1 ^ value->vlu_misc.vlu_int64) < 0) + ERR_post(Arg::Gds(isc_exception_integer_overflow)); + + return result; +} + +// Multiply two numbers, with SQL dialect-1 semantics. +// This function can be removed when dialect-3 becomes the lowest supported dialect. (Version 7.0?) +dsc* ArithmeticNode::multiply(const dsc* desc, impure_value* value) const +{ + DEV_BLKCHK(node, type_nod); + + // Handle floating arithmetic + + if (n... [truncated message content] |