blob: 023e5427903eb0dc56d6f506e8e294cc4641bd14 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef DBCOMMON_SRC_DBCOMMON_FUNCTION_ARITHMETIC_FUNCTION_H_
#define DBCOMMON_SRC_DBCOMMON_FUNCTION_ARITHMETIC_FUNCTION_H_
#include <math.h>
#include <cfloat>
#include <cmath>
#include <functional>
#include <limits>
#include "dbcommon/common/vector-transformer.h"
#include "dbcommon/function/func.h"
#include "dbcommon/type/type-util.h"
namespace dbcommon {
template <typename Typelhs, typename Typerhs, typename Typeret>
inline int32_t addOk(Typelhs x, Typerhs y) {
Typeret sum = static_cast<Typeret>(x) + static_cast<Typeret>(y);
return !(x < 0 && y < 0 && sum >= 0) && !(x >= 0 && y >= 0 && sum < 0);
}
template <typename Typelhs, typename Typerhs, typename Typeret>
inline int32_t subOk(Typelhs x, Typerhs y) {
if (static_cast<Typeret>(y) == std::numeric_limits<Typeret>::min()) {
return x >= 0 ? 0 : 1;
}
return addOk<Typelhs, Typerhs, Typeret>(x, -y);
}
template <typename Typelhs, typename Typerhs, typename Typeret>
inline int32_t mulOk(Typelhs x, Typerhs y) {
if (x == 0 || y == 0) {
return 1;
}
volatile Typeret mul = static_cast<Typeret>(x) * static_cast<Typeret>(y);
return mul / static_cast<Typeret>(x) == static_cast<Typeret>(y);
}
template <typename Typelhs, typename Typerhs, typename Typeret,
typename Operator>
struct CheckOk {
int32_t operator()(Typelhs lhsVal, Typerhs rhsVal) const { return 1; }
};
template <typename Typelhs, typename Typerhs, typename Typeret>
struct CheckOk<Typelhs, Typerhs, Typeret, std::plus<Typeret>> {
int32_t operator()(Typelhs lhsVal, Typerhs rhsVal) const {
if (std::is_same<float, Typeret>::value ||
std::is_same<double, Typeret>::value) {
Typeret res = std::plus<Typeret>()(lhsVal, rhsVal);
if (isinf(res)) {
LOG_ERROR(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE,
"value out of range: overflow");
} else {
return 1;
}
} else {
return addOk<Typelhs, Typerhs, Typeret>(lhsVal, rhsVal);
}
}
};
template <typename Typelhs, typename Typerhs, typename Typeret>
struct CheckOk<Typelhs, Typerhs, Typeret, std::minus<Typeret>> {
int32_t operator()(Typelhs lhsVal, Typerhs rhsVal) const {
if (std::is_same<float, Typeret>::value ||
std::is_same<double, Typeret>::value) {
Typeret res = std::minus<Typeret>()(lhsVal, rhsVal);
if (isinf(res)) {
LOG_ERROR(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE,
"value out of range: overflow");
} else {
return 1;
}
} else {
return subOk<Typelhs, Typerhs, Typeret>(lhsVal, rhsVal);
}
}
};
template <typename Typelhs, typename Typerhs, typename Typeret>
struct CheckOk<Typelhs, Typerhs, Typeret, std::multiplies<Typeret>> {
int32_t operator()(Typelhs lhsVal, Typerhs rhsVal) const {
if (std::is_same<float, Typeret>::value ||
std::is_same<double, Typeret>::value) {
Typeret res = std::multiplies<Typeret>()(lhsVal, rhsVal);
if (isinf(res)) {
LOG_ERROR(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE,
"value out of range: overflow");
} else {
return 1;
}
} else {
return mulOk<Typelhs, Typerhs, Typeret>(lhsVal, rhsVal);
}
}
};
template <typename Typeret, int32_t code>
struct SendErroLog {
void operator()() const { return; }
};
template <typename Typeret>
struct SendErroLog<Typeret, ERRCODE_DIVISION_BY_ZERO> {
void operator()() const {
LOG_ERROR(ERRCODE_DIVISION_BY_ZERO, "division by zero");
}
};
template <typename Typeret>
struct SendErroLog<Typeret, ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE> {
void operator()() const {
if (std::is_same<float, Typeret>::value ||
std::is_same<double, Typeret>::value) {
LOG_ERROR(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE,
"value out of range: overflow");
} else {
LOG_ERROR(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE, "%s out of range",
pgTypeName<Typeret>());
}
}
};
template <typename Typelhs, typename Operator, typename Typerhs,
typename Typeret, bool isDivides = false, bool isNeedOverFlow = false>
Datum type1_val_op_type2_val(Datum *params, uint64_t size) {
Scalar *retScalar = params[0];
Scalar *lhsScalar = params[1];
Scalar *rhsScalar = params[2];
if (lhsScalar->isnull || rhsScalar->isnull) {
retScalar->isnull = true;
} else {
Typelhs lhsVal = DatumGetValue<Typelhs>(lhsScalar->value);
Typerhs rhsVal = DatumGetValue<Typerhs>(rhsScalar->value);
if (isDivides && rhsVal == 0)
SendErroLog<Typeret, ERRCODE_DIVISION_BY_ZERO>()();
if (isNeedOverFlow) {
if (!CheckOk<Typelhs, Typerhs, Typeret, Operator>()(lhsVal, rhsVal))
SendErroLog<Typeret, ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE>()();
}
Typeret retVal = Operator()(lhsVal, rhsVal);
retScalar->isnull = false;
retScalar->value = CreateDatum(retVal);
}
return params[0];
}
template <typename Typelhs, typename Operator, typename Typerhs,
typename Typeret, bool isDivides = false, bool isNeedOverFlow = false>
Datum type1_val_op_type2_vec(Datum *params, uint64_t size) {
// Retrieve Scalar and Vector from params
Vector *retVector = params[0];
Scalar *lhsScalar = params[1];
Vector *rhsVector = params[2];
Typelhs lhsVal = DatumGetValue<Typelhs>(lhsScalar->value);
if (isDivides && rhsVector->checkZeroValue())
SendErroLog<Typeret, ERRCODE_DIVISION_BY_ZERO>()();
// Setup return Vector
FixedSizeTypeVectorRawData<Typerhs> rhs(rhsVector);
retVector->resize(rhs.plainSize, rhs.sel, rhs.nulls);
FixedSizeTypeVectorRawData<Typeret> ret(retVector);
auto operation = [&](uint64_t plainIdx) {
Typerhs rhsVal = rhs.values[plainIdx];
if (isNeedOverFlow) {
if (!CheckOk<Typelhs, Typerhs, Typeret, Operator>()(lhsVal, rhsVal))
SendErroLog<Typeret, ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE>()();
}
ret.values[plainIdx] = Operator()(lhsVal, rhsVal);
};
transformVector(ret.plainSize, ret.sel, ret.nulls, operation);
return params[0];
}
template <typename Typelhs, typename Operator, typename Typerhs,
typename Typeret, bool isDivides = false, bool isNeedOverFlow = false>
Datum type1_vec_op_type2_val(Datum *params, uint64_t size) {
// Retrieve Scalar and Vector from params
Vector *retVector = params[0];
Vector *lhsVector = params[1];
Scalar *rhsScalar = params[2];
Typerhs rhsVal = DatumGetValue<Typerhs>(rhsScalar->value);
if (isDivides && rhsVal == 0)
SendErroLog<Typeret, ERRCODE_DIVISION_BY_ZERO>()();
// Setup return Vector
FixedSizeTypeVectorRawData<Typelhs> lhs(lhsVector);
retVector->resize(lhs.plainSize, lhs.sel, lhs.nulls);
FixedSizeTypeVectorRawData<Typeret> ret(retVector);
auto operation = [&](uint64_t plainIdx) {
Typelhs lhsVal = lhs.values[plainIdx];
if (isNeedOverFlow) {
if (!CheckOk<Typelhs, Typerhs, Typeret, Operator>()(lhsVal, rhsVal))
SendErroLog<Typeret, ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE>()();
}
ret.values[plainIdx] = Operator()(lhsVal, rhsVal);
};
transformVector(ret.plainSize, ret.sel, ret.nulls, operation);
return params[0];
}
template <typename Typelhs, typename Operator, typename Typerhs,
typename Typeret, bool isDivides = false, bool isNeedOverFlow = false>
Datum type1_vec_op_type2_vec(Datum *params, uint64_t size) {
// Retrieve Vector from params
Vector *retVector = params[0];
Vector *lhsVector = params[1];
Vector *rhsVector = params[2];
if (isDivides && rhsVector->checkZeroValue())
SendErroLog<Typeret, ERRCODE_DIVISION_BY_ZERO>()();
// Setup return Vector
FixedSizeTypeVectorRawData<Typelhs> lhs(lhsVector);
FixedSizeTypeVectorRawData<Typerhs> rhs(rhsVector);
retVector->resize(lhs.plainSize, lhs.sel, lhs.nulls, rhs.nulls);
FixedSizeTypeVectorRawData<Typeret> ret(retVector);
assert(lhs.sel == rhs.sel && lhs.plainSize == rhs.plainSize);
auto operation = [&](uint64_t plainIdx) {
Typelhs lhsVal = lhs.values[plainIdx];
Typerhs rhsVal = rhs.values[plainIdx];
if (isNeedOverFlow) {
if (!CheckOk<Typelhs, Typerhs, Typeret, Operator>()(lhsVal, rhsVal))
SendErroLog<Typeret, ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE>()();
}
ret.values[plainIdx] = Operator()(lhsVal, rhsVal);
};
transformVector(ret.plainSize, ret.sel, ret.nulls, operation);
return params[0];
}
template <dbcommon::Function vec_op_vec, dbcommon::Function vec_op_val,
dbcommon::Function val_op_vec, dbcommon::Function val_op_val>
Datum type1_op_type2_bind(Datum *params, uint64_t size) {
assert(size == 3 && "invalid input");
Object *paraL = DatumGetValue<Object *>(params[1]);
Vector *vecL = dynamic_cast<Vector *>(paraL);
Object *paraR = DatumGetValue<Object *>(params[2]);
Vector *vecR = dynamic_cast<Vector *>(paraR);
if (vecL) {
if (vecR) {
return vec_op_vec(params, size);
} else {
return vec_op_val(params, size);
}
} else {
if (vecR) {
return val_op_vec(params, size);
} else {
return val_op_val(params, size);
}
}
}
template <dbcommon::Function vec_vec_vec_op, dbcommon::Function vec_vec_val_op,
dbcommon::Function vec_val_vec_op, dbcommon::Function vec_val_val_op,
dbcommon::Function val_vec_vec_op, dbcommon::Function val_vec_val_op,
dbcommon::Function val_val_vec_op, dbcommon::Function val_val_val_op>
Datum type1_type2_type3_bind(Datum *params, uint64_t size) {
assert(size == 4 && "invalid input");
Object *para1 = DatumGetValue<Object *>(params[1]);
Vector *vec1 = dynamic_cast<Vector *>(para1);
Object *para2 = DatumGetValue<Object *>(params[2]);
Vector *vec2 = dynamic_cast<Vector *>(para2);
Object *para3 = DatumGetValue<Object *>(params[3]);
Vector *vec3 = dynamic_cast<Vector *>(para3);
if (vec1) {
if (vec2) {
if (vec3) {
return vec_vec_vec_op(params, size);
} else {
return vec_vec_val_op(params, size);
}
} else {
if (vec3) {
return vec_val_vec_op(params, size);
} else {
return vec_val_val_op(params, size);
}
}
} else {
if (vec2) {
if (vec3) {
return val_vec_vec_op(params, size);
} else {
return val_vec_val_op(params, size);
}
} else {
if (vec3) {
return val_val_vec_op(params, size);
} else {
return val_val_val_op(params, size);
}
}
}
}
template <typename Typelhs, typename Operator, typename Typerhs,
typename Typeret, bool isDivides, bool isNeedOverFlow = false>
Datum type1_op_type2(Datum *params, uint64_t size) {
return type1_op_type2_bind<
type1_vec_op_type2_vec<Typelhs, Operator, Typerhs, Typeret, isDivides,
isNeedOverFlow>,
type1_vec_op_type2_val<Typelhs, Operator, Typerhs, Typeret, isDivides,
isNeedOverFlow>,
type1_val_op_type2_vec<Typelhs, Operator, Typerhs, Typeret, isDivides,
isNeedOverFlow>,
type1_val_op_type2_val<Typelhs, Operator, Typerhs, Typeret, isDivides,
isNeedOverFlow>>(params, size);
}
} // namespace dbcommon
#endif // DBCOMMON_SRC_DBCOMMON_FUNCTION_ARITHMETIC_FUNCTION_H_