From 84148cbe4c033d912650a132fee37e0d084629ce Mon Sep 17 00:00:00 2001 From: Sergey Zefirov <72864488+mariadb-SergeyZefirov@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:55:02 +0300 Subject: [PATCH] fix(datatypes, funcexp): Overflow detection for MCOL-5568 use case (and some other) (#2987) We add intermediate calculations in int128_t when target is UBIGINT and check for overflow before converting into the UBIGINT. This is so because we can overflow on addition and multiplication, with (some) signed operands or both unsigned. --- dbcon/execplan/arithmeticoperator.h | 46 ++++++++ dbcon/mysql/ha_mcs_execplan.cpp | 1 + .../basic/r/MCOL-5568-out-of-range.result | 61 ++++++++++ .../basic/t/MCOL-5568-out-of-range.test | 109 ++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 mysql-test/columnstore/basic/r/MCOL-5568-out-of-range.result create mode 100644 mysql-test/columnstore/basic/t/MCOL-5568-out-of-range.test diff --git a/dbcon/execplan/arithmeticoperator.h b/dbcon/execplan/arithmeticoperator.h index e694e577e..33e4dfc15 100644 --- a/dbcon/execplan/arithmeticoperator.h +++ b/dbcon/execplan/arithmeticoperator.h @@ -31,6 +31,7 @@ #include "operator.h" #include "parsetree.h" +#include "mcs_datatype.h" namespace messageqcpp { @@ -238,6 +239,51 @@ inline void ArithmeticOperator::evaluate(rowgroup::Row& row, bool& isNull, Parse break; case execplan::CalpontSystemCatalog::UBIGINT: + { + // XXX: this is bandaid solution for specific customer case (MCOL-5568). + // Despite that I tried to implement a proper solution: to have operations + // performed using int128_t amd then check the result. + int128_t x, y; + bool signedLeft = lop->data()->resultType().isSignedInteger(); + bool signedRight = rop->data()->resultType().isSignedInteger(); + if (signedLeft) + { + x = static_cast(lop->getIntVal(row, isNull)); + } + else + { + x = static_cast(lop->getUintVal(row, isNull)); + } + if (signedRight) + { + y = static_cast(rop->getIntVal(row, isNull)); + } + else + { + y = static_cast(rop->getUintVal(row, isNull)); + } + int128_t result = execute(x, y, isNull); + if (!isNull && (result > MAX_UBIGINT || result < 0)) + { + logging::Message::Args args; + std::string func = ""; + switch (fOp) + { + case OP_ADD: func = "\"+\""; break; + case OP_SUB: func = "\"-\""; break; + case OP_MUL: func = "\"*\""; break; + case OP_DIV: func = "\"/\""; break; + default: break; + } + args.add(func); + args.add(static_cast(x)); + args.add(static_cast(y)); + unsigned errcode = logging::ERR_FUNC_OUT_OF_RANGE_RESULT; + throw logging::IDBExcept(logging::IDBErrorInfo::instance()->errorMsg(errcode, args), errcode); + } + fResult.uintVal = static_castdata() && (sfitempp[1]->type() == Item::FUNC_ITEM)) diff --git a/mysql-test/columnstore/basic/r/MCOL-5568-out-of-range.result b/mysql-test/columnstore/basic/r/MCOL-5568-out-of-range.result new file mode 100644 index 000000000..f00a81d3d --- /dev/null +++ b/mysql-test/columnstore/basic/r/MCOL-5568-out-of-range.result @@ -0,0 +1,61 @@ +DROP DATABASE IF EXISTS MCOL5568; +CREATE DATABASE MCOL5568; +USE MCOL5568; +CREATE TABLE test_mult ( +indemnity_paid INT(11), +n_clms TINYINT(3) UNSIGNED +) ENGINE=COLUMNSTORE; +INSERT INTO test_mult (indemnity_paid, n_clms) VALUES (-10, 1); +SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "*" using value(s): -10.000000 1.000000 +SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "+" using value(s): -10.000000 1.000000 +SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult; +indemnity_paid n_clms (indemnity_paid + 9) + n_clms +-10 1 0 +CREATE TABLE test_mult2 ( +indemnity_paid TINYINT, +n_clms TINYINT UNSIGNED +) ENGINE=COLUMNSTORE; +INSERT INTO test_mult2 (indemnity_paid, n_clms) VALUES (-10, 1); +SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult2; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "*" using value(s): -10.000000 1.000000 +SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult2; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "+" using value(s): -10.000000 1.000000 +SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult2; +indemnity_paid n_clms (indemnity_paid + 9) + n_clms +-10 1 0 +CREATE TABLE test_mult3 ( +indemnity_paid SMALLINT, +n_clms TINYINT UNSIGNED +) ENGINE=COLUMNSTORE; +INSERT INTO test_mult3 (indemnity_paid, n_clms) VALUES (-10, 1); +SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult3; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "*" using value(s): -10.000000 1.000000 +SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult3; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "+" using value(s): -10.000000 1.000000 +SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult3; +indemnity_paid n_clms (indemnity_paid + 9) + n_clms +-10 1 0 +CREATE TABLE test_mult4 ( +indemnity_paid INTEGER, +n_clms TINYINT UNSIGNED +) ENGINE=COLUMNSTORE; +INSERT INTO test_mult4 (indemnity_paid, n_clms) VALUES (-10, 1); +SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult4; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "*" using value(s): -10.000000 1.000000 +SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult4; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "+" using value(s): -10.000000 1.000000 +SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult4; +indemnity_paid n_clms (indemnity_paid + 9) + n_clms +-10 1 0 +SELECT indemnity_paid, n_clms, n_clms * indemnity_paid FROM test_mult4; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "*" using value(s): 1.000000 -10.000000 +SELECT indemnity_paid, n_clms, n_clms + indemnity_paid FROM test_mult4; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "+" using value(s): 1.000000 -10.000000 +SELECT indemnity_paid, n_clms, n_clms + (indemnity_paid + 9) FROM test_mult4; +indemnity_paid n_clms n_clms + (indemnity_paid + 9) +-10 1 0 +SELECT indemnity_paid, n_clms, n_clms - 2 FROM test_mult4; +ERROR HY000: Internal error: MCS-2053: The result is out of range for function "-" using value(s): 1.000000 2.000000 +DROP DATABASE MCOL5568; diff --git a/mysql-test/columnstore/basic/t/MCOL-5568-out-of-range.test b/mysql-test/columnstore/basic/t/MCOL-5568-out-of-range.test new file mode 100644 index 000000000..013fe3512 --- /dev/null +++ b/mysql-test/columnstore/basic/t/MCOL-5568-out-of-range.test @@ -0,0 +1,109 @@ +--disable_warnings +DROP DATABASE IF EXISTS MCOL5568; +--enable_warnings + +CREATE DATABASE MCOL5568; + +USE MCOL5568; + +CREATE TABLE test_mult ( + +indemnity_paid INT(11), + +n_clms TINYINT(3) UNSIGNED + +) ENGINE=COLUMNSTORE; + +INSERT INTO test_mult (indemnity_paid, n_clms) VALUES (-10, 1); + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult; + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult; + +# not an error. +SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult; + +CREATE TABLE test_mult2 ( + +indemnity_paid TINYINT, + +n_clms TINYINT UNSIGNED + +) ENGINE=COLUMNSTORE; + +INSERT INTO test_mult2 (indemnity_paid, n_clms) VALUES (-10, 1); + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult2; + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult2; + +# not an error. +SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult2; + +CREATE TABLE test_mult3 ( + +indemnity_paid SMALLINT, + +n_clms TINYINT UNSIGNED + +) ENGINE=COLUMNSTORE; + +INSERT INTO test_mult3 (indemnity_paid, n_clms) VALUES (-10, 1); + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult3; + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult3; + +# not an error. +SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult3; + +CREATE TABLE test_mult4 ( + +indemnity_paid INTEGER, + +n_clms TINYINT UNSIGNED + +) ENGINE=COLUMNSTORE; + +INSERT INTO test_mult4 (indemnity_paid, n_clms) VALUES (-10, 1); + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, indemnity_paid * n_clms FROM test_mult4; + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, indemnity_paid + n_clms FROM test_mult4; + +# not an error. +SELECT indemnity_paid, n_clms, (indemnity_paid + 9) + n_clms FROM test_mult4; + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, n_clms * indemnity_paid FROM test_mult4; + +# an error. +--error 1815 +SELECT indemnity_paid, n_clms, n_clms + indemnity_paid FROM test_mult4; + +# not an error. +SELECT indemnity_paid, n_clms, n_clms + (indemnity_paid + 9) FROM test_mult4; + +# an error again. +--error 1815 +SELECT indemnity_paid, n_clms, n_clms - 2 FROM test_mult4; + +DROP DATABASE MCOL5568; +