From 6bd2cc0a2c61151d708b16a9ba87bc914679bfaa Mon Sep 17 00:00:00 2001 From: nnuanyang Date: Tue, 19 Dec 2023 17:43:41 -0800 Subject: [PATCH 1/2] @var bigint to unknown bug --- src/common/backend/parser/parse_coerce.cpp | 144 ++++++++++++++++++ src/gausskernel/runtime/executor/execQual.cpp | 43 +++--- .../set_user_defined_variables_test.source | 29 ++-- 3 files changed, 185 insertions(+), 31 deletions(-) diff --git a/src/common/backend/parser/parse_coerce.cpp b/src/common/backend/parser/parse_coerce.cpp index 53da1de5b..57b1ab191 100644 --- a/src/common/backend/parser/parse_coerce.cpp +++ b/src/common/backend/parser/parse_coerce.cpp @@ -323,6 +323,9 @@ Node *type_transfer(Node *node, Oid atttypid, bool isSelect) case INT1OID: case INT2OID: case INT4OID: + result = coerce_type(NULL, node, con->consttype, + INT4OID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); + break; case INT8OID: result = coerce_type(NULL, node, con->consttype, INT8OID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); @@ -573,6 +576,147 @@ 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); + if (pathtype != COERCION_PATH_NONE) { + if (pathtype != COERCION_PATH_RELABELTYPE) { + 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)); + } else { + 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; + } + return (Node *)newus; + } 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 a053e37cf..45fdd70fc 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -1099,14 +1099,24 @@ static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* is con = (Const *)uservar->value; entry->isParse = false; } else { - Node *node = coerce_type(NULL, (Node *)entry->value, entry->value->consttype, ((Const *)uservar->value)->consttype, - -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); - node = eval_const_expression_value(NULL, node, NULL); - if (nodeTag(node) != T_Const) { - ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), - errmsg("The value of a user_defined variable must be convertible to a constant."))); + 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); + node = eval_const_expression_value(NULL, node, NULL); + if (nodeTag(node) != T_Const) { + ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), + errmsg("The value of a user_defined variable must be convertible to a constant."))); + } + con = (Const *)node; } - con = (Const *)node; } } else { con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false); @@ -1316,21 +1326,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/output/set_user_defined_variables_test.source b/src/test/regress/output/set_user_defined_variables_test.source index fa73f2576..1cf0997ec 100644 --- a/src/test/regress/output/set_user_defined_variables_test.source +++ b/src/test/regress/output/set_user_defined_variables_test.source @@ -241,10 +241,10 @@ create table t_const as select @v1, @v2, @v3, @v4, @v5; Table "public.t_const" Column | Type | Modifiers | Storage | Stats target | Description --------+------------------+-----------+----------+--------------+------------- - @v1 | bigint | | plain | | + @v1 | integer | | plain | | @v2 | double precision | | plain | | @v3 | text | | extended | | - @v4 | bigint | | plain | | + @v4 | integer | | plain | | @v5 | text | | extended | | Has OIDs: no Options: orientation=row, compression=no @@ -405,8 +405,8 @@ create table res_select1 as select @v_select_bool1, @v_select_int1, @v_select_fl Table "public.res_select1" Column | Type | Modifiers | Storage | Stats target | Description ----------------------+------------------+-----------+----------+--------------+------------- - @v_select_bool1 | bigint | | plain | | - @v_select_int1 | bigint | | plain | | + @v_select_bool1 | integer | | plain | | + @v_select_int1 | integer | | plain | | @v_select_float1 | double precision | | plain | | @v_select_number1 | double precision | | plain | | @v_select_text1 | text | | extended | | @@ -427,8 +427,8 @@ create table res_select2 as select @v_select_bool2, @v_select_int2, @v_select_fl Table "public.res_select2" Column | Type | Modifiers | Storage | Stats target | Description ----------------------+------------------+-----------+----------+--------------+------------- - @v_select_bool2 | bigint | | plain | | - @v_select_int2 | bigint | | plain | | + @v_select_bool2 | integer | | plain | | + @v_select_int2 | integer | | plain | | @v_select_float2 | double precision | | plain | | @v_select_number2 | double precision | | plain | | @v_select_text2 | text | | extended | | @@ -1453,16 +1453,23 @@ my_table h WHERE @r <>0; 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; -ERROR: failed to find conversion function from bigint to integer + @r | parent_id2 +----+------------ + 5 | 4 + 4 | 2 + 2 | 1 + 1 | +(4 rows) + 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; @r | parent_id2 ----+------------ - 5 | 4 - 4 | 2 - 2 | 1 - 1 | + 5 | 4 + 4 | 2 + 2 | 1 + 1 | (4 rows) SELECT (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id1 FROM From c1e010760565a870e5324dcd27564cc2f6b21626 Mon Sep 17 00:00:00 2001 From: nnuanyang Date: Tue, 19 Dec 2023 18:26:26 -0800 Subject: [PATCH 2/2] =?UTF-8?q?=E8=87=AA=E5=A2=9E-CONCAT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/parser/parse_coerce.cpp | 37 ++++++------- src/gausskernel/runtime/executor/execQual.cpp | 35 ++++++------ .../set_user_defined_variables_test.source | 13 +++++ .../set_user_defined_variables_test.source | 53 +++++++++++-------- 4 files changed, 79 insertions(+), 59 deletions(-) diff --git a/src/common/backend/parser/parse_coerce.cpp b/src/common/backend/parser/parse_coerce.cpp index 57b1ab191..b2803a9cf 100644 --- a/src/common/backend/parser/parse_coerce.cpp +++ b/src/common/backend/parser/parse_coerce.cpp @@ -323,9 +323,6 @@ Node *type_transfer(Node *node, Oid atttypid, bool isSelect) case INT1OID: case INT2OID: case INT4OID: - result = coerce_type(NULL, node, con->consttype, - INT4OID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); - break; case INT8OID: result = coerce_type(NULL, node, con->consttype, INT8OID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); @@ -684,18 +681,31 @@ Node* coerce_type(ParseState* pstate, Node* node, Oid inputTypeId, Oid targetTyp newus->value = (Expr*)result; pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); - if (pathtype != COERCION_PATH_NONE) { - if (pathtype != COERCION_PATH_RELABELTYPE) { + 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)); + result = build_coercion_expression((Node *)newus, pathtype, funcId, baseTypeId, + baseTypeMod, cformat, location, (cformat != COERCE_IMPLICIT_CAST)); - if (targetTypeId != baseTypeId) + if (targetTypeId != baseTypeId) { result = coerce_to_domain(result, baseTypeId, baseTypeMod, @@ -704,18 +714,9 @@ Node* coerce_type(ParseState* pstate, Node* node, Oid inputTypeId, Oid targetTyp location, true, exprIsLengthCoercion(result, NULL)); - } else { - 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; } - return (Node *)newus; } if (IsA(node, Param) && pstate != NULL && pstate->p_coerce_param_hook != NULL) { /* diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index 45fdd70fc..34203833c 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -1095,28 +1095,23 @@ 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 { - 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); - node = eval_const_expression_value(NULL, node, NULL); - if (nodeTag(node) != T_Const) { - ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), - errmsg("The value of a user_defined variable must be convertible to a constant."))); - } - con = (Const *)node; + 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); + node = eval_const_expression_value(NULL, node, NULL); + if (nodeTag(node) != T_Const) { + ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), + errmsg("The value of a user_defined variable must be convertible to a constant."))); } + con = (Const *)node; } } else { con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false); 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 f71ea5d59..f9f9e8075 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 1cf0997ec..032412d9c 100644 --- a/src/test/regress/output/set_user_defined_variables_test.source +++ b/src/test/regress/output/set_user_defined_variables_test.source @@ -241,10 +241,10 @@ create table t_const as select @v1, @v2, @v3, @v4, @v5; Table "public.t_const" Column | Type | Modifiers | Storage | Stats target | Description --------+------------------+-----------+----------+--------------+------------- - @v1 | integer | | plain | | + @v1 | bigint | | plain | | @v2 | double precision | | plain | | @v3 | text | | extended | | - @v4 | integer | | plain | | + @v4 | bigint | | plain | | @v5 | text | | extended | | Has OIDs: no Options: orientation=row, compression=no @@ -405,8 +405,8 @@ create table res_select1 as select @v_select_bool1, @v_select_int1, @v_select_fl Table "public.res_select1" Column | Type | Modifiers | Storage | Stats target | Description ----------------------+------------------+-----------+----------+--------------+------------- - @v_select_bool1 | integer | | plain | | - @v_select_int1 | integer | | plain | | + @v_select_bool1 | bigint | | plain | | + @v_select_int1 | bigint | | plain | | @v_select_float1 | double precision | | plain | | @v_select_number1 | double precision | | plain | | @v_select_text1 | text | | extended | | @@ -427,8 +427,8 @@ create table res_select2 as select @v_select_bool2, @v_select_int2, @v_select_fl Table "public.res_select2" Column | Type | Modifiers | Storage | Stats target | Description ----------------------+------------------+-----------+----------+--------------+------------- - @v_select_bool2 | integer | | plain | | - @v_select_int2 | integer | | plain | | + @v_select_bool2 | bigint | | plain | | + @v_select_int2 | bigint | | plain | | @v_select_float2 | double precision | | plain | | @v_select_number2 | double precision | | plain | | @v_select_text2 | text | | extended | | @@ -1450,26 +1450,20 @@ 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; +ERROR: failed to find conversion function from bigint to integer 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; @r | parent_id2 ----+------------ - 5 | 4 - 4 | 2 - 2 | 1 - 1 | -(4 rows) - -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; - @r | parent_id2 -----+------------ - 5 | 4 - 4 | 2 - 2 | 1 - 1 | + 5 | 4 + 4 | 2 + 2 | 1 + 1 | (4 rows) SELECT (SELECT @r:= parent_id FROM my_table WHERE id = @r) AS parent_id1 FROM @@ -1509,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