diff --git a/.clang-format b/.clang-format index ad1882a5cce3208467475b12247f0182cb4ccedc..b59f00466629084b04b0d868e931136c504e3655 100644 --- a/.clang-format +++ b/.clang-format @@ -20,7 +20,7 @@ BreakBeforeBinaryOperators: None BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BinPackParameters: true -ColumnLimit: 120 +ColumnLimit: 120 ConstructorInitializerAllOnOneLineOrOnePerLine: true DerivePointerBinding: true ExperimentalAutoDetectBinPacking: false @@ -43,7 +43,7 @@ IndentWidth: 4 TabWidth: 4 UseTab: Never BreakBeforeBraces: Custom -BraceWrapping: +BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false diff --git a/src/common/backend/parser/parse_coerce.cpp b/src/common/backend/parser/parse_coerce.cpp index 67607a67dd01be93bd9a3f9672a18445bb045f8b..df9db1006fbed3a0a00593c1b16659197134c427 100644 --- a/src/common/backend/parser/parse_coerce.cpp +++ b/src/common/backend/parser/parse_coerce.cpp @@ -770,6 +770,7 @@ Node* coerce_type(ParseState* pstate, Node* node, Oid inputTypeId, Oid targetTyp */ ccontext = COERCION_ASSIGNMENT; } + pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); if (pathtype != COERCION_PATH_NONE) { if (pathtype != COERCION_PATH_RELABELTYPE) { diff --git a/src/common/backend/parser/parse_expr.cpp b/src/common/backend/parser/parse_expr.cpp index a675487957938840dc0c22d907065a61f6d2b0d5..04e0bb5f62d6338c57b30fda16322968a7d16a57 100644 --- a/src/common/backend/parser/parse_expr.cpp +++ b/src/common/backend/parser/parse_expr.cpp @@ -542,7 +542,7 @@ Node *transformExprRecurse(ParseState *pstate, Node *expr) result = transformSubLink(pstate, (SubLink*)expr); break; - case T_SelectIntoVarList: + case T_SelectIntoVarList: result = transformSelectIntoVarList(pstate, (SelectIntoVarList*)expr); break; diff --git a/src/common/backend/parser/parse_oper.cpp b/src/common/backend/parser/parse_oper.cpp index fdb864d111e8d6a52b7059b3f3d9f2d10587e4ec..01e67a20f6d50d8b299e0fa85e765dad2ee24155 100644 --- a/src/common/backend/parser/parse_oper.cpp +++ b/src/common/backend/parser/parse_oper.cpp @@ -16,10 +16,13 @@ #include "postgres.h" #include "knl/knl_variable.h" +#include + #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "lib/stringinfo.h" #include "nodes/nodeFuncs.h" +#include "nodes/makefuncs.h" #include "parser/parse_coerce.h" #include "parser/parse_func.h" #include "parser/parse_oper.h" @@ -27,6 +30,7 @@ #include "utils/builtins.h" #include "utils/inval.h" #include "utils/lsyscache.h" +#include "utils/numeric.h" #include "utils/syscache.h" #include "utils/typcache.h" @@ -373,11 +377,82 @@ bool IsIntType(Oid typeoid) } } -bool IsTextType(Oid attr_type) +static inline int64 integer_value(Datum value, Oid type_oid) +{ + switch (type_oid) { + case INT1OID: + return DatumGetInt8(value); + case INT2OID: + return DatumGetInt16(value); + case INT4OID: + return DatumGetInt32(value); + case INT8OID: + return DatumGetInt64(value); + default: + return 0; + } +} + +static inline Datum integer_datum(int64 value, Oid type_oid) +{ + switch (type_oid) { + case INT1OID: + return Int8GetDatum(value); + case INT2OID: + return Int16GetDatum(value); + case INT4OID: + return Int32GetDatum(value); + case INT8OID: + return Int64GetDatum(value); + default: + return 0; + } +} + +static inline int64 integer_max(Oid type_oid) { + switch (type_oid) { + case INT1OID: + return std::numeric_limits::max(); + case INT2OID: + return std::numeric_limits::max(); + case INT4OID: + return std::numeric_limits::max(); + case INT8OID: + return std::numeric_limits::max(); + default: + return 0; + } +} + +static inline int64 integer_min(Oid type_oid) { + switch (type_oid) { + case INT1OID: + return std::numeric_limits::min(); + case INT2OID: + return std::numeric_limits::min(); + case INT4OID: + return std::numeric_limits::min(); + case INT8OID: + return std::numeric_limits::min(); + default: + return 0; + } +} + +static inline bool is_text_type(Oid attr_type) { return (attr_type == UNKNOWNOID) || IsCharType(attr_type); } +static inline bool is_equality_op(char* oper_name) { + return strcmp("=", oper_name) == 0 || strcmp("<>", oper_name) == 0 || strcmp("!=", oper_name) == 0 || + strcmp("^=", oper_name) == 0; +} + +static inline bool is_relational_op(char* oper_name) { + return strcmp("<", oper_name) == 0 || strcmp(">", oper_name) == 0 || strcmp("<=", oper_name) == 0 || + strcmp(">=", oper_name) == 0; +} /* oper() -- search for a binary operator * Given operator name, types of arg1 and arg2, return oper struct. @@ -409,7 +484,7 @@ Operator oper(ParseState* pstate, List* opname, Oid ltypeId, Oid rtypeId, bool n } if (DB_IS_CMPT(A_FORMAT) && ACCEPT_FLOAT_STR_AS_INT && - ((IsIntType(ltypeId) && IsTextType(rtypeId)) || (IsIntType(rtypeId) && IsTextType(ltypeId)))) { + ((IsIntType(ltypeId) && is_text_type(rtypeId)) || (IsIntType(rtypeId) && is_text_type(ltypeId)))) { ltypeId = NUMERICOID; rtypeId = NUMERICOID; } @@ -730,16 +805,17 @@ Node* parse_get_last_srf(ParseState* pstate) return NULL; } -/* - * Operator expression construction. - * - * Transform operator expression ensuring type compatibility. - * This is where some type conversion happens. - * - * As with coerce_type, pstate may be NULL if no special unknown-Param - * processing is wanted. - */ -Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, Node* last_srf, int location, bool inNumeric) +struct op_maker { + ParseState* pstate; + List* op_name; + Node* l_tree; + Node* r_tree; + Node* last_srf; + int location; + bool inNumeric; +}; + +static inline Expr* make_op_general(op_maker& maker) { Oid ltypeId, rtypeId; Operator tup; @@ -752,47 +828,47 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, Node* OpExpr* result = NULL; /* Select the operator */ - if (rtree == NULL) { + if (maker.r_tree == NULL) { /* right operator */ - ltypeId = exprType(ltree); + ltypeId = exprType(maker.l_tree); rtypeId = InvalidOid; - tup = right_oper(pstate, opname, ltypeId, false, location); - } else if (ltree == NULL) { + tup = right_oper(maker.pstate, maker.op_name, ltypeId, false, maker.location); + } else if (maker.l_tree == NULL) { /* left operator */ - rtypeId = exprType(rtree); + rtypeId = exprType(maker.r_tree); ltypeId = InvalidOid; - tup = left_oper(pstate, opname, rtypeId, false, location); + tup = left_oper(maker.pstate, maker.op_name, rtypeId, false, maker.location); } else { /* otherwise, binary operator */ - ltypeId = exprType(ltree); - rtypeId = exprType(rtree); + ltypeId = exprType(maker.l_tree); + rtypeId = exprType(maker.r_tree); /* if one of the types is a encrypted type and we are in a function/procedure creation flow */ if (IsClientLogicType(ltypeId) || IsClientLogicType(rtypeId)) { - if(pstate != NULL && pstate->p_create_proc_operator_hook) { - pstate->p_create_proc_operator_hook(pstate, ltree, rtree, <ypeId, &rtypeId); + if (maker.pstate != NULL && maker.pstate->p_create_proc_operator_hook) { + maker.pstate->p_create_proc_operator_hook(maker.pstate, maker.l_tree, maker.r_tree, <ypeId, &rtypeId); } } - tup = oper(pstate, opname, ltypeId, rtypeId, false, location, inNumeric); + tup = oper(maker.pstate, maker.op_name, ltypeId, rtypeId, false, maker.location, maker.inNumeric); } - opform = check_operator_is_shell(opname, pstate, location, tup); + opform = check_operator_is_shell(maker.op_name, maker.pstate, maker.location, tup); /* Do typecasting and build the expression tree */ - if (rtree == NULL) { + if (maker.r_tree == NULL) { /* right operator */ - args = list_make1(ltree); + args = list_make1(maker.l_tree); actual_arg_types[0] = ltypeId; declared_arg_types[0] = opform->oprleft; nargs = 1; - } else if (ltree == NULL) { + } else if (maker.l_tree == NULL) { /* left operator */ - args = list_make1(rtree); + args = list_make1(maker.r_tree); actual_arg_types[0] = rtypeId; declared_arg_types[0] = opform->oprright; nargs = 1; } else { /* otherwise, binary operator */ - args = list_make2(ltree, rtree); + args = list_make2(maker.l_tree, maker.r_tree); actual_arg_types[0] = ltypeId; actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; @@ -808,7 +884,7 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, Node* rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, nargs, opform->oprresult, false); /* perform the necessary typecasting of arguments */ - make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); + make_fn_arguments(maker.pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = makeNode(OpExpr); @@ -818,13 +894,13 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, Node* result->opretset = get_func_retset(opform->oprcode); /* opcollid and inputcollid will be set by parse_collate.c */ result->args = args; - result->location = location; + result->location = maker.location; /* if it returns a set, check that's OK */ - if (result->opretset && pstate && pstate->p_is_flt_frame) { - check_srf_call_placement(pstate, last_srf, location); + if (result->opretset && maker.pstate && maker.pstate->p_is_flt_frame) { + check_srf_call_placement(maker.pstate, maker.last_srf, maker.location); /* ... and remember it for error checks at higher levels */ - pstate->p_last_srf = (Node*)result; + maker.pstate->p_last_srf = (Node*)result; } ReleaseSysCache(tup); @@ -832,6 +908,247 @@ Expr* make_op(ParseState* pstate, List* opname, Node* ltree, Node* rtree, Node* return (Expr*)result; } +namespace var_const_pruner { + +struct pruner { + op_maker& maker; + Var*& var; + Const*& con; + Oid var_type; + Oid con_type; + char* oper_name; +}; + +struct relational_op { + bool var_left; /* Is the `var` on the left. */ + bool lt; /* Is the operator less than (`<` or `<=`). */ + bool contains_eq; /* Does the operator check equality (`<=` or `>=`). */ +}; + +static inline relational_op preproc_relational_op(pruner& pruner) +{ + return { + pruner.maker.l_tree == (Node*)pruner.var, + strcmp("<", pruner.oper_name) == 0 || strcmp("<=", pruner.oper_name) == 0, + strcmp("<=", pruner.oper_name) == 0 || strcmp(">=", pruner.oper_name) == 0, + }; +} + +static inline Expr* prune_relational_int_out_of_range(const relational_op& op, int64 integer, pruner& pruner) +{ + int64 min = integer_min(pruner.var_type); + int64 max = integer_max(pruner.var_type); + + if (op.var_left ^ op.lt) { + if (integer > max - !op.contains_eq) { + return (Expr*)makeBoolConst(false, false); + } else if (integer < min + op.contains_eq) { + return (Expr*)makeNullTest(IS_NOT_NULL, (Expr*)pruner.var); + } + } else { + if (integer > max - op.contains_eq) { + return (Expr*)makeNullTest(IS_NOT_NULL, (Expr*)pruner.var); + } else if (integer < min + !op.contains_eq) { + return (Expr*)makeBoolConst(false, false); + } + } + + return nullptr; +} + +static inline Expr* prune_equality_int_out_of_range(bool eq, int64 integer, pruner& pruner) +{ + int64 min = integer_min(pruner.var_type); + int64 max = integer_max(pruner.var_type); + + if (integer > max || integer < min) { + if (eq) { + return (Expr*)makeBoolConst(false, false); + } else { + return (Expr*)makeNullTest(IS_NOT_NULL, (Expr*)pruner.var); + } + } + + return nullptr; +} + +/** + * Try pruner comparation operators. + * Only handle the case where the left operand is an integer Var and the right operand is a integer Const. + * + * @return Pruned expression, or nullptr if pruning fails. + */ +static inline Expr* prune_int_var_int_const(pruner& pruner) +{ + /* Make sure both `var` and `con` are integers. */ + if (!IsIntType(pruner.var_type) || !IsIntType(pruner.con_type)) { + return nullptr; + } + + Expr* result = nullptr; + int64 integer = integer_value(pruner.con->constvalue, pruner.con_type); + if (is_relational_op(pruner.oper_name)) { + relational_op op = preproc_relational_op(pruner); + if (result = prune_relational_int_out_of_range(op, integer, pruner)) { + return result; + } + } else if (is_equality_op(pruner.oper_name)) { + if (result = prune_equality_int_out_of_range(strcmp("=", pruner.oper_name), integer, pruner)) { + return result; + } + } + + if (pruner.con_type != pruner.var_type) { + pruner.con = makeConst(pruner.var_type, -1, InvalidOid, sizeof(int64), integer_datum(integer, pruner.var_type), + false, true, nullptr); + } + + return make_op_general(pruner.maker); +} + +/** + * Try pruner comparation operators. + * Only handle the case where the left operand is an integer Var and the right operand is a Numeric Const. + * + * @return Pruned expression, or nullptr if pruning fails. + */ +static inline Expr* prune_int_var_num_const(pruner& pruner) +{ + /* Make sure `var` is an integer and `con` is a Numeric */ + if (!IsIntType(pruner.var_type) || pruner.con_type != NUMERICOID) { + return nullptr; + } + + Numeric num = DatumGetNumeric(pruner.con->constvalue); + NumericVar num_var = {}; + init_var_from_num(num, &num_var); + + Expr* result = nullptr; + if (is_relational_op(pruner.oper_name)) { + /* Convert `var` to an `int8`, according to the oper_name. */ + relational_op op = preproc_relational_op(pruner); + if (op.var_left ^ op.lt ^ op.contains_eq) { + floor_var(&num_var, &num_var); + } else { + ceil_var(&num_var, &num_var); + } + + int64 integer = 0; + if (numericvar_to_int64(&num_var, &integer)) { + if (result = prune_relational_int_out_of_range(op, integer, pruner)) { + return result; + } + + /* Normal case. */ + pruner.con = + makeConst(INT8OID, -1, InvalidOid, sizeof(int64), Int64GetDatum(integer), false, true, nullptr); + return make_op_general(pruner.maker); + } else { + /* Handle out of range. */ + bool neg = num_var.sign == NUMERIC_NEG; + if (op.var_left ^ op.lt ^ neg) { + return (Expr*)makeBoolConst(false, false); + } else { + return (Expr*)makeNullTest(IS_NOT_NULL, (Expr*)pruner.var); + } + } + } else if (is_equality_op(pruner.oper_name)) { + /* Check if `var` contain fractional part. */ + bool contains_frac = num_var.weight + 1 < num_var.ndigits; + if (contains_frac) { + if (strcmp("=", pruner.oper_name) == 0) { + return (Expr*)makeBoolConst(false, false); + } else { + return (Expr*)makeNullTest(IS_NOT_NULL, (Expr*)pruner.var); + } + } else { + int64 integer = 0; + if (numericvar_to_int64(&num_var, &integer)) { + if (result = prune_equality_int_out_of_range(strcmp("=", pruner.oper_name), integer, pruner)) { + return result; + } + + pruner.con = + makeConst(INT8OID, -1, InvalidOid, sizeof(int64), Int64GetDatum(integer), false, true, nullptr); + return make_op_general(pruner.maker); + } else { + return (Expr*)makeBoolConst(false, false); + } + } + } + + return make_op_general(pruner.maker); +} + +/** + * Try pruner comparation operators by converting const operand types. + * It only handles the case where the left operand is a Var and the right operand is a Const. + * If pruning fails, it will fall back to `make_op_general`. + * + * @param[in] op_name Operator name + * @param[in, out] l_tree Left operand + * @param[in, out] r_tree Right operand + * @return Pruned expression, or nullptr if pruning fails. + */ +static inline Expr* prune(op_maker& maker) +{ + /* Make sure operands are a Var and a Const */ + Node** var = &maker.l_tree; + Node** con = &maker.r_tree; + if (maker.l_tree && IsA(maker.l_tree, Const)) { + var = &maker.r_tree; + con = &maker.l_tree; + } + if (!*var || !IsA(*var, Var) || !*con || !IsA(*con, Const)) { + return nullptr; + } + + Oid var_type = ((Var*)*var)->vartype; + Oid con_type = ((Const*)*con)->consttype; + + /* Deconstruct the operator name */ + if (list_length(maker.op_name) != 1) { + return nullptr; + } + char* oper_name = strVal(linitial(maker.op_name)); + + pruner pruner = {maker, (Var*&)*var, (Const*&)*con, var_type, con_type, oper_name}; + + /* Should not use table-driven, considering better performance. */ + Expr* result = nullptr; + if (result = prune_int_var_int_const(pruner)) { + return result; + } else if (result = prune_int_var_num_const(pruner)) { + return result; + } + + return make_op_general(maker); +} + +}; // namespace var_const_pruner + +/** + * Operator expression construction. + * + * Transform operator expression ensuring type compatibility. + * This is where some type conversion happens. + * + * As with coerce_type, pstate may be NULL if no special unknown-Param + * processing is wanted. + */ +Expr* make_op(ParseState* pstate, List* op_name, Node* ltree, Node* rtree, Node* last_srf, int location, bool inNumeric) +{ + op_maker maker = {pstate, op_name, ltree, rtree, last_srf, location, inNumeric}; + + /* All the pruners go here. */ + Expr* result = nullptr; + if (result = var_const_pruner::prune(maker)) { + return result; + } + + return make_op_general(maker); +} + /* * Build expression tree for "scalar op ANY/ALL (array)" construct. */ @@ -970,14 +1287,13 @@ static bool make_oper_cache_key(OprCacheKey* key, List* opname, Oid ltypeId, Oid { char* schemaname = NULL; char* opername = NULL; - errno_t rc = EOK; /* deconstruct the name list */ DeconstructQualifiedName(opname, &schemaname, &opername); /* ensure zero-fill for stable hashing */ - rc = memset_s(key, sizeof(OprCacheKey), 0, sizeof(OprCacheKey)); - securec_check(rc, "\0", "\0"); + Assert(key != nullptr); + *key = {}; /* save operator name and input types into key */ strlcpy(key->oprname, opername, NAMEDATALEN); @@ -1009,15 +1325,12 @@ static Oid find_oper_cache_entry(OprCacheKey* key) if (u_sess->parser_cxt.opr_cache_hash == NULL) { /* First time through: initialize the hash table */ - HASHCTL ctl; - errno_t rc; - - rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl)); - securec_check(rc, "\0", "\0"); - ctl.keysize = sizeof(OprCacheKey); - ctl.entrysize = sizeof(OprCacheEntry); - ctl.hash = tag_hash; - ctl.hcxt = u_sess->cache_mem_cxt; + HASHCTL ctl = { + .keysize = sizeof(OprCacheKey), + .entrysize = sizeof(OprCacheEntry), + .hash = tag_hash, + .hcxt = u_sess->cache_mem_cxt, + }; u_sess->parser_cxt.opr_cache_hash = hash_create("Operator lookup cache", 256, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); diff --git a/src/common/backend/utils/adt/numeric.cpp b/src/common/backend/utils/adt/numeric.cpp index f25689b1ad963fb16835c3f0294665402aaa5d52..66530da019215f1a6cab573ad44dd711daafd548 100644 --- a/src/common/backend/utils/adt/numeric.cpp +++ b/src/common/backend/utils/adt/numeric.cpp @@ -236,8 +236,6 @@ static void div_var_fast(NumericVar* var1, NumericVar* var2, NumericVar* result, static void div_var_int(const NumericVar *var, int ival, int ival_weight, NumericVar *result, int rscale, bool round); static int select_div_scale(NumericVar* var1, NumericVar* var2); static void mod_var(NumericVar* var1, NumericVar* var2, NumericVar* result); -static void ceil_var(NumericVar* var, NumericVar* result); -static void floor_var(NumericVar* var, NumericVar* result); static void sqrt_var(NumericVar* arg, NumericVar* result, int rscale); static void exp_var(NumericVar* arg, NumericVar* result, int rscale); @@ -5395,23 +5393,13 @@ bool numericvar_to_int64(const NumericVar* var, int64* result, bool can_ignore) for (i = 1; i <= weight; i++) { if (unlikely(pg_mul_s64_overflow(val, NBASE, &val))) { free_var(&rounded); - if (can_ignore) { - *result = neg ? LONG_MIN : LONG_MAX; - ereport(WARNING, (errmsg("value out of range"))); - return true; - } - return false; + goto out_of_range; } if (i < ndigits) { if (unlikely(pg_sub_s64_overflow(val, digits[i], &val))) { free_var(&rounded); - if (can_ignore) { - *result = neg ? LONG_MIN : LONG_MAX; - ereport(WARNING, (errmsg("value out of range"))); - return true; - } - return false; + goto out_of_range; } } } @@ -5419,13 +5407,22 @@ bool numericvar_to_int64(const NumericVar* var, int64* result, bool can_ignore) free_var(&rounded); if (!neg) { - if (unlikely(val == PG_INT64_MIN)) - return false; + if (unlikely(val == PG_INT64_MIN)) { + goto out_of_range; + } val = -val; } *result = val; return true; + +out_of_range: + if (can_ignore) { + *result = neg ? LONG_MIN : LONG_MAX; + ereport(WARNING, (errmsg("value out of range"))); + return true; + } + return false; } /* @@ -6704,7 +6701,7 @@ static void mod_var(NumericVar* var1, NumericVar* var2, NumericVar* result) * Return the smallest integer greater than or equal to the argument * on variable level */ -static void ceil_var(NumericVar* var, NumericVar* result) +void ceil_var(NumericVar* var, NumericVar* result) { NumericVar tmp; @@ -6725,7 +6722,7 @@ static void ceil_var(NumericVar* var, NumericVar* result) * Return the largest integer equal to or less than the argument * on variable level */ -static void floor_var(NumericVar* var, NumericVar* result) +void floor_var(NumericVar* var, NumericVar* result) { NumericVar tmp; @@ -7685,7 +7682,7 @@ static void round_var(NumericVar* var, int rscale) /* * If di = 0, the value loses all digits, but could round up to 1 if its - * first extra digit is >= 5. If di < 0 the result must be 0. + * first extra digit is >= 5. If di < 0 the result must be 0. */ if (di < 0) { var->ndigits = 0; @@ -7701,10 +7698,6 @@ static void round_var(NumericVar* var, int rscale) if (ndigits < var->ndigits || (ndigits == var->ndigits && di > 0)) { var->ndigits = ndigits; -#if DEC_DIGITS == 1 - /* di must be zero */ - carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0; -#else if (di == 0) { carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0; } @@ -7712,13 +7705,7 @@ static void round_var(NumericVar* var, int rscale) /* Must round within last NBASE digit */ int extra, pow10; -#if DEC_DIGITS == 4 pow10 = round_powers[di]; -#elif DEC_DIGITS == 2 - pow10 = 10; -#else -#error unsupported NBASE -#endif extra = digits[--ndigits] % pow10; digits[ndigits] -= extra; carry = 0; @@ -7731,7 +7718,6 @@ static void round_var(NumericVar* var, int rscale) digits[ndigits] = pow10; } } -#endif /* Propagate carry if needed */ while (carry) { diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h index 6a13a4d9c77e42d30210ededacd6bd9ba4e55441..296e907cd28e65be902832c34fe6a19b080b4dd0 100644 --- a/src/include/utils/numeric.h +++ b/src/include/utils/numeric.h @@ -357,6 +357,8 @@ extern void init_var_from_num(Numeric num, NumericVar* dest); extern bool numericvar_to_int64(const NumericVar* var, int64* result, bool can_ignore = false); extern void int64_to_numericvar(int64 val, NumericVar *var); extern void add_var(NumericVar *var1, NumericVar *var2, NumericVar *result); +extern void ceil_var(NumericVar* var, NumericVar* result); +extern void floor_var(NumericVar* var, NumericVar* result); extern char *numeric_normalize(Numeric num); extern double numeric_to_double_no_overflow(Numeric num); diff --git a/src/test/regress/expected/hw_partition_hash_dql.out b/src/test/regress/expected/hw_partition_hash_dql.out index c80fcf03ac4d122abe62f0d5991e994966c52811..3566f097e4ea2fe363e91694ac3e4a2c2e0fc114 100644 --- a/src/test/regress/expected/hw_partition_hash_dql.out +++ b/src/test/regress/expected/hw_partition_hash_dql.out @@ -2024,7 +2024,7 @@ explain (costs off, verbose on) select lower(C_CHAR_3), initcap(C_VARCHAR_3), sq Iterations: 2 -> Partitioned Seq Scan on fvt_compress_qwer.select_partition_table_000_3 Output: c_ts_without, c_char_3, c_varchar_3, c_int, c_numeric - Filter: ((select_partition_table_000_3.c_int > 600) OR (select_partition_table_000_3.c_bigint < 444444)) + Filter: ((select_partition_table_000_3.c_int > 600) OR (select_partition_table_000_3.c_bigint < 444444::bigint)) Selected Partitions: 1..2 (15 rows) diff --git a/src/test/regress/expected/hw_partition_list_dql.out b/src/test/regress/expected/hw_partition_list_dql.out index c1353b7c2be45ba11f9a249e9a62d3ba003afa4f..39518d397ace2050b58850222e0c4a46f32c3fe1 100644 --- a/src/test/regress/expected/hw_partition_list_dql.out +++ b/src/test/regress/expected/hw_partition_list_dql.out @@ -2019,7 +2019,7 @@ explain (costs off, verbose on) select lower(C_CHAR_3), initcap(C_VARCHAR_3), sq Iterations: 2 -> Partitioned Seq Scan on fvt_compress_qwer.select_partition_table_000_3 Output: c_ts_without, c_char_3, c_varchar_3, c_int, c_numeric - Filter: ((select_partition_table_000_3.c_int > 600) OR (select_partition_table_000_3.c_bigint < 444444)) + Filter: ((select_partition_table_000_3.c_int > 600) OR (select_partition_table_000_3.c_bigint < 444444::bigint)) Selected Partitions: 1..2 (15 rows) diff --git a/src/test/regress/expected/optimizing_index_scan_cstore.out b/src/test/regress/expected/optimizing_index_scan_cstore.out index f211386879c1f672988f66be534636ebda41c64a..88aef17dc1c0e9812800eebaca603a45b0118d64 100755 --- a/src/test/regress/expected/optimizing_index_scan_cstore.out +++ b/src/test/regress/expected/optimizing_index_scan_cstore.out @@ -424,7 +424,7 @@ explain(costs off) select * from t1 where c5 = 2 order by c1; QUERY PLAN -------------------------------- Index Scan using t1_pkey on t1 - Filter: (c5 = 2) + Filter: (c5 = 2::bigint) (2 rows) select * from t1 where c5 = 2 order by c1; @@ -447,7 +447,7 @@ explain(costs off) select * from t1 where c5 > 3 order by (c5, c1); Sort Sort Key: (ROW(c5, c1)) -> Index Scan using t1_c3_c5 on t1 - Index Cond: (c5 > 3) + Index Cond: (c5 > 3::bigint) (4 rows) select * from t1 where c5 > 3 order by (c5, c1); @@ -460,12 +460,12 @@ select * from t1 where c5 > 3 order by (c5, c1); (4 rows) explain(costs off) select * from t1 where c5 between 1 and 4 order by (c5, c1); - QUERY PLAN ------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------- Sort Sort Key: (ROW(c5, c1)) -> Index Scan using t1_c3_c5 on t1 - Index Cond: ((c5 >= 1) AND (c5 <= 4)) + Index Cond: ((c5 >= 1::bigint) AND (c5 <= 4::bigint)) (4 rows) select * from t1 where c5 between 1 and 4 order by (c5, c1); @@ -500,12 +500,12 @@ select * from t1 where c1 between -1 and 5 and c2 between 3 and 8 order by c1; --t1:c3 c5 explain(costs off) select * from t1 where c3 > 0 and c5 < 5 order by c1; - QUERY PLAN ---------------------------------------------- + QUERY PLAN +----------------------------------------------------- Sort Sort Key: c1 -> Index Scan using t1_c3_c5 on t1 - Index Cond: ((c3 > 0) AND (c5 < 5)) + Index Cond: ((c3 > 0) AND (c5 < 5::bigint)) (4 rows) select * from t1 where c3 > 0 and c5 < 5 order by c1; @@ -520,12 +520,12 @@ select * from t1 where c3 > 0 and c5 < 5 order by c1; (6 rows) explain(costs off) select /*+ indexonlyscan(t1)*/ c3,c5 from t1 where c3 > 0 and c5 < 5 order by (c3, c5); - QUERY PLAN ---------------------------------------------- + QUERY PLAN +----------------------------------------------------- Sort Sort Key: (ROW(c3, c5)) -> Index Only Scan using t1_c3_c5 on t1 - Index Cond: ((c3 > 0) AND (c5 < 5)) + Index Cond: ((c3 > 0) AND (c5 < 5::bigint)) (4 rows) select /*+ indexonlyscan(t1)*/ c3,c5 from t1 where c3 > 0 and c5 < 5 order by (c3, c5); @@ -578,7 +578,7 @@ explain(costs off) select * from t2 where c1 < 5 order by c1; QUERY PLAN -------------------------------- Index Scan using t2_pkey on t2 - Index Cond: (c1 < 5) + Index Cond: (c1 < 5::bigint) (2 rows) select * from t2 where c1 < 5 order by c1; @@ -594,10 +594,10 @@ select * from t2 where c1 < 5 order by c1; --t2:c2 explain(costs off) select * from t2 where c2 = 1500; - QUERY PLAN ------------------------ + QUERY PLAN +------------------------------- Seq Scan on t2 - Filter: (c2 = 1500) + Filter: (c2 = 1500::bigint) (2 rows) select * from t2 where c2 = 1500; @@ -618,12 +618,12 @@ select * from t2 where c2 = 1500::bigint; (0 rows) explain(costs off) select * from t2 where c2 < 1005 order by (c2, c1); - QUERY PLAN ------------------------------ + QUERY PLAN +------------------------------------- Sort Sort Key: (ROW(c2, c1)) -> Seq Scan on t2 - Filter: (c2 < 1005) + Filter: (c2 < 1005::bigint) (4 rows) select * from t2 where c2 < 1005 order by (c2, c1); @@ -2755,12 +2755,12 @@ drop index t3_c3 cascade; --Test 3-column index create index t1_c3_c4_c5 on t1(c3,c4,c5); explain(costs off) select * from t1 where c3 > 0 and c4 < 'hijlmn' and c5 < 5 order by c1; - QUERY PLAN -------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Sort Sort Key: c1 -> Index Scan using t1_c3_c4_c5 on t1 - Index Cond: ((c3 > 0) AND ((c4)::text < 'hijlmn'::text) AND (c5 < 5)) + Index Cond: ((c3 > 0) AND ((c4)::text < 'hijlmn'::text) AND (c5 < 5::bigint)) (4 rows) select * from t1 where c3 > 0 and c4 < 'hijlmn' and c5 < 5 order by c1; @@ -2774,12 +2774,12 @@ select * from t1 where c3 > 0 and c4 < 'hijlmn' and c5 < 5 order by c1; (5 rows) explain(costs off) select /*+ indexonlyscan(t1)*/ c3,c4,c5 from t1 where c3 > 0 and c4 < 'hijlmn' and c5 < 5 order by (c3, c4, c5); - QUERY PLAN ------------------------------------------------------------------------ + QUERY PLAN +------------------------------------------------------------------------------- Sort Sort Key: (ROW(c3, c4, c5)) -> Index Only Scan using t1_c3_c4_c5 on t1 - Index Cond: ((c3 > 0) AND (c4 < 'hijlmn'::text) AND (c5 < 5)) + Index Cond: ((c3 > 0) AND (c4 < 'hijlmn'::text) AND (c5 < 5::bigint)) (4 rows) select /*+ indexonlyscan(t1)*/ c3,c4,c5 from t1 where c3 > 0 and c4 < 'hijlmn' and c5 < 5 order by (c3, c4, c5); @@ -2801,7 +2801,7 @@ explain(costs off) select * from t1 where c3 = 1 and c5 = 2; ------------------------------ Index Scan using t1_c3 on t1 Index Cond: (c3 = 1) - Filter: (c5 = 2) + Filter: (c5 = 2::bigint) (3 rows) select * from t1 where c3 = 1 and c5 = 2; diff --git a/src/test/regress/expected/optimizing_index_scan_ustore.out b/src/test/regress/expected/optimizing_index_scan_ustore.out index cd8a71bd82fd85ef043a8ef873e3197bd7f00568..14adfe204132e229d397430b24f676db311a9b62 100644 --- a/src/test/regress/expected/optimizing_index_scan_ustore.out +++ b/src/test/regress/expected/optimizing_index_scan_ustore.out @@ -151,12 +151,12 @@ select * from t_ustore where c4 = 'abcdefg' order by (c4, c1); (2 rows) explain(costs off) select * from t_ustore where c3 > 0 and c5 < 5 order by c1; - QUERY PLAN ---------------------------------------------------- + QUERY PLAN +----------------------------------------------------- Sort Sort Key: c1 -> Index Scan using t_ustore_c3_c5 on t_ustore - Index Cond: ((c3 > 0) AND (c5 < 5)) + Index Cond: ((c3 > 0) AND (c5 < 5::bigint)) (4 rows) select * from t_ustore where c3 > 0 and c5 < 5 order by c1; @@ -176,7 +176,7 @@ explain(costs off) select /*+ indexonlyscan(t_ustore)*/ c3,c5 from t_ustore wher Sort Sort Key: (ROW(c3, c5)) -> Index Only Scan using t_ustore_c3_c5 on t_ustore - Index Cond: ((c3 > 0) AND (c5 < 5)) + Index Cond: ((c3 > 0) AND (c5 < 5::bigint)) (4 rows) select /*+ indexonlyscan(t_ustore)*/ c3,c5 from t_ustore where c3 > 0 and c5 < 5 order by (c3, c5); @@ -214,10 +214,10 @@ create unique index t_ustore_c3_c4_c5 on t_ustore(c3, c4, c5); ERROR: could not create unique index "t_ustore_c3_c4_c5" DETAIL: Key (c3, c4, c5)=(1, abcdefg, 2) is duplicated. explain(costs off) select * from t_ustore where c3 > 0 and c4 < 'hijlmn' and c5 < 5 order by c1; - QUERY PLAN ---------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------- Index Scan using t_ustore_pkey on t_ustore - Filter: ((c3 > 0) AND ((c4)::text < 'hijlmn'::text) AND (c5 < 5)) + Filter: ((c3 > 0) AND ((c4)::text < 'hijlmn'::text) AND (c5 < 5::bigint)) (2 rows) select * from t_ustore where c3 > 0 and c4 < 'hijlmn' and c5 < 5 order by c1; @@ -232,12 +232,12 @@ select * from t_ustore where c3 > 0 and c4 < 'hijlmn' and c5 < 5 order by c1; explain(costs off) select /*+ indexonlyscan(t_ustore)*/ c3, c4, c5 from t_ustore where c3 > 0 and c4 < 'hijklmn' and c5 < 5 order by (c3, c4, c5); WARNING: unused hint: IndexOnlyScan(t_ustore) - QUERY PLAN ----------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------ Sort Sort Key: (ROW(c3, c4, c5)) -> Seq Scan on t_ustore - Filter: ((c3 > 0) AND ((c4)::text < 'hijklmn'::text) AND (c5 < 5)) + Filter: ((c3 > 0) AND ((c4)::text < 'hijklmn'::text) AND (c5 < 5::bigint)) (4 rows) select /*+ indexonlyscan(t_ustore)*/ c3, c4, c5 from t_ustore where c3 > 0 and c4 < 'hijklmn' and c5 < 5 order by (c3, c4, c5); diff --git a/src/test/regress/expected/seqscan_fusion.out b/src/test/regress/expected/seqscan_fusion.out index 16592a0e44f37464ce4cd970ed059d1a7aaf1b4e..11f655d281f2f321402a669c0b6c53f0284f4b74 100644 --- a/src/test/regress/expected/seqscan_fusion.out +++ b/src/test/regress/expected/seqscan_fusion.out @@ -1398,7 +1398,7 @@ explain verbose select t.b, sum(cc) from (select b, sum(c) as cc from t1 group b -> HashAggregate (cost=41.08..43.58 rows=200 width=24) Output: t1.b, sum(t1.c) Group By Key: t1.b - Filter: (sum(t1.c) > 9000) + Filter: (sum(t1.c) > 9000::bigint) -> Seq Scan on lazyagg.t1 (cost=0.00..27.76 rows=1776 width=8) Output: t1.b, t1.c (21 rows) @@ -4589,7 +4589,7 @@ explain verbose select t.b, sum(cc) from (select b, sum(c) as cc from t1 group b -> HashAggregate (cost=41.08..43.58 rows=200 width=24) Output: t1.b, sum(t1.c) Group By Key: t1.b - Filter: (sum(t1.c) > 9000) + Filter: (sum(t1.c) > 9000::bigint) -> Seq Scan on lazyagg.t1 (cost=0.00..27.76 rows=1776 width=8) Output: t1.b, t1.c (21 rows) diff --git a/src/test/regress/expected/var_eq_const_selectivity.out b/src/test/regress/expected/var_eq_const_selectivity.out index 803890c28ed31d5eb53c80c5af69e9141e269fad..442e5ee365b9569899e52d064a7c75c656f4df7f 100644 --- a/src/test/regress/expected/var_eq_const_selectivity.out +++ b/src/test/regress/expected/var_eq_const_selectivity.out @@ -696,7 +696,7 @@ explain (costs off) select a, b from test_const where a = -20000; --?.* --?.* Seq Scan on test_const - Filter: (a = (-20000)) + Filter: (a = (-20000)::smallint) (2 rows) -- const falls into histogram bucket with equal bounds @@ -705,7 +705,7 @@ explain (costs off) select a, b from test_const where a = -32768; --?.* [Bypass] Index Only Scan using idx on test_const - Index Cond: (a = (-32768)) + Index Cond: (a = (-32768)::smallint) (3 rows) -- const falls into histogram bucket with unequal bounds @@ -714,7 +714,7 @@ explain (costs off) select a, b from test_const where a = -32667; --?.* [Bypass] Index Only Scan using idx on test_const - Index Cond: (a = (-32667)) + Index Cond: (a = (-32667)::smallint) (3 rows) -- const does not belong to MCV or histogram @@ -723,7 +723,7 @@ explain (costs off) select a, b from test_const where a = 200; --?.* [Bypass] Index Only Scan using idx on test_const - Index Cond: (a = 200) + Index Cond: (a = 200::smallint) (3 rows) set var_eq_const_selectivity = on; @@ -732,7 +732,7 @@ explain (costs off) select a, b from test_const where a = -20000; --?.* --?.* Seq Scan on test_const - Filter: (a = (-20000)) + Filter: (a = (-20000)::smallint) (2 rows) -- const falls into histogram bucket with equal bounds @@ -741,7 +741,7 @@ explain (costs off) select a, b from test_const where a = -32768; --?.* [Bypass] Index Only Scan using idx on test_const - Index Cond: (a = (-32768)) + Index Cond: (a = (-32768)::smallint) (3 rows) -- const falls into histogram bucket with unequal bounds @@ -750,7 +750,7 @@ explain (costs off) select a, b from test_const where a = -32667; --?.* [Bypass] Index Only Scan using idx on test_const - Index Cond: (a = (-32667)) + Index Cond: (a = (-32667)::smallint) (3 rows) -- const does not belong to MCV or histogram @@ -759,7 +759,7 @@ explain select a, b from test_const where a = 200; --?.* [Bypass] --? Index Only Scan using idx on test_const \(cost=[0-9]*\.[0-9]*\.\.[0-9]*\.[0-9]* rows=1 width=6\) - Index Cond: (a = 200) + Index Cond: (a = 200::smallint) (3 rows) drop table test_const; @@ -858,7 +858,7 @@ explain (costs off) select a, b from test_const where a = -20000; --?.* --?.* Seq Scan on test_const - Filter: (a = (-20000)) + Filter: (a = (-20000)::bigint) (2 rows) -- const falls into histogram bucket with equal bounds @@ -894,7 +894,7 @@ explain (costs off) select a, b from test_const where a = -20000; --?.* --?.* Seq Scan on test_const - Filter: (a = (-20000)) + Filter: (a = (-20000)::bigint) (2 rows) -- const falls into histogram bucket with equal bounds