diff --git a/src/common/backend/parser/parse_coerce.cpp b/src/common/backend/parser/parse_coerce.cpp index 53da1de5ba4e1ae6758070eb2ef290941d0ad4d4..b2803a9cf85ed3ebe91fc943c5704470f61038e2 100644 --- a/src/common/backend/parser/parse_coerce.cpp +++ b/src/common/backend/parser/parse_coerce.cpp @@ -573,6 +573,151 @@ Node* coerce_type(ParseState* pstate, Node* node, Oid inputTypeId, Oid targetTyp return result; } + if (inputTypeId == UNKNOWNOID && IsA(node, UserVar) && IsA(((UserVar*)node)->value, Const)) { + /* + * Input is a string constant with previously undetermined type. Apply + * the target type's typinput function to it to produce a constant of + * the target type. + * + * NOTE: this case cannot be folded together with the other + * constant-input case, since the typinput function does not + * necessarily behave the same as a type conversion function. For + * example, int4's typinput function will reject "1.2", whereas + * float-to-int type conversion will round to integer. + * + * XXX if the typinput function is not immutable, we really ought to + * postpone evaluation of the function call until runtime. But there + * is no way to represent a typinput function call as an expression + * tree, because C-string values are not Datums. (XXX This *is* + * possible as of 7.3, do we want to do it?) + */ + Const* con = (Const*)((UserVar*)node)->value; + Const* newcon = makeNode(Const); + Oid baseTypeId; + int32 baseTypeMod; + int32 inputTypeMod; + Type targetType; + ParseCallbackState pcbstate; + + /* + * If the target type is a domain, we want to call its base type's + * input routine, not domain_in(). This is to avoid premature failure + * when the domain applies a typmod: existing input routines follow + * implicit-coercion semantics for length checks, which is not always + * what we want here. The needed check will be applied properly + * inside coerce_to_domain(). + */ + baseTypeMod = targetTypeMod; + baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); + + /* + * For most types we pass typmod -1 to the input routine, because + * existing input routines follow implicit-coercion semantics for + * length checks, which is not always what we want here. Any length + * constraint will be applied later by our caller. An exception + * however is the INTERVAL type, for which we *must* pass the typmod + * or it won't be able to obey the bizarre SQL-spec input rules. (Ugly + * as sin, but so is this part of the spec...) + */ + if (baseTypeId == INTERVALOID) { + inputTypeMod = baseTypeMod; + } else { + inputTypeMod = -1; + } + + targetType = typeidType(baseTypeId); + + newcon->consttype = baseTypeId; + newcon->consttypmod = inputTypeMod; + if (OidIsValid(GetCollationConnection()) && + IsSupportCharsetType(baseTypeId)) { + newcon->constcollid = GetCollationConnection(); + } else { + newcon->constcollid = typeTypeCollation(targetType); + } + newcon->constlen = typeLen(targetType); + newcon->constbyval = typeByVal(targetType); + newcon->constisnull = con->constisnull; + newcon->cursor_data.cur_dno = -1; + + /* + * We use the original literal's location regardless of the position + * of the coercion. This is a change from pre-9.2 behavior, meant to + * simplify life for pg_stat_statements. + */ + newcon->location = con->location; + + /* + * Set up to point at the constant's text if the input routine throws + * an error. + */ + setup_parser_errposition_callback(&pcbstate, pstate, con->location); + + /* + * We assume here that UNKNOWN's internal representation is the same + * as CSTRING. + */ + if (!con->constisnull) { + newcon->constvalue = stringTypeDatum_with_collation(targetType, DatumGetCString(con->constvalue), + inputTypeMod, pstate != NULL && pstate->p_has_ignore, con->constcollid); + } else { + newcon->constvalue = + stringTypeDatum(targetType, NULL, inputTypeMod, pstate != NULL && pstate->p_has_ignore); + } + + cancel_parser_errposition_callback(&pcbstate); + + result = (Node*)newcon; + + /* If target is a domain, apply constraints. */ + if (baseTypeId != targetTypeId) { + result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, cformat, location, false, false); + } + + ReleaseSysCache(targetType); + + UserVar *newus = makeNode(UserVar); + newus->name = ((UserVar*)node)->name; + newus->value = (Expr*)result; + + pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); + switch (pathtype) { + case COERCION_PATH_NONE: + return (Node *)newus; + break; + case COERCION_PATH_RELABELTYPE: { + result = coerce_to_domain((Node *)newus, InvalidOid, -1, targetTypeId, cformat, location, false, false); + if (result == (Node *)newus) { + RelabelType* r = makeRelabelType((Expr*)result, targetTypeId, -1, InvalidOid, cformat); + + r->location = location; + result = (Node*)r; + } + return result; + } break; + default: { + Oid baseTypeId; + int32 baseTypeMod; + + baseTypeMod = targetTypeMod; + baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); + + result = build_coercion_expression((Node *)newus, pathtype, funcId, baseTypeId, + baseTypeMod, cformat, location, (cformat != COERCE_IMPLICIT_CAST)); + + if (targetTypeId != baseTypeId) { + result = coerce_to_domain(result, + baseTypeId, + baseTypeMod, + targetTypeId, + cformat, + location, + true, + exprIsLengthCoercion(result, NULL)); + } + } break; + } + } if (IsA(node, Param) && pstate != NULL && pstate->p_coerce_param_hook != NULL) { /* * Allow the CoerceParamHook to decide what happens. It can return a diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index a053e37cf34f40a1cfe89e2fd876523048ad2fc6..34203833c75ea3ed05f6092020513334abaf8680 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -1095,9 +1095,14 @@ static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* is /* if not found, return a null const */ if (found) { - if (entry->isParse) { - con = (Const *)uservar->value; - entry->isParse = false; + Oid target_type = InvalidOid; + if (IsA(uservar->value, CoerceViaIO)) { + target_type = ((CoerceViaIO *)uservar->value)->resulttype; + } else { + target_type = ((Const *)uservar->value)->consttype; + } + if (target_type == UNKNOWNOID && ((Const *)uservar->value)->constisnull) { + con = entry->value; } else { Node *node = coerce_type(NULL, (Node *)entry->value, entry->value->consttype, ((Const *)uservar->value)->consttype, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); @@ -1316,21 +1321,14 @@ static Datum ExecEvalUserSetElm(ExprState* exprstate, ExprContext* econtext, boo UserVar *uservar = (UserVar*)linitial(elem->name); entry = (GucUserParamsEntry*)hash_search(u_sess->utils_cxt.set_user_params_htab, uservar->name, HASH_FIND, &found); - if (found) { - Const* expr = entry->value; - bool if_use = false; - if (expr->consttype != (usestate->xprstate).resultType && is_in_table) { - find_uservar_in_expr(usestate->instate, uservar->name, &if_use); - if (if_use) { - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("Can not change type of user defined variable when use relations."))); - } - } - } } - Oid atttypid = exprType((Node*)elem->val); + Oid atttypid = InvalidOid; + if (!found) { + atttypid = exprType((Node *)usestate->instate->expr); + } else { + atttypid = exprType((Node *)elem->val); + } value = CStringFromDatum(atttypid, result); con = processResToConst(value, atttypid, collid); diff --git a/src/test/regress/input/set_user_defined_variables_test.source b/src/test/regress/input/set_user_defined_variables_test.source index f71ea5d59adcf72cf70cd8ebda4fa996afc09969..f9f9e80752f2ec25d50dd4a42a363ec56d4b2326 100644 --- a/src/test/regress/input/set_user_defined_variables_test.source +++ b/src/test/regress/input/set_user_defined_variables_test.source @@ -728,6 +728,7 @@ SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2, @l:= @l+ 1 AS lvl FROM (SELECT @r:= 5, @l:= 0) vars, my_table h WHERE @r <>0; +-- error, dolphin cast bigint to integer Implicit SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2 FROM (SELECT @r:= 5) vars, my_table h WHERE @r<> 0; @@ -761,6 +762,18 @@ INNER JOIN recursive_query r ON e.col_2 = (r.col_2 + INTERVAL '1year') SELECT col_1, col_2, col_3 FROM recursive_query ORDER BY col_2 ASC; +-- 自增-CONCAT-报错 +SET @counter := 0; +SET @sequence := ''; +begin +label_1: +WHILE @counter < 10 DO +SET @counter := @counter + 1; +SET @sequence := CONCAT(@sequence, @counter, ', '); +END WHILE label_1; +end; +/ +SELECT TRIM(TRAILING ', ' FROM @sequence); \c regression drop database if exists test_set; diff --git a/src/test/regress/output/set_user_defined_variables_test.source b/src/test/regress/output/set_user_defined_variables_test.source index fa73f257639f7b5355e5a9bbf6a2e6955d5c893a..032412d9cb850f7e9c5af8d9080dfb0037a7d37c 100644 --- a/src/test/regress/output/set_user_defined_variables_test.source +++ b/src/test/regress/output/set_user_defined_variables_test.source @@ -1450,6 +1450,7 @@ my_table h WHERE @r <>0; 1 | | 4 (4 rows) +-- error, dolphin cast bigint to integer Implicit SELECT @r, (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id2 FROM (SELECT @r:= 5) vars, my_table h WHERE @r<> 0; @@ -1502,6 +1503,23 @@ ORDER BY col_2 ASC; dddd | 03-23-2023 | default col_3 (2 rows) +-- 自增-CONCAT-报错 +SET @counter := 0; +SET @sequence := ''; +begin +label_1: +WHILE @counter < 10 DO +SET @counter := @counter + 1; +SET @sequence := CONCAT(@sequence, @counter, ', '); +END WHILE label_1; +end; +/ +SELECT TRIM(TRAILING ', ' FROM @sequence); + rtrim +------------------------------- + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +(1 row) + \c regression drop database if exists test_set; \! @abs_bindir@/gs_guc reload -Z datanode -D @abs_srcdir@/tmp_check/datanode1 -c "enable_set_variable_b_format=off" >/dev/null 2>&1