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