Skip to content

Commit 98f4f39

Browse files
MCOL-6018: throw error on overflows
Now we are more in line with server's behavior, if strict mode is enabled.
1 parent 3fea9bf commit 98f4f39

File tree

14 files changed

+481
-55
lines changed

14 files changed

+481
-55
lines changed

build/bootstrap_mcs.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,11 @@ run_unit_tests() {
619619

620620
message "Running unittests"
621621
cd $MARIA_BUILD_PATH
622+
# Config is needed for Unittests from buildroot
623+
if [[ $BUILD_PACKAGES = true ]]; then
624+
message "Storing Columnstore.xml to oam/etc/Columnstore.xml for unittests"
625+
cp $COLUMSNTORE_SOURCE_PATH/oam/etc/Columnstore.xml /etc/columnstore/Columnstore.xml
626+
fi
622627
${CTEST_BIN_NAME} . -R columnstore: -j $(nproc) --output-on-failure
623628
exit_code=$?
624629
cd - >/dev/null

dbcon/execplan/arithmeticoperator.h

Lines changed: 157 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@
2828
#include <iosfwd>
2929
#include <cmath>
3030
#include <sstream>
31+
#include <limits>
3132

3233
#include "operator.h"
3334
#include "parsetree.h"
3435
#include "mcs_datatype.h"
36+
#include "exceptclasses.h"
37+
#include "overflow_config.h"
3538

3639
namespace messageqcpp
3740
{
@@ -40,6 +43,7 @@ class ByteStream;
4043

4144
namespace execplan
4245
{
46+
4347
class ArithmeticOperator : public Operator
4448
{
4549
using cscType = execplan::CalpontSystemCatalog::ColType;
@@ -270,21 +274,25 @@ inline void ArithmeticOperator::evaluate(rowgroup::Row& row, bool& isNull, Parse
270274
int128_t result = execute(x, y, isNull);
271275
if (!isNull && (result > MAX_UBIGINT || result < 0))
272276
{
273-
logging::Message::Args args;
274-
std::string func = "<unknown>";
275-
switch (fOp)
276-
{
277-
case OP_ADD: func = "\"+\""; break;
278-
case OP_SUB: func = "\"-\""; break;
279-
case OP_MUL: func = "\"*\""; break;
280-
case OP_DIV: func = "\"/\""; break;
281-
default: break;
282-
}
283-
args.add(func);
284-
args.add(static_cast<double>(x));
285-
args.add(static_cast<double>(y));
286-
unsigned errcode = logging::ERR_FUNC_OUT_OF_RANGE_RESULT;
287-
throw logging::IDBExcept(logging::IDBErrorInfo::instance()->errorMsg(errcode, args), errcode);
277+
if (columnstore::isStrictOverflowMode())
278+
{
279+
logging::Message::Args args;
280+
std::string func = "<unknown>";
281+
switch (fOp)
282+
{
283+
case OP_ADD: func = "\"+\""; break;
284+
case OP_SUB: func = "\"-\""; break;
285+
case OP_MUL: func = "\"*\""; break;
286+
case OP_DIV: func = "\"/\""; break;
287+
default: break;
288+
}
289+
args.add(func);
290+
args.add(static_cast<double>(x));
291+
args.add(static_cast<double>(y));
292+
unsigned errcode = logging::ERR_FUNC_OUT_OF_RANGE_RESULT;
293+
throw logging::IDBExcept(logging::IDBErrorInfo::instance()->errorMsg(errcode, args), errcode);
294+
}
295+
isNull = true;
288296
}
289297
fResult.uintVal = static_cast<uint64_t>(result);
290298
}
@@ -344,11 +352,142 @@ inline result_t ArithmeticOperator::execute(result_t op1, result_t op2, bool& is
344352
}
345353
switch (fOp)
346354
{
347-
case OP_ADD: return op1 + op2;
355+
case OP_ADD:
356+
{
357+
// Check for addition overflow
358+
// XXX: __int128 is not signed for some reason.
359+
if constexpr (std::is_signed_v<result_t> || std::is_same<__int128, result_t>::value)
360+
{
361+
if ((op2 > 0 && op1 > std::numeric_limits<result_t>::max() - op2) ||
362+
(op2 < 0 && op1 < std::numeric_limits<result_t>::min() - op2))
363+
{
364+
if (columnstore::isStrictOverflowMode())
365+
{
366+
// Strict mode: throw exception for out of range
367+
std::ostringstream oss;
368+
oss << "BIGINT value is out of range in addition";
369+
throw logging::IDBExcept(oss.str(), logging::ERR_FUNC_OUT_OF_RANGE_RESULT);
370+
}
371+
else
372+
{
373+
// Permissive mode: set null and return clamped value
374+
isNull = true;
375+
return (op2 > 0) ? std::numeric_limits<result_t>::max() : std::numeric_limits<result_t>::min();
376+
}
377+
}
378+
}
379+
else
380+
{
381+
if (op1 > std::numeric_limits<result_t>::max() - op2)
382+
{
383+
if (columnstore::isStrictOverflowMode())
384+
{
385+
// Strict mode: throw exception for out of range
386+
std::ostringstream oss;
387+
oss << "BIGINT UNSIGNED value is out of range in addition";
388+
throw logging::IDBExcept(oss.str(), logging::ERR_FUNC_OUT_OF_RANGE_RESULT);
389+
}
390+
else
391+
{
392+
// Permissive mode: set null and return max value
393+
isNull = true;
394+
return std::numeric_limits<result_t>::max();
395+
}
396+
}
397+
}
398+
return op1 + op2;
399+
}
348400

349-
case OP_SUB: return op1 - op2;
401+
case OP_SUB:
402+
{
403+
// Check for subtraction overflow
404+
if constexpr (std::is_signed_v<result_t> || std::is_same<__int128, result_t>::value)
405+
{
406+
if ((op2 < 0 && op1 > std::numeric_limits<result_t>::max() + op2) ||
407+
(op2 > 0 && op1 < std::numeric_limits<result_t>::min() + op2))
408+
{
409+
if (columnstore::isStrictOverflowMode())
410+
{
411+
// Strict mode: throw exception for out of range
412+
std::ostringstream oss;
413+
oss << "BIGINT value is out of range in subtraction";
414+
throw logging::IDBExcept(oss.str(), logging::ERR_FUNC_OUT_OF_RANGE_RESULT);
415+
}
416+
else
417+
{
418+
// Permissive mode: set null and return clamped value
419+
isNull = true;
420+
return (op2 < 0) ? std::numeric_limits<result_t>::max() : std::numeric_limits<result_t>::min();
421+
}
422+
}
423+
}
424+
else
425+
{
426+
if (op1 < op2)
427+
{
428+
if (columnstore::isStrictOverflowMode())
429+
{
430+
// Handle underflow - throw exception for out of range
431+
std::ostringstream oss;
432+
oss << "BIGINT UNSIGNED value is out of range in subtraction";
433+
throw logging::IDBExcept(oss.str(), logging::ERR_FUNC_OUT_OF_RANGE_RESULT);
434+
}
435+
else
436+
{
437+
isNull = true;
438+
}
439+
}
440+
}
441+
return op1 - op2;
442+
}
350443

351-
case OP_MUL: return op1 * op2;
444+
case OP_MUL:
445+
{
446+
// Check for multiplication overflow
447+
if (op1 != 0 && op2 != 0)
448+
{
449+
if constexpr (std::is_signed_v<result_t> || std::is_same<__int128, result_t>::value)
450+
{
451+
// For signed types, check both positive and negative overflow
452+
if ((op1 > 0 && op2 > 0 && op1 > std::numeric_limits<result_t>::max() / op2) ||
453+
(op1 < 0 && op2 < 0 && op1 < std::numeric_limits<result_t>::max() / op2) ||
454+
(op1 > 0 && op2 < 0 && op2 < std::numeric_limits<result_t>::min() / op1) ||
455+
(op1 < 0 && op2 > 0 && op1 < std::numeric_limits<result_t>::min() / op2))
456+
{
457+
if (columnstore::isStrictOverflowMode())
458+
{
459+
// Handle overflow - throw exception for out of range
460+
std::ostringstream oss;
461+
oss << "BIGINT value is out of range in multiplication";
462+
throw logging::IDBExcept(oss.str(), logging::ERR_FUNC_OUT_OF_RANGE_RESULT);
463+
}
464+
else
465+
{
466+
isNull = true;
467+
}
468+
}
469+
}
470+
else
471+
{
472+
// For unsigned types
473+
if (op1 > std::numeric_limits<result_t>::max() / op2)
474+
{
475+
if (columnstore::isStrictOverflowMode())
476+
{
477+
// Handle overflow - throw exception for out of range
478+
std::ostringstream oss;
479+
oss << "BIGINT UNSIGNED value is out of range in multiplication";
480+
throw logging::IDBExcept(oss.str(), logging::ERR_FUNC_OUT_OF_RANGE_RESULT);
481+
}
482+
else
483+
{
484+
isNull = true;
485+
}
486+
}
487+
}
488+
}
489+
return op1 * op2;
490+
}
352491

353492
case OP_DIV:
354493
if (op2)

dbcon/mysql/ha_mcs_client_udfs.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ using namespace logging;
4646
#include "columnstoreversion.h"
4747
#include "ha_mcs_sysvars.h"
4848
#include "basic/string_utils.h"
49+
#include "oamcache.h"
4950

5051
extern "C"
5152
{
@@ -58,6 +59,7 @@ extern "C"
5859
const char* InvalidParmSize = "Invalid parameter size: Input value cannot be larger than ";
5960
const char* MsgEMIndexSizeInitErrMsg = "mcs_emindex_size() takes no arguments";
6061
const char* MsgEMIndexFreeInitErrMsg = "mcs_emindex_free() takes no arguments";
62+
const char* MsgNodesCountInitErrMsg = "mcs_nodes_count() takes no arguments";
6163

6264
const size_t Plen = strlen(SetParmsPrelude);
6365
const size_t Elen = strlen(SetParmsError);
@@ -1087,4 +1089,25 @@ extern "C"
10871089
{
10881090
}
10891091

1092+
long long mcs_nodes_count(UDF_INIT* /*initid*/, UDF_ARGS* /*args*/, char* /*is_null*/, char* /*error*/)
1093+
{
1094+
oam::OamCache* oamCache = oam::OamCache::makeOamCache();
1095+
return oamCache->getPMToDbrootsMap()->size();
1096+
}
1097+
1098+
my_bool mcs_nodes_count_init(UDF_INIT* /*initid*/, UDF_ARGS* args, char* message)
1099+
{
1100+
if (args->arg_count != 0)
1101+
{
1102+
strcpy(message, MsgNodesCountInitErrMsg);
1103+
return 1;
1104+
}
1105+
1106+
return 0;
1107+
}
1108+
1109+
void mcs_nodes_count_deinit(UDF_INIT* /*initid*/)
1110+
{
1111+
}
1112+
10901113
} // extern "C"

dbcon/mysql/install_mcs_mysql.sh.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ CREATE OR REPLACE FUNCTION mcssystemreadonly RETURNS INTEGER SONAME 'ha_columnst
5656
CREATE OR REPLACE FUNCTION mcssystemprimary RETURNS INTEGER SONAME 'ha_columnstore.so';
5757
CREATE OR REPLACE FUNCTION mcs_emindex_size RETURNS INTEGER SONAME 'ha_columnstore.so';
5858
CREATE OR REPLACE FUNCTION mcs_emindex_free RETURNS INTEGER SONAME 'ha_columnstore.so';
59+
CREATE OR REPLACE FUNCTION mcs_nodes_count RETURNS INTEGER SONAME 'ha_columnstore.so';
5960
CREATE OR REPLACE FUNCTION columnstore_dataload RETURNS STRING SONAME 'ha_columnstore.so';
6061
CREATE OR REPLACE AGGREGATE FUNCTION regr_avgx RETURNS REAL SONAME 'libregr_mysql.so';
6162
CREATE OR REPLACE AGGREGATE FUNCTION regr_avgy RETURNS REAL SONAME 'libregr_mysql.so';

mysql-test/columnstore/basic/r/MCOL-5568-out-of-range.result

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,74 @@ indemnity_paid n_clms n_clms + (indemnity_paid + 9)
5959
SELECT indemnity_paid, n_clms, n_clms - 2 FROM test_mult4;
6060
ERROR HY000: Internal error: MCS-2053: The result is out of range for function "-" using value(s): 1.000000 2.000000
6161
DROP DATABASE MCOL5568;
62+
CREATE DATABASE MCOL5568;
63+
USE MCOL5568;
64+
CREATE TABLE test_mult (
65+
indemnity_paid INT(11),
66+
n_clms TINYINT(3) UNSIGNED
67+
) ENGINE=COLUMNSTORE;
68+
INSERT INTO test_mult (indemnity_paid, n_clms) VALUES (-10, 1);
69+
SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult;
70+
indemnity_paid n_clms indemnity_paid * n_clms
71+
-10 1 NULL
72+
SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult;
73+
indemnity_paid n_clms indemnity_paid + n_clms
74+
-10 1 NULL
75+
SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult;
76+
indemnity_paid n_clms (indemnity_paid + 9) + n_clms
77+
-10 1 0
78+
CREATE TABLE test_mult2 (
79+
indemnity_paid TINYINT,
80+
n_clms TINYINT UNSIGNED
81+
) ENGINE=COLUMNSTORE;
82+
INSERT INTO test_mult2 (indemnity_paid, n_clms) VALUES (-10, 1);
83+
SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult2;
84+
indemnity_paid n_clms indemnity_paid * n_clms
85+
-10 1 NULL
86+
SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult2;
87+
indemnity_paid n_clms indemnity_paid + n_clms
88+
-10 1 NULL
89+
SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult2;
90+
indemnity_paid n_clms (indemnity_paid + 9) + n_clms
91+
-10 1 0
92+
CREATE TABLE test_mult3 (
93+
indemnity_paid SMALLINT,
94+
n_clms TINYINT UNSIGNED
95+
) ENGINE=COLUMNSTORE;
96+
INSERT INTO test_mult3 (indemnity_paid, n_clms) VALUES (-10, 1);
97+
SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult3;
98+
indemnity_paid n_clms indemnity_paid * n_clms
99+
-10 1 NULL
100+
SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult3;
101+
indemnity_paid n_clms indemnity_paid + n_clms
102+
-10 1 NULL
103+
SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult3;
104+
indemnity_paid n_clms (indemnity_paid + 9) + n_clms
105+
-10 1 0
106+
CREATE TABLE test_mult4 (
107+
indemnity_paid INTEGER,
108+
n_clms TINYINT UNSIGNED
109+
) ENGINE=COLUMNSTORE;
110+
INSERT INTO test_mult4 (indemnity_paid, n_clms) VALUES (-10, 1);
111+
SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult4;
112+
indemnity_paid n_clms indemnity_paid * n_clms
113+
-10 1 NULL
114+
SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult4;
115+
indemnity_paid n_clms indemnity_paid + n_clms
116+
-10 1 NULL
117+
SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult4;
118+
indemnity_paid n_clms (indemnity_paid + 9) + n_clms
119+
-10 1 0
120+
SELECT indemnity_paid, n_clms, n_clms * indemnity_paid FROM test_mult4;
121+
indemnity_paid n_clms n_clms * indemnity_paid
122+
-10 1 NULL
123+
SELECT indemnity_paid, n_clms, n_clms + indemnity_paid FROM test_mult4;
124+
indemnity_paid n_clms n_clms + indemnity_paid
125+
-10 1 NULL
126+
SELECT indemnity_paid, n_clms, n_clms + (indemnity_paid + 9) FROM test_mult4;
127+
indemnity_paid n_clms n_clms + (indemnity_paid + 9)
128+
-10 1 0
129+
SELECT indemnity_paid, n_clms, n_clms - 2 FROM test_mult4;
130+
indemnity_paid n_clms n_clms - 2
131+
-10 1 NULL
132+
DROP DATABASE MCOL5568;

0 commit comments

Comments
 (0)