From 06f316894d1bfddab7b8bf9d085e98d18173b4e3 Mon Sep 17 00:00:00 2001 From: yuhuanhuan <1500773557@qq.com> Date: Tue, 23 Aug 2022 16:49:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=85=BC=E5=AE=B9MySQL=20SET?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8F=98=E9=87=8F=E7=9A=84=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/src/sgml/ref/set.sgmlin | 3 + src/bin/gs_guc/cluster_guc.conf | 1 + src/common/backend/nodes/copyfuncs.cpp | 29 + src/common/backend/nodes/equalfuncs.cpp | 23 + src/common/backend/nodes/nodeFuncs.cpp | 20 + src/common/backend/nodes/nodes.cpp | 4 +- src/common/backend/nodes/outfuncs.cpp | 23 + src/common/backend/nodes/readfuncs.cpp | 24 + src/common/backend/parser/analyze.cpp | 61 + src/common/backend/parser/gram.y | 170 ++- src/common/backend/parser/parse_coerce.cpp | 66 ++ src/common/backend/parser/parse_collate.cpp | 1 + src/common/backend/parser/parse_expr.cpp | 57 + src/common/backend/parser/parse_func.cpp | 12 + src/common/backend/parser/parse_target.cpp | 12 +- src/common/backend/parser/scan.l | 20 + src/common/backend/utils/adt/ruleutils.cpp | 5 + src/common/backend/utils/misc/guc.cpp | 119 +- .../interfaces/libpq/frontend_parser/gram.y | 2 +- src/common/pl/plpgsql/src/gram.y | 2 +- src/common/pl/plpgsql/src/pl_exec.cpp | 10 + .../dbmind/kernel/index_advisor.cpp | 35 +- .../optimizer/rewrite/rewriteHandler.cpp | 125 ++ src/gausskernel/optimizer/util/clauses.cpp | 19 + src/gausskernel/optimizer/util/pgxcship.cpp | 1 + src/gausskernel/process/tcop/postgres.cpp | 10 + src/gausskernel/process/tcop/utility.cpp | 7 +- .../process/threadpool/threadpool_worker.cpp | 2 + src/gausskernel/runtime/executor/execQual.cpp | 13 +- src/include/commands/sqladvisor.h | 11 + .../knl/knl_guc/knl_session_attr_common.h | 1 + src/include/knl/knl_session.h | 2 + src/include/nodes/nodes.h | 4 +- src/include/nodes/parsenodes_common.h | 16 +- src/include/optimizer/clauses.h | 2 + src/include/parser/parse_coerce.h | 2 + src/include/rewrite/rewriteHandler.h | 2 + src/include/utils/guc.h | 9 +- .../set_user_defined_variables_test.source | 476 ++++++++ .../set_user_defined_variables_test.source | 1017 +++++++++++++++++ src/test/regress/parallel_schedule0 | 3 + 41 files changed, 2396 insertions(+), 25 deletions(-) create mode 100644 src/test/regress/input/set_user_defined_variables_test.source create mode 100644 src/test/regress/output/set_user_defined_variables_test.source diff --git a/doc/src/sgml/ref/set.sgmlin b/doc/src/sgml/ref/set.sgmlin index 3f251fa54..5964e07cf 100644 --- a/doc/src/sgml/ref/set.sgmlin +++ b/doc/src/sgml/ref/set.sgmlin @@ -18,6 +18,9 @@ SET [ SESSION | LOCAL ] {CURRENT_SCHEMA { TO | = } { schema | DEFAULT } | SCHEMA 'schema'}; SET [ SESSION | LOCAL ] XML OPTION { DOCUMENT | CONTENT }; +SET @var_name := expr [, @var_name := expr] ... +SET @var_name = expr [, @var_name = expr] ... +NOTICE: '@var_name' is only avaiable in CENTRALIZED mode and B-format database, and enable_set_variable_b_format = on. \ No newline at end of file diff --git a/src/bin/gs_guc/cluster_guc.conf b/src/bin/gs_guc/cluster_guc.conf index 41aa17a24..0dd3f1e9c 100755 --- a/src/bin/gs_guc/cluster_guc.conf +++ b/src/bin/gs_guc/cluster_guc.conf @@ -310,6 +310,7 @@ wdr_snapshot_interval|int|10,60|min|NULL| wdr_snapshot_retention_days|int|1,8|NULL|NULL| wdr_snapshot_query_timeout|int|100,2147483647|s|NULL| enable_wdr_snapshot|bool|0,0|NULL|NULL| +enable_set_variable_b_format|bool|0,0|NULL|NULL| enable_asp|bool|0,0|NULL|NULL| enable_startwith_debug|bool|0,0|NULL|NULL| enable_stmt_track|bool|0,0|NULL|NULL| diff --git a/src/common/backend/nodes/copyfuncs.cpp b/src/common/backend/nodes/copyfuncs.cpp index dcf3e2b6a..9f539c1ee 100644 --- a/src/common/backend/nodes/copyfuncs.cpp +++ b/src/common/backend/nodes/copyfuncs.cpp @@ -4079,6 +4079,28 @@ static IndexElem* _copyIndexElem(const IndexElem* from) return newnode; } +static UserSetElem* _copyUserSetElem(const UserSetElem* from) +{ + UserSetElem* newnode = makeNode(UserSetElem); + + COPY_SCALAR_FIELD(xpr.selec); + COPY_NODE_FIELD(name); + COPY_NODE_FIELD(val); + + return newnode; +} + +static UserVar* _copyUserVar(const UserVar* from) +{ + UserVar* newnode = makeNode(UserVar); + + COPY_SCALAR_FIELD(xpr.selec); + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(value); + + return newnode; +} + static ColumnDef* _copyColumnDef(const ColumnDef* from) { ColumnDef* newnode = makeNode(ColumnDef); @@ -5598,6 +5620,7 @@ static VariableSetStmt* _copyVariableSetStmt(const VariableSetStmt* from) COPY_STRING_FIELD(name); COPY_NODE_FIELD(args); COPY_SCALAR_FIELD(is_local); + COPY_NODE_FIELD(defined_args); return newnode; } @@ -8016,6 +8039,12 @@ void* copyObject(const void* from) case T_AlterSubscriptionStmt: retval = _copyAlterSubscriptionStmt((AlterSubscriptionStmt *)from); break; + case T_UserSetElem: + retval = _copyUserSetElem((UserSetElem *)from); + break; + case T_UserVar: + retval = _copyUserVar((UserVar *)from); + break; case T_PredictByFunction: retval = _copyPredictByFunctionStmt((PredictByFunction *)from); break; diff --git a/src/common/backend/nodes/equalfuncs.cpp b/src/common/backend/nodes/equalfuncs.cpp index 1dbd799c1..af5c88ab6 100644 --- a/src/common/backend/nodes/equalfuncs.cpp +++ b/src/common/backend/nodes/equalfuncs.cpp @@ -1783,6 +1783,7 @@ static bool _equalVariableSetStmt(const VariableSetStmt* a, const VariableSetStm COMPARE_STRING_FIELD(name); COMPARE_NODE_FIELD(args); COMPARE_SCALAR_FIELD(is_local); + COMPARE_NODE_FIELD(defined_args); return true; } @@ -2550,6 +2551,22 @@ static bool _equalIndexElem(const IndexElem* a, const IndexElem* b) return true; } +static bool _equalUserSetElem(const UserSetElem *a, const UserSetElem *b) +{ + COMPARE_NODE_FIELD(name); + COMPARE_NODE_FIELD(val); + + return true; +} + +static bool _equalUserVar(const UserVar *a, const UserVar *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(value); + + return true; +} + static bool _equalColumnDef(const ColumnDef* a, const ColumnDef* b) { COMPARE_STRING_FIELD(colname); @@ -4088,6 +4105,12 @@ bool equal(const void* a, const void* b) case T_AlterPublicationStmt: retval = _equalAlterPublicationStmt((AlterPublicationStmt *)a, (AlterPublicationStmt *)b); break; + case T_UserSetElem: + retval = _equalUserSetElem((UserSetElem *)a, (UserSetElem *)b); + break; + case T_UserVar: + retval = _equalUserVar((UserVar *)a, (UserVar *)b); + break; case T_CreateSubscriptionStmt: retval = _equalCreateSubscriptionStmt((CreateSubscriptionStmt *)a, (CreateSubscriptionStmt *)b); break; diff --git a/src/common/backend/nodes/nodeFuncs.cpp b/src/common/backend/nodes/nodeFuncs.cpp index 0ee58ebb9..0b67f0225 100644 --- a/src/common/backend/nodes/nodeFuncs.cpp +++ b/src/common/backend/nodes/nodeFuncs.cpp @@ -68,6 +68,9 @@ Oid exprType(const Node* expr) case T_Const: type = ((const Const*)expr)->consttype; break; + case T_UserVar: + type = ((const Const*)(((UserVar*)expr)->value))->consttype; + break; case T_Param: type = ((const Param*)expr)->paramtype; break; @@ -250,6 +253,8 @@ int32 exprTypmod(const Node* expr) return ((const Var*)expr)->vartypmod; case T_Const: return ((const Const*)expr)->consttypmod; + case T_UserVar: + return ((const Const*)(((UserVar*)expr)->value))->consttypmod; case T_Param: return ((const Param*)expr)->paramtypmod; case T_ArrayRef: @@ -706,6 +711,9 @@ Oid exprCollation(const Node* expr) case T_NamedArgExpr: coll = exprCollation((Node*)((const NamedArgExpr*)expr)->arg); break; + case T_UserVar: + coll = ((const Const*)(((UserVar*)expr)->value))->constcollid; + break; case T_OpExpr: coll = ((const OpExpr*)expr)->opcollid; break; @@ -909,6 +917,9 @@ void exprSetCollation(Node* expr, Oid collation) case T_Const: ((Const*)expr)->constcollid = collation; break; + case T_UserVar: + ((Const*)(((UserVar*)expr)->value))->constcollid = collation; + break; case T_Rownum: ((Rownum*)expr)->rownumcollid = collation; break; @@ -1550,6 +1561,7 @@ bool expression_tree_walker(Node* node, bool (*walker)(), void* context) case T_Null: case T_PgFdwRemoteInfo: case T_Rownum: + case T_UserVar: /* primitive node types with no expression subnodes */ break; case T_Aggref: { @@ -2191,6 +2203,14 @@ Node* expression_tree_mutator(Node* node, Node* (*mutator)(Node*, void*), void* MUTATE(newnode->arg, nexpr->arg, Expr*); return (Node*)newnode; } break; + case T_UserVar: { + UserVar* oldnode = (UserVar *)node; + UserVar* newnode = NULL; + + FLATCOPY(newnode, oldnode, UserVar, isCopy); + MUTATE(newnode->value, oldnode->value, Expr*); + return (Node *)newnode; + } break; case T_OpExpr: { OpExpr* expr = (OpExpr*)node; OpExpr* newnode = NULL; diff --git a/src/common/backend/nodes/nodes.cpp b/src/common/backend/nodes/nodes.cpp index 6417ad7de..0124924b2 100755 --- a/src/common/backend/nodes/nodes.cpp +++ b/src/common/backend/nodes/nodes.cpp @@ -594,7 +594,9 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, {T_ExplainModelStmt, "ExplainModelStmt"}, // End DB4AI {T_TdigestData, "TdigestData"}, - {T_CentroidPoint, "CentroidPoint"} + {T_CentroidPoint, "CentroidPoint"}, + {T_UserSetElem, "UserSetElem"}, + {T_UserVar, "UserVar"} }; char* nodeTagToString(NodeTag tag) diff --git a/src/common/backend/nodes/outfuncs.cpp b/src/common/backend/nodes/outfuncs.cpp index 14bde1160..a1c370517 100755 --- a/src/common/backend/nodes/outfuncs.cpp +++ b/src/common/backend/nodes/outfuncs.cpp @@ -3914,6 +3914,23 @@ static void _outIndexElem(StringInfo str, IndexElem* node) WRITE_ENUM_FIELD(ordering, SortByDir); WRITE_ENUM_FIELD(nulls_ordering, SortByNulls); } + +static void _outUserSetElem(StringInfo str, UserSetElem* node) +{ + WRITE_NODE_TYPE("USERSETELEM"); + + WRITE_NODE_FIELD(name); + WRITE_NODE_FIELD(val); +} + +static void _outUserVar(StringInfo str, UserVar* node) +{ + WRITE_NODE_TYPE("USERVAR"); + + WRITE_STRING_FIELD(name); + WRITE_NODE_FIELD(value); +} + static void _outDefElem(StringInfo str, DefElem* node) { WRITE_NODE_TYPE("DEFELEM"); @@ -6507,6 +6524,12 @@ static void _outNode(StringInfo str, const void* obj) case T_TrainModel: _outTrainModel(str, (TrainModel*)obj); break; + case T_UserSetElem: + _outUserSetElem(str, (UserSetElem *) obj); + break; + case T_UserVar: + _outUserVar(str, (UserVar *) obj); + break; case T_PLDebug_variable: _outPLDebug_variable(str, (PLDebug_variable*) obj); break; diff --git a/src/common/backend/nodes/readfuncs.cpp b/src/common/backend/nodes/readfuncs.cpp index 9469c0fd6..46b4fe9fd 100755 --- a/src/common/backend/nodes/readfuncs.cpp +++ b/src/common/backend/nodes/readfuncs.cpp @@ -5749,6 +5749,26 @@ static TdigestData* _readTdigestData() READ_DONE(); } +static UserSetElem* _readUserSetElem() +{ + READ_LOCALS(UserSetElem); + + READ_NODE_FIELD(name); + READ_NODE_FIELD(val); + + READ_DONE(); +} + +static UserVar* _readUserVar() +{ + READ_LOCALS(UserVar); + + READ_STRING_FIELD(name); + READ_NODE_FIELD(value); + + READ_DONE(); +} + /* * parseNodeString * @@ -6208,6 +6228,10 @@ Node* parseNodeString(void) return_value = _readPLDebug_frame(); } else if (MATCH("TdigestData", 11)) { return_value = _readTdigestData(); + } else if (MATCH("USERSETELEM", 11)) { + return_value = _readUserSetElem(); + } else if (MATCH("USERVAR", 7)) { + return_value = _readUserVar(); } else { ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 77e9df43b..4a78afe4d 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -102,6 +102,7 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt); static void checkUpsertTargetlist(Relation targetTable, List* updateTlist); static UpsertExpr* transformUpsertClause(ParseState* pstate, UpsertClause* upsertClause, RangeVar* relation); static int count_rowexpr_columns(ParseState* pstate, Node* expr); +static Query* transformVariableSetStmt(ParseState* pstate, VariableSetStmt* stmt); static Query* transformSelectStmt( ParseState* pstate, SelectStmt* stmt, bool isFirstNode = true, bool isCreateView = false); static Query* transformValuesClause(ParseState* pstate, SelectStmt* stmt); @@ -434,6 +435,27 @@ Query* transformStmt(ParseState* pstate, Node* parseTree, bool isFirstNode, bool result = transformCreateModelStmt(pstate, (CreateModelStmt*) parseTree); break; + case T_VariableSetStmt: { + VariableSetStmt* n = (VariableSetStmt*)parseTree; + if (n->kind == VAR_SET_DEFINED) { + result = transformVariableSetStmt(pstate, n); + } else { + result = makeNode(Query); + result->commandType = CMD_UTILITY; + result->utilityStmt = (Node*)parseTree; + } + } break; + + case T_PrepareStmt: { + PrepareStmt* n = (PrepareStmt *)parseTree; + if (IsA(n->query, UserVar)) { + Node *uvar = transformExpr(pstate, n->query); + n->query = (Node *)copyObject((UserVar *)uvar); + } + result = makeNode(Query); + result->commandType = CMD_UTILITY; + result->utilityStmt = (Node*)parseTree; + } break; default: @@ -515,6 +537,12 @@ bool analyze_requires_snapshot(Node* parseTree) } break; + case T_VariableSetStmt: + /* user-defined variables support sublink */ + if (((VariableSetStmt *)parseTree)->defined_args != NULL) { + result = true; + } + break; default: /* other utility statements don't have any real parse analysis */ result = false; @@ -2237,6 +2265,39 @@ static bool shouldTransformStartWithStmt(ParseState* pstate, SelectStmt* stmt, Q return true; } +/* + * transformVariableSetStmt - transforms a user-defined variable + */ +static Query* transformVariableSetStmt(ParseState* pstate, VariableSetStmt* stmt) +{ + Query *query = makeNode(Query); + List *resultList = NIL; + ListCell *temp = NULL; + + foreach (temp, stmt->defined_args) { + UserSetElem *userElem = (UserSetElem *)lfirst(temp); + UserSetElem *newUserElem = makeNode(UserSetElem); + newUserElem->name = userElem->name; + + Node *node = transformExpr(pstate, (Node *)userElem->val); + + if (IsA(node, UserSetElem)) { + newUserElem->name = list_concat(newUserElem->name, ((UserSetElem *)node)->name); + newUserElem->val = ((UserSetElem *)node)->val; + } else { + newUserElem->val = (Expr *)node; + } + + resultList = lappend(resultList, newUserElem); + } + stmt->defined_args = resultList; + + query->commandType = CMD_UTILITY; + query->utilityStmt = (Node*)stmt; + + return query; +} + /* * transformSelectStmt - * transforms a Select Statement diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 7be7ca9ff..c86c406d7 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -67,6 +67,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "nodes/print.h" #include "optimizer/planner.h" #include "parser/gramparse.h" #include "parser/parse_type.h" @@ -661,6 +662,9 @@ static int errstate; %type with_clause opt_with_clause %type cte_list +%type user_defined_list +%type uservar_name user_defined_single + %type within_group_clause pkg_body_subprogram %type window_clause window_definition_list opt_partition_clause %type window_definition over_clause window_specification @@ -782,7 +786,7 @@ static int errstate; * DOT_DOT is unused in the core SQL grammar, and so will always provoke * parse errors. It is needed by PL/pgsql. */ -%token IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp COMMENTSTRING +%token IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp COMMENTSTRING SET_USER_IDENT %token ICONST PARAM %token TYPECAST ORA_JOINOP DOT_DOT COLON_EQUALS PARA_EQUALS @@ -925,7 +929,7 @@ static int errstate; %left OR %left AND %right NOT -%right '=' +%right '=' COLON_EQUALS %nonassoc '<' '>' CmpOp %nonassoc LIKE ILIKE SIMILAR %nonassoc ESCAPE @@ -2002,7 +2006,34 @@ VariableSetStmt: n->is_local = false; $$ = (Node *) n; } - ; + | SET user_defined_list + { +#ifdef ENABLE_MULTIPLE_NODES + const char* message = "SET @var_name := expr is not yet supported in distributed database."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SET @var_name := expr is not yet supported in distributed database."))); +#endif + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT && u_sess->attr.attr_common.enable_set_variable_b_format) { + VariableSetStmt *n = makeNode(VariableSetStmt); + n->kind = VAR_SET_DEFINED; + n->name = "USER DEFINED VARIABLE"; + n->defined_args = $2; + n->is_local = false; + $$ = (Node *)n; + } else { + const char* message = "SET @var_name := expr is supported only in B-format database, and enable_set_variable_b_format = on."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errmodule(MOD_PARSER), + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("SET @var_name := expr is supported only in B-format database, and enable_set_variable_b_format = on."), + parser_errposition(@1))); + $$ = NULL;/* not reached */ + } + } + ; set_rest: TRANSACTION transaction_mode_list @@ -2022,6 +2053,9 @@ set_rest: $$ = n; } | set_rest_more + { + $$ = $1; + } ; generic_set: @@ -2217,6 +2251,61 @@ set_rest_more: /* Generic SET syntaxes: */ $$ = n; } ; + +user_defined_list: + user_defined_single { $$ = list_make1($1); } + | user_defined_list ',' user_defined_single { $$ = lappend($1, $3); } + ; + +user_defined_single: + uservar_name COLON_EQUALS a_expr + { + UserSetElem *n = makeNode(UserSetElem); + n->name = list_make1($1); + n->val = (Expr *)$3; + $$ = (Node *)n; + } + | uservar_name '=' a_expr + { + UserSetElem *n = makeNode(UserSetElem); + n->name = list_make1($1); + n->val = (Expr *)$3; + $$ = (Node *)n; + } + ; + + +uservar_name: + SET_USER_IDENT + { + int len = strlen($1); + error_t errorno = EOK; + + if (len < 1) { + ereport(errstate, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("Incorrect user_defined variable."), + parser_errposition(@1))); + } + + char *name = (char *)palloc(len + 1); + errorno = memset_s(name, len + 1, 0, len + 1); + securec_check(errorno, "\0", "\0"); + if ((len > 2) && (strncmp($1, "'", 1) == 0 || strncmp($1, "\"", 1) == 0 || strncmp($1, "`", 1) == 0)) { + errorno = strncpy_s(name, len + 1, $1 + 1, len + 1); + securec_check(errorno, "\0", "\0"); + name[len - 2] = '\0'; + } else { + errorno = strncpy_s(name, len + 1, $1, len + 1); + securec_check(errorno, "\0", "\0"); + name[len] = '\0'; + } + + UserVar *n = makeNode(UserVar); + n->name = downcase_truncate_identifier(name, strlen(name), true); + $$ = (Node *)n; + } + ; var_name: ColId { $$ = $1; } | var_name '.' ColId @@ -18484,7 +18573,29 @@ PreparableStmt: | UpdateStmt | DeleteStmt /* by default all are $$=$1 */ | MergeStmt - ; + | uservar_name + { +#ifdef ENABLE_MULTIPLE_NODES + const char* message = "@var_name is not yet supported in distributed database."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("@var_name is not yet supported in distributed database."))); +#endif + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT && u_sess->attr.attr_common.enable_set_variable_b_format) { + $$ = $1; + } else { + const char* message = "@var_name is supported only in B-format database, and enable_set_variable_b_format = on."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errmodule(MOD_PARSER), + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("@var_name is supported only in B-format database, and enable_set_variable_b_format = on."), + parser_errposition(@1))); + $$ = NULL;/* not reached */ + } + } + ; /***************************************************************************** * @@ -21346,7 +21457,32 @@ a_expr: c_expr { $$ = $1; } { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); } | a_expr '@' a_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "@", $1, $3, @2); } - | a_expr CmpOp a_expr + | uservar_name COLON_EQUALS a_expr + { +#ifdef ENABLE_MULTIPLE_NODES + const char* message = "@var_name := expr is not yet supported in distributed database."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("@var_name := expr is not yet supported in distributed database."))); +#endif + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT && u_sess->attr.attr_common.enable_set_variable_b_format) { + UserSetElem *n = makeNode(UserSetElem); + n->name = list_make1((Node *)$1); + n->val = (Expr *)$3; + $$ = (Node *) n; + } else { + const char* message = "@var_name := expr is supported only in B-format database, and enable_set_variable_b_format = on."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errmodule(MOD_PARSER), + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("@var_name := expr is supported only in the SET syntax of B-format database, and enable_set_variable_b_format = on."), + parser_errposition(@1))); + $$ = NULL;/* not reached */ + } + } + | a_expr CmpOp a_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, $2, $1, $3, @2); } | a_expr qual_Op a_expr %prec Op { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); } @@ -22001,7 +22137,29 @@ c_expr: columnref %prec UMINUS { $$ = $1; } g->location = @1; $$ = (Node *)g; } - ; + | uservar_name %prec UMINUS + { +#ifdef ENABLE_MULTIPLE_NODES + const char* message = "@var_name is not yet supported in distributed database."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("@var_name is not yet supported in distributed database."))); +#endif + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT && u_sess->attr.attr_common.enable_set_variable_b_format) { + $$ = $1; + } else { + const char* message = "@var_name is supported only in B-format database, and enable_set_variable_b_format = on."; + InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); + ereport(errstate, + (errmodule(MOD_PARSER), + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("@var_name is supported only in B-format database, and enable_set_variable_b_format = on."), + parser_errposition(@1))); + $$ = NULL;/* not reached */ + } + } + ; /* Used for List Distribution to avoid reduce/reduce conflict. This is unavoidable, since Bison is LALR(1) compiler */ c_expr_noparen: columnref { $$ = $1; } diff --git a/src/common/backend/parser/parse_coerce.cpp b/src/common/backend/parser/parse_coerce.cpp index 03bfc777f..d05e46689 100644 --- a/src/common/backend/parser/parse_coerce.cpp +++ b/src/common/backend/parser/parse_coerce.cpp @@ -31,6 +31,7 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/typcache.h" +#include "optimizer/clauses.h" #ifdef PGXC #include "pgxc/pgxc.h" #endif @@ -168,6 +169,71 @@ Node* coerce_to_target_type(ParseState* pstate, Node* expr, Oid exprtype, Oid ta return result; } +/* + * user_defined variables only store integer, float, bit, string and null, + * therefor, we convert the constant to the corresponding type. + * atttypid: datatype + * isSelect: subquery flag + */ +Node *type_transfer(Node *node, Oid atttypid, bool isSelect) +{ + Node *result = NULL; + Const *con = (Const *)node; + if (con->constisnull) { + return node; + } + + switch (atttypid) { + case BOOLOID: + case INT1OID: + case INT2OID: + case INT4OID: + case INT8OID: + result = coerce_type(NULL, node, con->consttype, + INT8OID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); + break; + case FLOAT4OID: + case FLOAT8OID: + case NUMERICOID: + result = coerce_type(NULL, node, con->consttype, + FLOAT8OID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); + break; + case BITOID: + result = coerce_type(NULL, node, con->consttype, + BITOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); + break; + case VARBITOID: + result = coerce_type(NULL, node, con->consttype, + VARBITOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); + break; + default: + result = isSelect ? node : + coerce_type(NULL, node, con->consttype, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); + break; + } + + return result; +} + +/* + * convert expression to const. + */ +Node *const_expression_to_const(Node *node) +{ + Node *result = NULL; + Const *con = (Const *)node; + + 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."))); + } + + /* user_defined varibale only stores integer, float, bit, string, null. */ + result = type_transfer(node, con->consttype, false); + return eval_const_expression_value(NULL, result, NULL); +} + /* * coerce_type() * Convert an expression to a different type. diff --git a/src/common/backend/parser/parse_collate.cpp b/src/common/backend/parser/parse_collate.cpp index 87584a22f..5e9c98196 100644 --- a/src/common/backend/parser/parse_collate.cpp +++ b/src/common/backend/parser/parse_collate.cpp @@ -508,6 +508,7 @@ static bool assign_collations_walker(Node* node, assign_collations_context* cont case T_CaseTestExpr: case T_SetToDefault: case T_CurrentOfExpr: + case T_UserVar: /* * General case for childless expression nodes. These should diff --git a/src/common/backend/parser/parse_expr.cpp b/src/common/backend/parser/parse_expr.cpp index 8bd4b20db..cab889cab 100644 --- a/src/common/backend/parser/parse_expr.cpp +++ b/src/common/backend/parser/parse_expr.cpp @@ -62,6 +62,8 @@ static Node* transformAExprDistinct(ParseState* pstate, A_Expr* a); static Node* transformAExprNullIf(ParseState* pstate, A_Expr* a); static Node* transformAExprOf(ParseState* pstate, A_Expr* a); static Node* transformAExprIn(ParseState* pstate, A_Expr* a); +static Node* transformUserSetElem(ParseState* pstate, UserSetElem *elem); +static Node* transformUserVar(UserVar *uservar); static Node* transformFuncCall(ParseState* pstate, FuncCall* fn); static Node* transformCaseExpr(ParseState* pstate, CaseExpr* c); static Node* transformSubLink(ParseState* pstate, SubLink* sublink); @@ -235,6 +237,16 @@ Node* transformExpr(ParseState* pstate, Node* expr) break; } + case T_UserSetElem: { + result = transformUserSetElem(pstate, (UserSetElem *)expr); + break; + } + + case T_UserVar: { + result = transformUserVar((UserVar *)expr); + break; + } + case T_FuncCall: result = transformFuncCall(pstate, (FuncCall*)expr); break; @@ -1431,6 +1443,51 @@ static bool NeedExtractOutParam(FuncCall* fn, Node* result) #endif } +/* + * rewrite userSetElem. + * extract name from old userSetElem into new userSetElem, + * such as, @var_name1 := @var_name2 := @var_name3 := ... := expr. + */ +static Node* transformUserSetElem(ParseState* pstate, UserSetElem *elem) +{ + UserSetElem *result = makeNode(UserSetElem); + result->name = elem->name; + Node *value = transformExpr(pstate, (Node*)elem->val); + + if (IsA(elem->val, UserSetElem)) { + result->name = list_concat(result->name, ((UserSetElem *)value)->name); + result->val = ((UserSetElem *)value)->val; + } else { + result->val = (Expr *)value; + } + + return (Node *)result; +} + +/* + * Get user_defined varibales from hash table, + * if not found, NULL is returned. + */ +static Node* transformUserVar(UserVar *uservar) +{ + bool found = false; + + GucUserParamsEntry *entry = (GucUserParamsEntry *)hash_search(u_sess->utils_cxt.set_user_params_htab, + uservar->name, HASH_FIND, &found); + + if (!found) { + /* return a null const */ + Const *nullValue = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false); + return (Node *)nullValue; + } + + UserVar *result = makeNode(UserVar); + result->name = uservar->name; + result->value = (Expr *)copyObject(entry->value); + + return (Node *)result; +} + static Node* transformFuncCall(ParseState* pstate, FuncCall* fn) { List* targs = NIL; diff --git a/src/common/backend/parser/parse_func.cpp b/src/common/backend/parser/parse_func.cpp index 1e7e48f85..63f966b07 100644 --- a/src/common/backend/parser/parse_func.cpp +++ b/src/common/backend/parser/parse_func.cpp @@ -1828,6 +1828,18 @@ void make_fn_arguments(ParseState* pstate, List* fargs, Oid* actual_arg_types, O COERCE_IMPLICIT_CAST, -1); na->arg = (Expr*)node; + } else if (IsA(node, UserVar)) { + UserVar* uvar = (UserVar*)node; + + node = coerce_type(pstate, + (Node*)uvar->value, + actual_arg_types[i], + declared_arg_types[i], + -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); + uvar->value = (Expr*)node; } else { node = coerce_type(pstate, node, diff --git a/src/common/backend/parser/parse_target.cpp b/src/common/backend/parser/parse_target.cpp index 8a398a464..9bcfc0ba4 100644 --- a/src/common/backend/parser/parse_target.cpp +++ b/src/common/backend/parser/parse_target.cpp @@ -28,6 +28,7 @@ #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "parser/parse_type.h" +#include "nodes/parsenodes_common.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/rel.h" @@ -1249,7 +1250,7 @@ static List* ExpandRowReference(ParseState* pstate, Node* expr, bool targetlist) } if (unlikely(tupleDesc == NULL)) { - ereport(ERROR, + ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("tupleDesc should not be null"))); } @@ -1670,6 +1671,15 @@ static int FigureColnameInternal(Node* node, char** name) case T_XmlSerialize: *name = "xmlserialize"; return 2; + /* get name of user_defined variables. */ + case T_UserVar: { + size_t len = strlen(((UserVar *)node)->name) + strlen("@") + 1; + char *colname = (char *)palloc0(len); + errno_t rc = snprintf_s(colname, len, len - 1, "@%s", ((UserVar *)node)->name); + securec_check_ss(rc, "\0", "\0"); + *name = colname; + return 1; + } break; default: break; } diff --git a/src/common/backend/parser/scan.l b/src/common/backend/parser/scan.l index 59764a6ff..8108d2b2a 100755 --- a/src/common/backend/parser/scan.l +++ b/src/common/backend/parser/scan.l @@ -363,6 +363,12 @@ newParam :({identifier}|{integer}) newArray :({identifier}|{integer}){space}*\] +set_user_cont [A-Za-z\377_0-9\$\.] +set_user_escape_quote [^'] +set_user_escape_dquote [^"] +set_user_escape_bquote [^`] +setUserIdentifier @(({set_user_cont}+)|(\'{set_user_escape_quote}+\')|(\"{set_user_escape_dquote}+\")|(\`{set_user_escape_bquote}+\`)) + other . /* @@ -1102,6 +1108,20 @@ other . return IDENT; } +{setUserIdentifier} { + if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT && u_sess->attr.attr_common.enable_set_variable_b_format) { + SET_YYLLOC(); + yylval->str = pstrdup(yytext + 1); + yyextra->is_hint_str = false; + return SET_USER_IDENT; + } else { + yyless(1); + yylval->str = pstrdup(yytext); + yyextra->is_hint_str = false; + return yytext[0]; + } + } + {other} { SET_YYLLOC(); yyextra->is_hint_str = false; diff --git a/src/common/backend/utils/adt/ruleutils.cpp b/src/common/backend/utils/adt/ruleutils.cpp index 2fea73042..c31b61166 100644 --- a/src/common/backend/utils/adt/ruleutils.cpp +++ b/src/common/backend/utils/adt/ruleutils.cpp @@ -9104,6 +9104,11 @@ static void get_rule_expr(Node* node, deparse_context* context, bool showimplici get_const_expr((Const*)node, context, 0); break; + case T_UserVar: + appendStringInfo(buf, "@"); + appendStringInfo(buf, ((UserVar*)node)->name); + break; + case T_Param: get_parameter((Param*)node, context); break; diff --git a/src/common/backend/utils/misc/guc.cpp b/src/common/backend/utils/misc/guc.cpp index b7936eecd..03ece4a45 100755 --- a/src/common/backend/utils/misc/guc.cpp +++ b/src/common/backend/utils/misc/guc.cpp @@ -71,10 +71,12 @@ #include "optimizer/planmain.h" #include "optimizer/prep.h" #include "optimizer/gtmfree.h" +#include "optimizer/clauses.h" #include "parser/parse_expr.h" #include "parser/parse_oper.h" #include "parser/parse_type.h" #include "parser/parser.h" +#include "parser/parse_coerce.h" #include "parser/scansup.h" #include "pgstat.h" #include "pgxc/route.h" @@ -121,6 +123,7 @@ #include "replication/syncrep.h" #include "replication/walreceiver.h" #include "replication/walsender.h" +#include "rewrite/rewriteHandler.h" #include "storage/buf/bufmgr.h" #include "storage/cucache_mgr.h" #include "storage/smgr/fd.h" @@ -164,6 +167,7 @@ #include "utils/guc_memory.h" #include "utils/guc_network.h" #include "utils/guc_resource.h" +#include "nodes/parsenodes_common.h" #ifndef PG_KRB_SRVTAB #define PG_KRB_SRVTAB "" @@ -264,6 +268,7 @@ const char* sync_guc_variable_namelist[] = {"work_mem", "enable_instr_rt_percentile", "instr_rt_percentile_interval", "enable_wdr_snapshot", + "enable_set_variable_b_format", "wdr_snapshot_interval", "wdr_snapshot_retention_days", "wdr_snapshot_query_timeout", @@ -1053,6 +1058,17 @@ static void InitConfigureNamesBool() NULL, NULL}, #endif + {{"enable_set_variable_b_format", + PGC_SIGHUP, + NODE_ALL, + INSTRUMENTS_OPTIONS, + gettext_noop("enable set variable in b format."), + NULL}, + &u_sess->attr.attr_common.enable_set_variable_b_format, + false, + NULL, + NULL, + NULL}, {{"enable_wdr_snapshot", PGC_SIGHUP, NODE_ALL, @@ -8523,7 +8539,7 @@ void AlterSystemSetConfigFile(AlterSystemStmt * altersysstmt) /* * SET command */ -void ExecSetVariableStmt(VariableSetStmt* stmt) +void ExecSetVariableStmt(VariableSetStmt* stmt, ParamListInfo paramInfo) { GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET; @@ -8770,6 +8786,32 @@ void ExecSetVariableStmt(VariableSetStmt* stmt) case VAR_RESET_ALL: ResetAllOptions(); break; + case VAR_SET_DEFINED: + if (strcmp(stmt->name, "USER DEFINED VARIABLE") == 0) { + ListCell *head = NULL; + + foreach (head, stmt->defined_args) { + UserSetElem *elem = (UserSetElem *)lfirst(head); + Node *node = NULL; + + /* evaluates the value of expression, the boundParam only supported in PL/SQL. */ + node = eval_const_expression_value(NULL, (Node *)elem->val, paramInfo); + + if (nodeTag(node) == T_Const) { + elem->val = (Expr *)const_expression_to_const(node); + } else { + elem->val = (Expr *)const_expression_to_const(QueryRewriteNonConstant(node)); + } + + int ret = check_set_user_message(elem); + if (ret == -1) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), errmsg("invalid name or hash table is nill."))); + } + } + } + break; + default: break; } @@ -12514,6 +12556,81 @@ int check_set_message_to_send(const VariableSetStmt* stmt, const char* queryStri return 0; } +/* + * @Description: init user parameters hash table + * @IN void + * @Return: void + * @See also: + */ +void init_set_user_params_htab(void) +{ + HASHCTL hash_ctl; + + errno_t errval = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl)); + securec_check_errval(errval, , LOG); + + hash_ctl.keysize = NAMEDATALEN; + hash_ctl.hcxt = SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB); + hash_ctl.entrysize = sizeof(GucUserParamsEntry); + hash_ctl.hash = string_hash; + + u_sess->utils_cxt.set_user_params_htab = hash_create( + "set user params hash table", WORKLOAD_STAT_HASH_SIZE, &hash_ctl, HASH_CONTEXT | HASH_ELEM | HASH_FUNCTION); +} + +/* + * @Description: find user_deined variable in hash table + * @IN elem: UserSetElem + * @Return: 0: append success + * -1: append fail + * @See also: + */ +int check_set_user_message(const UserSetElem *elem) +{ + ListCell *head = NULL; + char *name = NULL; + bool found = false; + + if (nodeTag(elem->val) != T_Const) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), errmsg("The value of user_defined variable must be a const"))); + } + + /* no hash table, we can only choose appending mode */ + if (u_sess->utils_cxt.set_user_params_htab == NULL) { + return -1; + } + + foreach (head, elem->name) { + UserVar *n = (UserVar *)lfirst(head); + name = n->name; + + if (!StringIsValid(name)) { + return -1; + } + + GucUserParamsEntry *entry = (GucUserParamsEntry *)hash_search(u_sess->utils_cxt.set_user_params_htab, + name, HASH_ENTER, &found); + if (entry == NULL) { + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to create user_defined entry due to out of memory"))); + } + + USE_MEMORY_CONTEXT(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB)); + + if (found) { + entry->value = (Const *)copyObject((Const *)(elem->val)); + } else { + errno_t errval = strncpy_s(entry->name, sizeof(entry->name), name, sizeof(entry->name) - 1); + securec_check_errval(errval, , LOG); + + entry->value = (Const *)copyObject((Const *)(elem->val)); + } + } + + return 0; +} + /*get set commant in input_set_message*/ char* get_set_string() { diff --git a/src/common/interfaces/libpq/frontend_parser/gram.y b/src/common/interfaces/libpq/frontend_parser/gram.y index ff7485f7c..d48c94b1a 100755 --- a/src/common/interfaces/libpq/frontend_parser/gram.y +++ b/src/common/interfaces/libpq/frontend_parser/gram.y @@ -479,7 +479,7 @@ extern THR_LOCAL bool stmt_contains_operator_plus; * DOT_DOT is unused in the core SQL grammar, and so will always provoke * parse errors. It is needed by PL/pgsql. */ -%token IDENT FCONST SCONST BCONST XCONST Op CmpOp COMMENTSTRING +%token IDENT FCONST SCONST BCONST XCONST Op CmpOp COMMENTSTRING SET_USER_IDENT %token ICONST PARAM %token TYPECAST ORA_JOINOP DOT_DOT COLON_EQUALS PARA_EQUALS diff --git a/src/common/pl/plpgsql/src/gram.y b/src/common/pl/plpgsql/src/gram.y index d7fbd1755..35437a5d9 100755 --- a/src/common/pl/plpgsql/src/gram.y +++ b/src/common/pl/plpgsql/src/gram.y @@ -387,7 +387,7 @@ static bool need_build_row_for_func_arg(PLpgSQL_rec **rec, PLpgSQL_row **row, in * Some of these are not directly referenced in this file, but they must be * here anyway. */ -%token IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp COMMENTSTRING +%token IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp COMMENTSTRING SET_USER_IDENT %token ICONST PARAM %token TYPECAST ORA_JOINOP DOT_DOT COLON_EQUALS PARA_EQUALS diff --git a/src/common/pl/plpgsql/src/pl_exec.cpp b/src/common/pl/plpgsql/src/pl_exec.cpp index 96563a861..60ed5e8f6 100644 --- a/src/common/pl/plpgsql/src/pl_exec.cpp +++ b/src/common/pl/plpgsql/src/pl_exec.cpp @@ -12245,6 +12245,16 @@ static bool needRecompilePlan(SPIPlanPtr plan) return true; } + /* + * if stmt is user_defined variables, recompile the query. + */ + if (plansource->raw_parse_tree != NULL && IsA(plansource->raw_parse_tree, VariableSetStmt)) { + VariableSetStmt *stmt = (VariableSetStmt *)plansource->raw_parse_tree; + if (stmt->kind == VAR_SET_DEFINED) { + return true; + } + } + ret_val = checkRecompileCondition(plansource); /* Once we find one need re-compile stmt in SP, all stored procedure should be re-compiled. */ diff --git a/src/gausskernel/dbmind/kernel/index_advisor.cpp b/src/gausskernel/dbmind/kernel/index_advisor.cpp index 125ec911e..e7fc832e8 100644 --- a/src/gausskernel/dbmind/kernel/index_advisor.cpp +++ b/src/gausskernel/dbmind/kernel/index_advisor.cpp @@ -42,7 +42,6 @@ #include "parser/parsetree.h" #include "parser/analyze.h" #include "securec.h" -#include "tcop/dest.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" #include "tcop/pquery.h" @@ -51,7 +50,6 @@ #include "utils/relcache.h" #include "utils/syscache.h" #include "utils/lsyscache.h" -#include "utils/palloc.h" #include "utils/fmgroids.h" #include "utils/snapmgr.h" @@ -69,12 +67,6 @@ typedef struct { char index_type[NAMEDATALEN]; } SuggestedIndex; -typedef struct { - DestReceiver pub; - MemoryContext mxct; - List *tuples; -} StmtResult; - typedef struct { char *schema_name; char *table_name; @@ -120,7 +112,6 @@ THR_LOCAL List *g_drived_tables = NIL; THR_LOCAL TableCell *g_driver_table = NULL; static SuggestedIndex *suggest_index(const char *, _out_ int *); -static StmtResult *execute_stmt(const char *query_string, bool need_result = false); static List *get_table_indexes(Oid oid); static List *get_index_attname(Oid oid); static char *search_table_attname(Oid attrelid, int2 attnum); @@ -627,6 +618,7 @@ StmtResult *execute_stmt(const char *query_string, bool need_result) int16 format = 0; List *parsetree_list = NULL; ListCell *parsetree_item = NULL; + bool snapshot_set = false; parsetree_list = pg_parse_query(query_string, NULL); Assert(list_length(parsetree_list) == 1); // ought to be one query @@ -639,9 +631,18 @@ StmtResult *execute_stmt(const char *query_string, bool need_result) Node *parsetree = (Node *)lfirst(parsetree_item); const char *commandTag = CreateCommandTag(parsetree); + if (u_sess->utils_cxt.ActiveSnapshot == NULL && analyze_requires_snapshot(parsetree)) { + PushActiveSnapshot(GetTransactionSnapshot()); + snapshot_set = true; + } + querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0); plantree_list = pg_plan_queries(querytree_list, 0, NULL); + if (snapshot_set) { + PopActiveSnapshot(); + } + portal = CreatePortal(query_string, true, true); portal->visible = false; PortalDefineQuery(portal, NULL, query_string, commandTag, plantree_list, NULL); @@ -675,6 +676,8 @@ DestReceiver *create_stmt_receiver() self->pub.mydest = DestTuplestore; self->tuples = NIL; + self->isnulls = NULL; + self->atttypids = NULL; self->mxct = CurrentMemoryContext; return (DestReceiver *)self; @@ -692,13 +695,23 @@ void receive(TupleTableSlot *slot, DestReceiver *self) bool typisvarlena = false; List *values = NIL; + if (result->isnulls == NULL) { + result->isnulls = (bool *)pg_malloc(natts * sizeof(bool)); + } + if (result->atttypids == NULL) { + result->atttypids = (Oid *)pg_malloc(natts * sizeof(Oid)); + } + MemoryContext oldcontext = MemoryContextSwitchTo(result->mxct); for (int i = 0; i < natts; ++i) { origattr = tableam_tslot_getattr(slot, i + 1, &isnull); if (isnull) { + result->isnulls[i] = true; continue; } + + result->isnulls[i] = false; getTypeOutputInfo(typeinfo->attrs[i]->atttypid, &typoutput, &typisvarlena); if (typisvarlena) { @@ -709,6 +722,7 @@ void receive(TupleTableSlot *slot, DestReceiver *self) value = OidOutputFunctionCall(typoutput, attr); values = lappend(values, value); + result->atttypids[i] = typeinfo->attrs[i]->atttypid; /* Clean up detoasted copy, if any */ if (DatumGetPointer(attr) != DatumGetPointer(origattr)) { @@ -739,6 +753,9 @@ void destroy(DestReceiver *self) list_free_deep((List *)lfirst(item)); } list_free(tuples); + + pfree_ext(result->isnulls); + pfree_ext(result->atttypids); pfree_ext(self); } diff --git a/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp b/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp index 3a8c3b831..058a857db 100644 --- a/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp +++ b/src/gausskernel/optimizer/rewrite/rewriteHandler.cpp @@ -42,6 +42,8 @@ #include "catalog/pg_constraint.h" #include "catalog/namespace.h" #include "client_logic/client_logic.h" +#include "catalog/pg_proc.h" +#include "commands/sqladvisor.h" #ifdef PGXC #include "pgxc/locator.h" @@ -3274,4 +3276,127 @@ List* QueryRewriteCTAS(Query* parsetree) } } } + +/* + * User_defined variables is a string in prepareStmt. + * Get selectStmt/insertStmt/updateStmt/deleteStmt/mergeStmt from user_defined variables by pg_parse_query. + * Then, execute SQL: PREPARE stmt AS selectStmt/insertStmt/updateStmt/deleteStmt/mergeStmt. + */ +List* QueryRewritePrepareStmt(Query* parsetree) +{ + char *sqlstr = NULL; + List* raw_parsetree_list = NIL; + List* querytree_list = NULL; + + PrepareStmt *stmt = (PrepareStmt *)parsetree->utilityStmt; + UserVar *uservar = (UserVar *)stmt->query; + Const* value = (Const *)uservar->value; + + if (value->consttype != TEXTOID) { + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + errmsg("userdefined variable in prepate statement must be text type."))); + } + + sqlstr = TextDatumGetCString(value->constvalue); + + raw_parsetree_list = pg_parse_query(sqlstr); + + if (raw_parsetree_list->length != 1) { + ereport(ERROR, + (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), + errmsg("prepare user_defined variable can contain only one SQL statement."))); + } + + switch (nodeTag(linitial(raw_parsetree_list))) { + case T_SelectStmt: + case T_InsertStmt: + case T_UpdateStmt: + case T_DeleteStmt: + case T_MergeStmt: + stmt->query = (Node *)copyObject((Node *)linitial(raw_parsetree_list)); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("the statement in prepate is not supported."))); + break; + } + + querytree_list = pg_analyze_and_rewrite((Node*)stmt, sqlstr, NULL, 0); + return querytree_list; +} + +/* + * Get value from a subquery or non-constant expression by constructing SQL. + * input: + node: a subquery expression or non-constant expression. + * return: Const expression. + */ +Node* QueryRewriteNonConstant(Node *node) +{ + Query* cparsetree = NULL; + Const* con = NULL; + List *p_target = NIL; + Node *res = NULL; + SelectStmt* select_stmt = makeNode(SelectStmt); + + /* get targetList. */ + TargetEntry* target = makeTargetEntry((Expr*)node, (AttrNumber)1, NULL, false); + p_target = list_make1(target); + select_stmt->targetList = list_copy(p_target); + + /* construct Query node for subquery. */ + cparsetree = (Query *)makeNode(Query); + cparsetree->commandType = CMD_SELECT; + cparsetree->utilityStmt = (Node *)select_stmt; + cparsetree->hasSubLinks = true; + cparsetree->canSetTag = true; + cparsetree->jointree = makeFromExpr(NULL, NULL); + cparsetree->targetList = list_copy(p_target); + + StringInfo select_sql = makeStringInfo(); + + /* deparse the SQL statement from the subquery. */ + deparse_query(cparsetree, select_sql, NIL, false, false); + + StmtResult *result = execute_stmt(select_sql->data, true); + + DestroyStringInfo(select_sql); + + bool isnull = result->isnulls[0]; + if (isnull) { + /* return a null const */ + con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false); + (*result->pub.rDestroy)((DestReceiver *)result); + return (Node *)con; + } + + char* value = (char *)linitial((List *)linitial(result->tuples)); + uint len = strlen(value); + char* str_value = (char *)palloc(len + 1); + errno_t rc = strncpy_s(str_value, len + 1, value, len + 1); + securec_check(rc, "\0", "\0"); + str_value[len] = '\0'; + Datum str_datum = CStringGetDatum(str_value); + Oid atttypid = result->atttypids[0]; + + /* convert value to const expression. */ + if (atttypid == BOOLOID) { + if (strcmp(str_value, "t") == 0) { + con = makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(true), false, true); + } else { + con = makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(false), false, true); + } + res = (Node *)con; + } else { + con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, str_datum, false, false); + res = type_transfer((Node *)con, atttypid, true); + } + + (*result->pub.rDestroy)((DestReceiver *)result); + + return res; +} + #endif diff --git a/src/gausskernel/optimizer/util/clauses.cpp b/src/gausskernel/optimizer/util/clauses.cpp index 8261e1607..c5f53f5c7 100644 --- a/src/gausskernel/optimizer/util/clauses.cpp +++ b/src/gausskernel/optimizer/util/clauses.cpp @@ -1324,6 +1324,7 @@ static bool contain_leaky_functions_walker(Node* node, void* context) case T_BooleanTest: case T_List: case T_HashFilter: + case T_UserVar: /* * We know these node types don't contain function calls; but @@ -2345,6 +2346,24 @@ Node* eval_const_expressions_params(PlannerInfo* root, Node* node, ParamListInfo return eval_const_expressions_mutator(node, &context); } +/* + * eval_const_expression_value + * only for set user_defined variables scenes. + * the difference between eval_const_expression_value and eval_const_expressions_params is + * that estimate is set to true. + */ +Node *eval_const_expression_value(PlannerInfo* root, Node* node, ParamListInfo boundParams) +{ + eval_const_expressions_context context; + + context.boundParams = boundParams; + context.root = root; /* for inlined-function dependencies */ + context.active_fns = NIL; /* nothing being recursively simplified */ + context.case_val = NULL; /* no CASE being examined */ + context.estimate = true; /* unsafe transformations OK */ + return eval_const_expressions_mutator(node, &context); +} + /* -------------------- * estimate_expression_value * diff --git a/src/gausskernel/optimizer/util/pgxcship.cpp b/src/gausskernel/optimizer/util/pgxcship.cpp index 478b71ad1..c7ae1abe0 100644 --- a/src/gausskernel/optimizer/util/pgxcship.cpp +++ b/src/gausskernel/optimizer/util/pgxcship.cpp @@ -1216,6 +1216,7 @@ static bool pgxc_shippability_walker(Node* node, Shippability_context* sc_contex switch (nodeTag(node)) { /* Constants are always shippable */ case T_Const: + case T_UserVar: pgxc_set_exprtype_shippability(exprType(node), sc_context); break; diff --git a/src/gausskernel/process/tcop/postgres.cpp b/src/gausskernel/process/tcop/postgres.cpp index 5e2f0c0a7..42d832182 100755 --- a/src/gausskernel/process/tcop/postgres.cpp +++ b/src/gausskernel/process/tcop/postgres.cpp @@ -1152,6 +1152,13 @@ static List* pg_rewrite_query(Query* query) } else { querytree_list = QueryRewriteCTAS(query); } + } else if (IsA(query->utilityStmt, PrepareStmt)) { + PrepareStmt *stmt = (PrepareStmt *)query->utilityStmt; + if (IsA(stmt->query, UserVar)) { + querytree_list = QueryRewritePrepareStmt(query); + } else { + querytree_list = list_make1(query); + } } else { querytree_list = list_make1(query); } @@ -7290,6 +7297,9 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam InitializeGUCOptions(); } + /* set user-defined params */ + init_set_user_params_htab(); + /* * Parse command-line options. */ diff --git a/src/gausskernel/process/tcop/utility.cpp b/src/gausskernel/process/tcop/utility.cpp index fd8a545fb..0476aa278 100755 --- a/src/gausskernel/process/tcop/utility.cpp +++ b/src/gausskernel/process/tcop/utility.cpp @@ -5592,7 +5592,7 @@ void standard_ProcessUtility(Node* parse_tree, const char* query_string, ParamLi #endif case T_VariableSetStmt: - ExecSetVariableStmt((VariableSetStmt*)parse_tree); + ExecSetVariableStmt((VariableSetStmt*)parse_tree, params); #ifdef PGXC /* Let the pooler manage the statement */ if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) { @@ -8523,6 +8523,7 @@ const char* CreateCommandTag(Node* parse_tree) case VAR_SET_DEFAULT: case VAR_SET_MULTI: case VAR_SET_ROLEPWD: + case VAR_SET_DEFINED: tag = "SET"; break; case VAR_RESET: @@ -9504,6 +9505,10 @@ LogStmtLevel GetCommandLogLevel(Node* parse_tree) lev = LOGSTMT_ALL; break; + case T_UserVar: + lev = LOGSTMT_ALL; + break; + case T_VariableShowStmt: lev = LOGSTMT_ALL; break; diff --git a/src/gausskernel/process/threadpool/threadpool_worker.cpp b/src/gausskernel/process/threadpool/threadpool_worker.cpp index e2376d0f6..4f68fba0c 100644 --- a/src/gausskernel/process/threadpool/threadpool_worker.cpp +++ b/src/gausskernel/process/threadpool/threadpool_worker.cpp @@ -819,6 +819,8 @@ static bool InitSession(knl_session_context* session) /* Init GUC option for this session. */ InitializeGUCOptions(); + init_set_user_params_htab(); + /* Read in remaining GUC variables */ read_nondefault_variables(); diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index bd3f7a66b..8aef44d95 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -1041,7 +1041,17 @@ static Datum ExecEvalWholeRowSlow( */ static Datum ExecEvalConst(ExprState* exprstate, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone) { - Const* con = (Const*)exprstate->expr; + Const* con = NULL; + if (IsA(exprstate->expr, UserVar)) { + bool found = false; + UserVar *uservar = (UserVar *)exprstate->expr; + GucUserParamsEntry *entry = (GucUserParamsEntry *)hash_search(u_sess->utils_cxt.set_user_params_htab, uservar->name, HASH_FIND, &found); + + /* if not found, return a null const */ + con = found ? entry->value : makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false); + } else { + con = (Const*)exprstate->expr; + } if (isDone != NULL) *isDone = ExprSingleResult; @@ -5220,6 +5230,7 @@ ExprState* ExecInitExpr(Expr* node, PlanState* parent) } break; case T_Const: + case T_UserVar: state = (ExprState*)makeNode(ExprState); state->evalfunc = ExecEvalConst; break; diff --git a/src/include/commands/sqladvisor.h b/src/include/commands/sqladvisor.h index 07a934661..0bc67bb32 100644 --- a/src/include/commands/sqladvisor.h +++ b/src/include/commands/sqladvisor.h @@ -30,6 +30,8 @@ #include "lib/stringinfo.h" #include "commands/dbcommands.h" #include "nodes/parsenodes.h" +#include "utils/palloc.h" +#include "tcop/dest.h" #define GWC_NUM_OF_BUCKETS (64) #define GWC_HTAB_SIZE (64) @@ -191,6 +193,14 @@ typedef struct { int capacity; } JoinMaxHeap; +typedef struct { + DestReceiver pub; + MemoryContext mxct; + List *tuples; + Oid *atttypids; /* for sublink in user-defined variables */ + bool *isnulls; /* for sublink in user-defined variables */ +} StmtResult; + typedef struct { List* currentSearchPathResult; List* allSearchPathResults; @@ -267,5 +277,6 @@ extern Datum end_collect_workload(PG_FUNCTION_ARGS); extern Datum analyze_workload(PG_FUNCTION_ARGS); extern bool checkSelectIntoParse(SelectStmt* stmt); extern PLpgSQL_datum* copypPlpgsqlDatum(PLpgSQL_datum* datum); +extern StmtResult *execute_stmt(const char *query_string, bool need_result = false); #endif /* SQLADVISOR_H */ diff --git a/src/include/knl/knl_guc/knl_session_attr_common.h b/src/include/knl/knl_guc/knl_session_attr_common.h index 3310ada2c..80902f285 100644 --- a/src/include/knl/knl_guc/knl_session_attr_common.h +++ b/src/include/knl/knl_guc/knl_session_attr_common.h @@ -201,6 +201,7 @@ typedef struct knl_session_attr_common { char* track_stmt_retention_time; bool enable_wdr_snapshot; + bool enable_set_variable_b_format; bool enable_asp; int wdr_snapshot_interval; int wdr_snapshot_retention_days; diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 96a42ed5c..0519ae451 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -644,6 +644,8 @@ typedef struct knl_u_utils_context { syscalllock deleMemContextMutex; unsigned int sql_ignore_strategy_val; + + HTAB* set_user_params_htab; } knl_u_utils_context; typedef struct knl_u_security_context { diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 31656eab5..d0883b31c 100755 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -783,7 +783,9 @@ typedef enum NodeTag { T_PLDebug_frame, T_TdigestData, - T_CentroidPoint + T_CentroidPoint, + T_UserSetElem, + T_UserVar } NodeTag; /* if you add to NodeTag also need to add nodeTagToString */ diff --git a/src/include/nodes/parsenodes_common.h b/src/include/nodes/parsenodes_common.h index 4769705cf..b1cb320ef 100644 --- a/src/include/nodes/parsenodes_common.h +++ b/src/include/nodes/parsenodes_common.h @@ -525,7 +525,8 @@ typedef enum { VAR_SET_MULTI, /* special case for SET TRANSACTION ... */ VAR_SET_ROLEPWD, /* special case for SET ROLE PASSWORD... */ VAR_RESET, /* RESET var */ - VAR_RESET_ALL /* RESET ALL */ + VAR_RESET_ALL, /* RESET ALL */ + VAR_SET_DEFINED /* SET @var_name = expr and SET @var_name := expr */ } VariableSetKind; typedef struct VariableSetStmt { @@ -534,8 +535,21 @@ typedef struct VariableSetStmt { char *name; /* variable to be set */ List *args; /* List of A_Const nodes */ bool is_local; /* SET LOCAL? */ + List *defined_args; /* List of user_defined variable */ } VariableSetStmt; +typedef struct UserVar { + Expr xpr; + char* name; + Expr* value; /* const */ +} UserVar; + +typedef struct UserSetElem { + Expr xpr; + List* name; /* user_defined variable name list, UserVar*/ + Expr* val; /* user_defined variable value*/ +} UserSetElem; + typedef struct AlterRoleSetStmt { NodeTag type; char *role; /* role name */ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index b948e4a3a..17d7408fd 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -95,6 +95,8 @@ extern Node* eval_const_expressions(PlannerInfo* root, Node* node); extern Node* eval_const_expressions_params(PlannerInfo* root, Node* node, ParamListInfo boundParams); +extern Node *eval_const_expression_value(PlannerInfo* root, Node* node, ParamListInfo boundParams); + extern Node* estimate_expression_value(PlannerInfo* root, Node* node, EState* estate = NULL); extern Query* inline_set_returning_function(PlannerInfo* root, RangeTblEntry* rte); diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index d26304664..fb7700531 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -46,6 +46,8 @@ extern TYPCATEGORY TypeCategory(Oid type); extern Node* coerce_to_target_type(ParseState* pstate, Node* expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location); extern bool can_coerce_type(int nargs, Oid* input_typeids, Oid* target_typeids, CoercionContext ccontext); +extern Node *type_transfer(Node *node, Oid atttypid, bool isSelect); +extern Node *const_expression_to_const(Node *node); extern Node* coerce_type(ParseState* pstate, Node* node, Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat, int location); extern Node* coerce_to_domain(Node* arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, CoercionForm cformat, diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index a7880dce2..d6844037d 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -27,5 +27,7 @@ extern void rewriteTargetListMerge(Query* parsetree, Index result_relation, List extern List* QueryRewriteCTAS(Query* parsetree); extern List* QueryRewriteRefresh(Query *parsetree); #endif +extern List* QueryRewritePrepareStmt(Query *parsetree); +extern Node* QueryRewriteNonConstant(Node *node); #endif /* REWRITEHANDLER_H */ diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 4e8cb38e3..861098e7c 100755 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -170,6 +170,11 @@ typedef struct { char* query; /* set query string */ } GucParamsEntry; +typedef struct { + char name[NAMEDATALEN]; /* user-defined name */ + Const *value; +} GucUserParamsEntry; + #define GUC_QUALIFIER_SEPARATOR '.' /* @@ -274,7 +279,7 @@ extern TupleDesc GetPGVariableResultDesc(const char* name); extern char* RewriteBeginQuery(char* query_string, const char* name, List* args); #endif -extern void ExecSetVariableStmt(VariableSetStmt* stmt); +extern void ExecSetVariableStmt(VariableSetStmt* stmt, ParamListInfo paramInfo); extern char* ExtractSetVariableArgs(VariableSetStmt* stmt); extern void ProcessGUCArray(ArrayType* array, GucContext context, GucSource source, GucAction action); @@ -450,8 +455,10 @@ extern void reset_set_message(bool); extern void append_set_message(const char* str); extern void init_set_params_htab(void); +extern void init_set_user_params_htab(void); extern void make_set_message(void); extern int check_set_message_to_send(const VariableSetStmt* stmt, const char* queryString); +extern int check_set_user_message(const UserSetElem *elem); #define TRANS_ENCRYPT_SAMPLE_RNDM "1234567890ABCDEF" #define TRANS_ENCRYPT_SAMPLE_STRING "TRANS_ENCRY_SAMPLE_STRING" diff --git a/src/test/regress/input/set_user_defined_variables_test.source b/src/test/regress/input/set_user_defined_variables_test.source new file mode 100644 index 000000000..2538c1905 --- /dev/null +++ b/src/test/regress/input/set_user_defined_variables_test.source @@ -0,0 +1,476 @@ +-- error +set @v1 := 1; +select @v1; + +\! @abs_bindir@/gs_guc reload -Z datanode -D @abs_srcdir@/tmp_check/datanode1 -c "enable_set_variable_b_format=on" >/dev/null 2>&1 +\! sleep 1 + +-- error +set @v1 := 1; +select @v1; +\c regression +create database test_set dbcompatibility 'b'; +\c test_set +show enable_set_variable_b_format; + +-- test var_name +set @v1 := 1; +set @1a_b.2$3 := 2; +set @a_b.2$3 := 3; +set @_ab.2$3 := 4; +set @.ab_2$3 := 5; +set @$ab.2_3 := 6; +select @v1, @1a_b.2$3, @a_b.2$3, @_ab.2$3, @.ab_2$3, @$ab.2_3; + +-- error +set @gdas()^& := 1; + +select lengthb('abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca'); +set @abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca := 64; +select @abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca as value1, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc as value2, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab as value3; + +\c test_set +set @'v1' := 1; +set @'1a_b.2$3' := 2; +set @'a_b.2$3' := 3; +set @'_ab.2$3' := 4; +set @'.ab_2$3' := 5; +set @'$ab.2_3' := 6; +set @'gdas()^&?<>cs' := 7; +select @v1, @1a_b.2$3, @a_b.2$3, @_ab.2$3, @.ab_2$3, @$ab.2_3, @'v1', @'1a_b.2$3', @'a_b.2$3', @'_ab.2$3', @'.ab_2$3', @'$ab.2_3'; +select @'gdas()^&?<>cs'; +-- error +select @gdas()^&?<>cs; + +set @'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca' := 64; +select @'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca' as value1, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc' as value2, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab' as value3, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca as value4, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc as value5, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab as value6; + + +\c test_set; +set @"v1" := 1; +set @"1a_b.2$3" := 2; +set @"a_b.2$3" := 3; +set @"_ab.2$3" := 4; +set @".ab_2$3" := 5; +set @"$ab.2_3" := 6; +set @"gdas()^&?<>cs" := 7; +select @v1, @1a_b.2$3, @a_b.2$3, @_ab.2$3, @.ab_2$3, @$ab.2_3, +@'v1', @'1a_b.2$3', @'a_b.2$3', @'_ab.2$3', @'.ab_2$3', @'$ab.2_3', +@"v1", @"1a_b.2$3", @"a_b.2$3", @"_ab.2$3", @".ab_2$3", @"$ab.2_3"; +select @"gdas()^&?<>cs", @'gdas()^&?<>cs'; +-- error +select @gdas()^&?<>cs; + +set @"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" := 64; +select @"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" as value1, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" as value2, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab" as value3, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca as value4, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc as value5, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab as value6, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca' as value7, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc' as value8, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab' as value9; + +\c test_set; +set @`v1` := 1; +set @`1a_b.2$3` := 2; +set @`a_b.2$3` := 3; +set @`_ab.2$3` := 4; +set @`.ab_2$3` := 5; +set @`$ab.2_3` := 6; +set @`gdas()^&?<>cs` := 7; +select @v1, @1a_b.2$3, @a_b.2$3, @_ab.2$3, @.ab_2$3, @$ab.2_3, +@'v1', @'1a_b.2$3', @'a_b.2$3', @'_ab.2$3', @'.ab_2$3', @'$ab.2_3', +@"v1", @"1a_b.2$3", @"a_b.2$3", @"_ab.2$3", @".ab_2$3", @"$ab.2_3", +@`v1`, @`1a_b.2$3`, @`a_b.2$3`, @`_ab.2$3`, @`.ab_2$3`, @`$ab.2_3`; +select @`gdas()^&?<>cs`, @"gdas()^&?<>cs", @'gdas()^&?<>cs'; +-- error +select @gdas()^&?<>cs; + +set @`abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca` := 64; +select @`abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca` as value1, +@`abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc` as value2, +@`abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab` as value3, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca as value4, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc as value5, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab as value6, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca' as value7, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc' as value8, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab' as value9, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" as value10, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" as value11, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab" as value12; + +-- test expression +\c test_set +set @v1 := 10, @v2 := 10.3, @v3 := 'abc', @v4 := true, @v5 := date '12-10-2022'; +drop table if exists t_const; +create table t_const as select @v1, @v2, @v3, @v4, @v5; +\d+ t_const +select * from t_const; + +drop table if exists t_bit; +drop table if exists t_bit2; +create table t_bit(f1 bit(8), f2 bit varying(20)); +insert into t_bit values(x'41', x'41'); +set @b1 := x'41', @b2 := x'41':: bit varying, @b3 := (select x'41'), @b4 := (select x'41' :: bit varying), @b5 := (select f1 from t_bit), @b6 := (select f2 from t_bit); +create table t_bit2 as select @b1, @b2, @b3, @b4, @b5, @b6, x'41', x'41'::bit varying; +\d+ t_bit2 +select * from t_bit2; + +set @v7 := '52093.89'::money; +set @v8 := box '((0,0),(1,1))'; +set @v9 := '52093.89'::money::number; +select @v9; + +set @abs := abs(-1), @concat := concat('hello', 'world'), @diameter := diameter(circle '((0,0),2.0)'); +set @time := timestamp '2001-09-28 01:00' + interval '23 hours', @age := age(timestamp '2001-04-10', timestamp '1957-06-13'); +select @abs, @concat, @diameter, @time, @age; + +-- error +set @center := center(box '((0,0),(1,2))'); + +set @value := 10; +set @v_and := 1 and 4, @v_or := 1 or 4, @v_not := not 1; +set @v_all := not ((1 and @value) or @value); +select @value, @v_and, @v_or, @v_not, @v_all; + +set @v2 := 1 + 1, @v3 := 2 - 8, @v4 := 2 * 10, @v5 := @v1 / 2, @v6 := @v1 = 10; +select @v2, @v3, @v4, @v5, @v6; + +set @between := 2 BETWEEN 1 AND 3, @not_between := 2 NOT BETWEEN 1 AND 3; +set @is_null := 2 + NULL IS NULL, @is_not_null := 2 + 2 IS NOT NULL; +set @isnull := @is_null ISNULL, @notnull := @is_not_null NOTNULL; +set @distinct := @between or @not_between IS DISTINCT FROM NULL, @not_distinct := @between + @not_between * NULL IS NOT DISTINCT FROM NULL; +select @between, @not_between, @is_null, @is_not_null, @isnull, @notnull, @distinct, @not_distinct; + +set @rownum := rownum; +select @rownum; + +-- condition expression +set @v1 := 1, @v2 := 0; +set @v_case := CASE WHEN @v1 = 1 THEN 'one' WHEN @v2 = 2 THEN 'two' ELSE 'other' END; +select @v_case; +set @v1 := 0, @v2 := 2; +set @v_case := CASE WHEN @v1 = 1 THEN 'one' WHEN @v2 = 2 THEN 'two' ELSE 'other' END; +select @v_case; +set @v1 := 0, @v2 := 0; +set @v_case := CASE WHEN @v1 = 1 THEN 'one' WHEN @v2 = 2 THEN 'two' ELSE 'other' END; +select @v_case; + +set @v_decode := DECODE('A','A',1,'B',2,0); +set @v_coalesce1 := COALESCE('abc','Hello World'), @v_coalesce2 := COALESCE(NULL,'Hello World'), @v_coalesce3 := COALESCE(NULL, NULL, 'dajd'); +select @v_decode, @v_coalesce1, @v_coalesce2, @v_coalesce3; + +set @v_nullif := NULLIF('Hello','Hello World'), @v_greatest := greatest(9000,155555,2.01), @v_least := least(9000,2), @v_nvl := nvl(null,1); +select @v_nullif, @v_greatest, @v_least, @v_nvl; + +-- subLink +drop table if exists select_tt1; +drop table if exists res_select1; +drop table if exists res_select2; +drop table if exists select_tt2; +drop table if exists select_tt3; +create table select_tt1(f1 int, f2 bool, f3 float, f4 number, f5 text, f6 bit(8), f7 bit varying(20), f8 timestamp); +insert into select_tt1 values(1, false, 12.5, 18.888888888888888888888, NULL, x'41', x'41', timestamp '2001-09-29 03:00'); +set @v_select_bool1 := (select true), @v_select_bool2 := (select f2 from select_tt1); +set @v_select_int1 := (select 1), @v_select_int2 := (select f1 from select_tt1); +set @v_select_float1 := (select -14.4564), @v_select_float2 := (select f3 from select_tt1); +set @v_select_number1 := (select 436721.2321::number), @v_select_number2 := (select f4 from select_tt1); +set @v_select_text1 := (select 'dadsa'), @v_select_text2 := (select f5 from select_tt1); +set @v_select_bit1 := (select x'42'), @v_select_bit2 := (select f6 from select_tt1); +set @v_select_bitvaryng1 := (select x'42' :: bit varying), @v_select_bitvaryng2 := (select f7 from select_tt1); +set @v_select_time1 := timestamp '2021-10-10 01:21:10', @v_select_time2 := (select f8 from select_tt1); +create table res_select1 as select @v_select_bool1, @v_select_int1, @v_select_float1, @v_select_number1, @v_select_text1, @v_select_bit1, @v_select_bitvaryng1, @v_select_time1; +\d+ res_select1 +select * from res_select1; +create table res_select2 as select @v_select_bool2, @v_select_int2, @v_select_float2, @v_select_number2, @v_select_text2, @v_select_bit2, @v_select_bitvaryng2, @v_select_time2; +\d+ res_select2 +select * from res_select2; + +create table select_tt2(f1 int, f2 bool); +insert into select_tt2 values(10, true); +-- error +set @v := (select * from select_tt2); + +create table select_tt3(f1 int); +insert into select_tt3 values(10), (11); +-- error +set @v := (select * from select_tt3); + +set @v_exists := exists (select 10), @v_notexists := not exists (select 10); +select @v_exists, @v_notexists; + +set @v := 1; +set @v_in := @v in (select 1), @v_notin := @v not in (select 2), @v_any := @v < any (select 3), @v_some := @v < some (select 1), @v_all := @v < all (select 2); +select @v_in, @v_notin, @v_any, @v_some, @v_all; + +-- array expression +set @v_in := 8000 + 500 IN (10000, 9000), @v_notin := 8000 + 500 NOT IN (10000, 9000); +set @v_some := 8000 + 500 < SOME (array[10000, 9000]), @v_any := 8000 + 500 < ANY (array[10000, 9000]); +set @v_all := 8000 + 500 < ALL (array[10000, 9000]); +select @v_in, @v_notin, @v_any, @v_some, @v_all; + +-- row expression +set @v_row := ROW(1,2,NULL) < ROW(1,3,0); +select @v_row; + +-- test multi-variable +\c test_set +set @v1 := @v2 := @v3 := @v4 := 10; +select @v1, @v2, @v3, @v4; + +set @v1 = @v2 := @v3 := @v4 := 20; +select @v1, @v2, @v3, @v4; + +-- error +set @v1 = @v2 := (@v3) = @v4 := v5 := 30; + +set @vx := 40; +set @v1 := @v2 := @v3 := @v4 := @vx = 40; +select @v1, @v2, @v3, @v4, @vx; + +\c test_set +set @v1 := 1, @v2 := 2, @v3 := 3; +select @v1, @v2, @v3; + +set @v1 = 11, @v2 = 22, @v3 = 33; +select @v1, @v2, @v3; + +set @v1 := -1, @v2 := -2, @v3 = -3, @v4 := -4; +select @v1, @v2, @v3, @v4; + +-- test in application scenario +\c test_set +set @v1 := -1, @v2 := 'hello'; +select @v1 + 1, abs(@v1), concat(@v2, ' world!'); + +drop table if exists test1; +drop table if exists test2; +create table test1(f1 int, f2 int, f3 varchar(20)); +insert into test1 values(@v1 + 1, abs(@v1), concat(@v2, ' world!')); +select * from test1; + +create table test2 as select @v1 + 1, abs(@v1), concat(@v2, ' world!'); +select * from test2; + +update test1 set f3 = left(@v2, @v1 :: int); +select * from test1; + +-- test prepare +-- selectStmt +\c test_set +set @v1 := 'select * from test1'; +prepare stmt1 as @v1; +execute stmt1; + +-- insertStmt +set @v2 := 'insert into test1 values(1, 2, 123)'; +prepare stmt2 as @v2; +execute stmt2; +select * from test1; + +-- updateStmt +set @vx := 2, @vy := 'world'; +set @v3 := 'update test1 set f3 = left(@vy, (@vx) :: int)'; +prepare stmt3 as @v3; +execute stmt3; +select * from test1; + +-- deleteStmt +set @v4 := 'delete from test1 where f1 = 1'; +prepare stmt4 as @v4; +execute stmt4; +select * from test1; + +-- mergeStmt +set @v5 := 'merge into test1 using test2 on (test2.abs = test1.f2) WHEN MATCHED THEN update set test1.f3 = test2.concat'; +prepare stmt5 as @v5; +execute stmt5; +select * from test1; + +-- otherStmt +set @v6 := 'drop table test2'; +-- error +prepare stmt6 as @v6; + +-- multiStmt +set @v7 := 'select 1; select 2;'; +-- error +prepare stmt6 as @v7; + +-- other scenario +\c test_set +select @ 1; +drop table if exists test_opr; +create table test_opr(f1 int); +insert into test_opr(-1), (-2); +select @ f1 from test_opr; +select @v1; +set @v1 := -10; +select @ @v1; + +set @v1 := 10, @v2 := 'abc'; +drop table if exists test_pro; +create table test_pro(f1 int, f2 varchar(20)); +create or replace procedure pro_insert() +as +begin + insert into test_pro values(@v1, @v2); +end; +/ +call pro_insert(); +select * from test_pro; +set @v1 := 11, @v2 := 'xxx'; +call pro_insert(); +select * from test_pro; +\c test_set +call pro_insert(); +select * from test_pro; + +set @v1 := 10, @v2 := 20; +set @in1 := -2, @in2 := -5; +create or replace function func_add_sql(num1 bigint, num2 bigint) return bigint +as +begin + return num1 - num2 + @v1; +end; +/ +call func_add_sql(-2, -5); +call func_add_sql(num1 => @in1, num2 => @in2); +call func_add_sql(num2 := @in2, num1 := @in1); +call func_add_sql(@in1, @in2); + +create or replace function func_test() return bigint +as +declare + v1 bigint := 5; +begin + v1 := -5; + return @v1 + @v2 + v1; +end; +/ +call func_test(); + +\c test_set +drop table if exists t2; +create table t2(a int, b int); +insert into t2 values(1,2); +select * from t2; + +create or replace procedure autonomous_4(a int, b int) as +declare + num3 int := a; + num4 int := b; + pragma autonomous_transaction; +begin + insert into t2 values(num3, num4); + insert into t2 values(@v1, @v2); +end; +/ + +create or replace procedure autonomous_5(a int, b int) as +declare +begin + insert into t2 values(111, 222); + autonomous_4(a,b); + rollback; +end; +/ + +set @v1 = 1111, @v2 = 2222; +select autonomous_5(11, 22); +select * from t2; + +-- AUTONOMOUS TRANSACTION will start a new session, so user_defined_variable is null. + +\c test_set +drop table if exists tt; +create table tt(f1 int, f2 text, f3 float, f4 bool); +create or replace procedure pro_test1(in a int, in b text, in c float, in d bool) as +declare + num1 int := a; + num2 text := b; + num3 float := c; + num4 bool := d; +begin + set @v1 := num1, @v2 := num2, @v3 := num3, @v4 := num4; + insert into tt values(@v1, @v2, @v3, @v4); + insert into tt values(@v1 + 1, concat(@v2, ' world'), @v3 / 2, @v4); +end; +/ +call pro_test1(1, 'hello', 12.12, true); +select * from tt; +call pro_test1((select 1) + 1, (select 'hello'), 12.12, exists (select 10)); +select * from tt; +delete tt; +\c test_set +call pro_test1(1, 'hello', 12.12, true); +select * from tt; +call pro_test1((select 1) + 1, (select 'hello'), 12.12, exists (select 10)); +select * from tt; +delete tt; + +set @v := 0; +create or replace procedure pro_test2(in a int, in b text, in c float, in d bool) +is +begin + set @v1 := a, @v2 := b, @v3 := c, @v4 := d; + insert into tt values(@v1, @v2, @v3, @v4); + set @v1 := a + (select 1), @v2 := concat(b, (select ' world')), @v3 := abs(c / 2), @v4 := @v in (select 1) or d; + insert into tt values(@v1, @v2, @v3, @v4); + insert into tt values(@v1 + 1, concat(@v2, ' world'), @v3 / 2, @v4); + set @v5 := @v1 + 1, @v6 := concat(@v2, '123'), @v7 := @v3 * 2, @v8 := @v4 and @v; + insert into tt values(@v5, @v6, @v7, @v8); +end; +/ +call pro_test2(1, 'hello', 12.12, true); +select * from tt; +call pro_test2((select 1) + 1, (select 'hello'), 12.12, exists (select 10)); +select * from tt; +delete tt; +\c test_set +set @v := 0; +call pro_test2(1, 'hello', 12.12, true); +select * from tt; +call pro_test2((select 1) + 1, (select 'hello'), 12.12, exists (select 10)); +select * from tt; +delete tt; + +\c test_set +start TRANSACTION; +set @v1 := 1; +select @v1; +set client_encoding = SQL_ASCII; +ROLLBACK; +show client_encoding; +select @v1; + +-- exception scenario +\c test_set +set @v2 := 2; +select 100>@v2; +select 100<@v2; +select 100=@v2; +set @v1=@v2+1; +select 100> @v2, 100< @v2, 100= @v2; +set @v1= @v2+1; +select @v1; +set @ v1:=10; +select @v1:=10; + +\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 +\! sleep 1 + +show enable_set_variable_b_format; \ No newline at end of file diff --git a/src/test/regress/output/set_user_defined_variables_test.source b/src/test/regress/output/set_user_defined_variables_test.source new file mode 100644 index 000000000..812d3e27b --- /dev/null +++ b/src/test/regress/output/set_user_defined_variables_test.source @@ -0,0 +1,1017 @@ +-- error +set @v1 := 1; +ERROR: syntax error at or near "set @" +LINE 1: set @v1 := 1; + ^ +select @v1; +ERROR: column "v1" does not exist +LINE 1: select @v1; + ^ +\! @abs_bindir@/gs_guc reload -Z datanode -D @abs_srcdir@/tmp_check/datanode1 -c "enable_set_variable_b_format=on" >/dev/null 2>&1 +\! sleep 1 +-- error +set @v1 := 1; +ERROR: syntax error at or near "set @" +LINE 1: set @v1 := 1; + ^ +select @v1; +ERROR: column "v1" does not exist +LINE 1: select @v1; + ^ +\c regression +create database test_set dbcompatibility 'b'; +\c test_set +show enable_set_variable_b_format; + enable_set_variable_b_format +------------------------------ + on +(1 row) + +-- test var_name +set @v1 := 1; +set @1a_b.2$3 := 2; +set @a_b.2$3 := 3; +set @_ab.2$3 := 4; +set @.ab_2$3 := 5; +set @$ab.2_3 := 6; +select @v1, @1a_b.2$3, @a_b.2$3, @_ab.2$3, @.ab_2$3, @$ab.2_3; + @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 +-----+-----------+----------+----------+----------+---------- + 1 | 2 | 3 | 4 | 5 | 6 +(1 row) + +-- error +set @gdas()^& := 1; +ERROR: syntax error at or near "(" +LINE 1: set @gdas()^& := 1; + ^ +select lengthb('abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca'); + lengthb +--------- + 64 +(1 row) + +set @abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca := 64; +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +select @abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca as value1, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc as value2, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab as value3; +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" + value1 | value2 | value3 +--------+--------+-------- + 64 | 64 | +(1 row) + +\c test_set +set @'v1' := 1; +set @'1a_b.2$3' := 2; +set @'a_b.2$3' := 3; +set @'_ab.2$3' := 4; +set @'.ab_2$3' := 5; +set @'$ab.2_3' := 6; +set @'gdas()^&?<>cs' := 7; +select @v1, @1a_b.2$3, @a_b.2$3, @_ab.2$3, @.ab_2$3, @$ab.2_3, @'v1', @'1a_b.2$3', @'a_b.2$3', @'_ab.2$3', @'.ab_2$3', @'$ab.2_3'; + @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 | @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 +-----+-----------+----------+----------+----------+----------+-----+-----------+----------+----------+----------+---------- + 1 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 +(1 row) + +select @'gdas()^&?<>cs'; + @gdas()^&?<>cs +---------------- + 7 +(1 row) + +-- error +select @gdas()^&?<>cs; +ERROR: syntax error at or near "(" +LINE 1: select @gdas()^&?<>cs; + ^ +set @'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca' := 64; +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +select @'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca' as value1, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc' as value2, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab' as value3, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca as value4, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc as value5, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab as value6; +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" + value1 | value2 | value3 | value4 | value5 | value6 +--------+--------+--------+--------+--------+-------- + 64 | 64 | | 64 | 64 | +(1 row) + +\c test_set; +set @"v1" := 1; +set @"1a_b.2$3" := 2; +set @"a_b.2$3" := 3; +set @"_ab.2$3" := 4; +set @".ab_2$3" := 5; +set @"$ab.2_3" := 6; +set @"gdas()^&?<>cs" := 7; +select @v1, @1a_b.2$3, @a_b.2$3, @_ab.2$3, @.ab_2$3, @$ab.2_3, +@'v1', @'1a_b.2$3', @'a_b.2$3', @'_ab.2$3', @'.ab_2$3', @'$ab.2_3', +@"v1", @"1a_b.2$3", @"a_b.2$3", @"_ab.2$3", @".ab_2$3", @"$ab.2_3"; + @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 | @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 | @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 +-----+-----------+----------+----------+----------+----------+-----+-----------+----------+----------+----------+----------+-----+-----------+----------+----------+----------+---------- + 1 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 +(1 row) + +select @"gdas()^&?<>cs", @'gdas()^&?<>cs'; + @gdas()^&?<>cs | @gdas()^&?<>cs +----------------+---------------- + 7 | 7 +(1 row) + +-- error +select @gdas()^&?<>cs; +ERROR: syntax error at or near "(" +LINE 1: select @gdas()^&?<>cs; + ^ +set @"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" := 64; +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +select @"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" as value1, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" as value2, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab" as value3, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca as value4, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc as value5, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab as value6, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca' as value7, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc' as value8, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab' as value9; +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" + value1 | value2 | value3 | value4 | value5 | value6 | value7 | value8 | value9 +--------+--------+--------+--------+--------+--------+--------+--------+-------- + 64 | 64 | | 64 | 64 | | 64 | 64 | +(1 row) + +\c test_set; +set @`v1` := 1; +set @`1a_b.2$3` := 2; +set @`a_b.2$3` := 3; +set @`_ab.2$3` := 4; +set @`.ab_2$3` := 5; +set @`$ab.2_3` := 6; +set @`gdas()^&?<>cs` := 7; +select @v1, @1a_b.2$3, @a_b.2$3, @_ab.2$3, @.ab_2$3, @$ab.2_3, +@'v1', @'1a_b.2$3', @'a_b.2$3', @'_ab.2$3', @'.ab_2$3', @'$ab.2_3', +@"v1", @"1a_b.2$3", @"a_b.2$3", @"_ab.2$3", @".ab_2$3", @"$ab.2_3", +@`v1`, @`1a_b.2$3`, @`a_b.2$3`, @`_ab.2$3`, @`.ab_2$3`, @`$ab.2_3`; + @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 | @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 | @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 | @v1 | @1a_b.2$3 | @a_b.2$3 | @_ab.2$3 | @.ab_2$3 | @$ab.2_3 +-----+-----------+----------+----------+----------+----------+-----+-----------+----------+----------+----------+----------+-----+-----------+----------+----------+----------+----------+-----+-----------+----------+----------+----------+---------- + 1 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 +(1 row) + +select @`gdas()^&?<>cs`, @"gdas()^&?<>cs", @'gdas()^&?<>cs'; + @gdas()^&?<>cs | @gdas()^&?<>cs | @gdas()^&?<>cs +----------------+----------------+---------------- + 7 | 7 | 7 +(1 row) + +-- error +select @gdas()^&?<>cs; +ERROR: syntax error at or near "(" +LINE 1: select @gdas()^&?<>cs; + ^ +set @`abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca` := 64; +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +select @`abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca` as value1, +@`abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc` as value2, +@`abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab` as value3, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca as value4, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc as value5, +@abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab as value6, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca' as value7, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc' as value8, +@'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab' as value9, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" as value10, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" as value11, +@"abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcab" as value12; +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" +NOTICE: identifier "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca" will be truncated to "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" + value1 | value2 | value3 | value4 | value5 | value6 | value7 | value8 | value9 | value10 | value11 | value12 +--------+--------+--------+--------+--------+--------+--------+--------+--------+---------+---------+--------- + 64 | 64 | | 64 | 64 | | 64 | 64 | | 64 | 64 | +(1 row) + +-- test expression +\c test_set +set @v1 := 10, @v2 := 10.3, @v3 := 'abc', @v4 := true, @v5 := date '12-10-2022'; +drop table if exists t_const; +NOTICE: table "t_const" does not exist, skipping +create table t_const as select @v1, @v2, @v3, @v4, @v5; +\d+ t_const + Table "public.t_const" + Column | Type | Modifiers | Storage | Stats target | Description +--------+------------------+-----------+----------+--------------+------------- + @v1 | bigint | | plain | | + @v2 | double precision | | plain | | + @v3 | text | | extended | | + @v4 | bigint | | plain | | + @v5 | text | | extended | | +Has OIDs: no +Options: orientation=row, compression=no + +select * from t_const; + @v1 | @v2 | @v3 | @v4 | @v5 +-----+------+-----+-----+------------ + 10 | 10.3 | abc | 1 | 12-10-2022 +(1 row) + +drop table if exists t_bit; +NOTICE: table "t_bit" does not exist, skipping +drop table if exists t_bit2; +NOTICE: table "t_bit2" does not exist, skipping +create table t_bit(f1 bit(8), f2 bit varying(20)); +insert into t_bit values(x'41', x'41'); +set @b1 := x'41', @b2 := x'41':: bit varying, @b3 := (select x'41'), @b4 := (select x'41' :: bit varying), @b5 := (select f1 from t_bit), @b6 := (select f2 from t_bit); +create table t_bit2 as select @b1, @b2, @b3, @b4, @b5, @b6, x'41', x'41'::bit varying; +\d+ t_bit2 + Table "public.t_bit2" + Column | Type | Modifiers | Storage | Stats target | Description +----------+-------------+-----------+----------+--------------+------------- + @b1 | "bit" | | extended | | + @b2 | bit varying | | extended | | + @b3 | "bit" | | extended | | + @b4 | bit varying | | extended | | + @b5 | "bit" | | extended | | + @b6 | bit varying | | extended | | + ?column? | "bit" | | extended | | + varbit | bit varying | | extended | | +Has OIDs: no +Options: orientation=row, compression=no + +select * from t_bit2; + @b1 | @b2 | @b3 | @b4 | @b5 | @b6 | ?column? | varbit +----------+----------+----------+----------+----------+----------+----------+---------- + 01000001 | 01000001 | 01000001 | 01000001 | 01000001 | 01000001 | 01000001 | 01000001 +(1 row) + +set @v7 := '52093.89'::money; +ERROR: failed to find conversion function from money to text +set @v8 := box '((0,0),(1,1))'; +ERROR: failed to find conversion function from box to text +set @v9 := '52093.89'::money::number; +select @v9; + @v9 +---------- + 52093.89 +(1 row) + +set @abs := abs(-1), @concat := concat('hello', 'world'), @diameter := diameter(circle '((0,0),2.0)'); +set @time := timestamp '2001-09-28 01:00' + interval '23 hours', @age := age(timestamp '2001-04-10', timestamp '1957-06-13'); +select @abs, @concat, @diameter, @time, @age; + @abs | @concat | @diameter | @time | @age +------+------------+-----------+--------------------------+--------------------------- + 1 | helloworld | 4 | Sat Sep 29 00:00:00 2001 | @ 43 years 9 mons 27 days +(1 row) + +-- error +set @center := center(box '((0,0),(1,2))'); +ERROR: failed to find conversion function from point to text +set @value := 10; +set @v_and := 1 and 4, @v_or := 1 or 4, @v_not := not 1; +set @v_all := not ((1 and @value) or @value); +select @value, @v_and, @v_or, @v_not, @v_all; + @value | @v_and | @v_or | @v_not | @v_all +--------+--------+-------+--------+-------- + 10 | 1 | 1 | 0 | 0 +(1 row) + +set @v2 := 1 + 1, @v3 := 2 - 8, @v4 := 2 * 10, @v5 := @v1 / 2, @v6 := @v1 = 10; +select @v2, @v3, @v4, @v5, @v6; + @v2 | @v3 | @v4 | @v5 | @v6 +-----+-----+-----+-----+----- + 2 | -6 | 20 | 5 | 1 +(1 row) + +set @between := 2 BETWEEN 1 AND 3, @not_between := 2 NOT BETWEEN 1 AND 3; +set @is_null := 2 + NULL IS NULL, @is_not_null := 2 + 2 IS NOT NULL; +set @isnull := @is_null ISNULL, @notnull := @is_not_null NOTNULL; +set @distinct := @between or @not_between IS DISTINCT FROM NULL, @not_distinct := @between + @not_between * NULL IS NOT DISTINCT FROM NULL; +select @between, @not_between, @is_null, @is_not_null, @isnull, @notnull, @distinct, @not_distinct; + @between | @not_between | @is_null | @is_not_null | @isnull | @notnull | @distinct | @not_distinct +----------+--------------+----------+--------------+---------+----------+-----------+--------------- + 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 +(1 row) + +set @rownum := rownum; +select @rownum; + @rownum +--------- + 1 +(1 row) + +-- condition expression +set @v1 := 1, @v2 := 0; +set @v_case := CASE WHEN @v1 = 1 THEN 'one' WHEN @v2 = 2 THEN 'two' ELSE 'other' END; +select @v_case; + @v_case +--------- + one +(1 row) + +set @v1 := 0, @v2 := 2; +set @v_case := CASE WHEN @v1 = 1 THEN 'one' WHEN @v2 = 2 THEN 'two' ELSE 'other' END; +select @v_case; + @v_case +--------- + two +(1 row) + +set @v1 := 0, @v2 := 0; +set @v_case := CASE WHEN @v1 = 1 THEN 'one' WHEN @v2 = 2 THEN 'two' ELSE 'other' END; +select @v_case; + @v_case +--------- + other +(1 row) + +set @v_decode := DECODE('A','A',1,'B',2,0); +set @v_coalesce1 := COALESCE('abc','Hello World'), @v_coalesce2 := COALESCE(NULL,'Hello World'), @v_coalesce3 := COALESCE(NULL, NULL, 'dajd'); +select @v_decode, @v_coalesce1, @v_coalesce2, @v_coalesce3; + @v_decode | @v_coalesce1 | @v_coalesce2 | @v_coalesce3 +-----------+--------------+--------------+-------------- + 1 | abc | Hello World | dajd +(1 row) + +set @v_nullif := NULLIF('Hello','Hello World'), @v_greatest := greatest(9000,155555,2.01), @v_least := least(9000,2), @v_nvl := nvl(null,1); +select @v_nullif, @v_greatest, @v_least, @v_nvl; + @v_nullif | @v_greatest | @v_least | @v_nvl +-----------+-------------+----------+-------- + Hello | 155555 | 2 | 1 +(1 row) + +-- subLink +drop table if exists select_tt1; +NOTICE: table "select_tt1" does not exist, skipping +drop table if exists res_select1; +NOTICE: table "res_select1" does not exist, skipping +drop table if exists res_select2; +NOTICE: table "res_select2" does not exist, skipping +drop table if exists select_tt2; +NOTICE: table "select_tt2" does not exist, skipping +drop table if exists select_tt3; +NOTICE: table "select_tt3" does not exist, skipping +create table select_tt1(f1 int, f2 bool, f3 float, f4 number, f5 text, f6 bit(8), f7 bit varying(20), f8 timestamp); +insert into select_tt1 values(1, false, 12.5, 18.888888888888888888888, NULL, x'41', x'41', timestamp '2001-09-29 03:00'); +set @v_select_bool1 := (select true), @v_select_bool2 := (select f2 from select_tt1); +set @v_select_int1 := (select 1), @v_select_int2 := (select f1 from select_tt1); +set @v_select_float1 := (select -14.4564), @v_select_float2 := (select f3 from select_tt1); +set @v_select_number1 := (select 436721.2321::number), @v_select_number2 := (select f4 from select_tt1); +set @v_select_text1 := (select 'dadsa'), @v_select_text2 := (select f5 from select_tt1); +set @v_select_bit1 := (select x'42'), @v_select_bit2 := (select f6 from select_tt1); +set @v_select_bitvaryng1 := (select x'42' :: bit varying), @v_select_bitvaryng2 := (select f7 from select_tt1); +set @v_select_time1 := timestamp '2021-10-10 01:21:10', @v_select_time2 := (select f8 from select_tt1); +create table res_select1 as select @v_select_bool1, @v_select_int1, @v_select_float1, @v_select_number1, @v_select_text1, @v_select_bit1, @v_select_bitvaryng1, @v_select_time1; +\d+ res_select1 + Table "public.res_select1" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+------------------+-----------+----------+--------------+------------- + @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 | | + @v_select_bit1 | "bit" | | extended | | + @v_select_bitvaryng1 | bit varying | | extended | | + @v_select_time1 | text | | extended | | +Has OIDs: no +Options: orientation=row, compression=no + +select * from res_select1; + @v_select_bool1 | @v_select_int1 | @v_select_float1 | @v_select_number1 | @v_select_text1 | @v_select_bit1 | @v_select_bitvaryng1 | @v_select_time1 +-----------------+----------------+------------------+-------------------+-----------------+----------------+----------------------+-------------------------- + 1 | 1 | -14.4564 | 436721.2321 | dadsa | 01000010 | 01000010 | Sun Oct 10 01:21:10 2021 +(1 row) + +create table res_select2 as select @v_select_bool2, @v_select_int2, @v_select_float2, @v_select_number2, @v_select_text2, @v_select_bit2, @v_select_bitvaryng2, @v_select_time2; +\d+ res_select2 + Table "public.res_select2" + Column | Type | Modifiers | Storage | Stats target | Description +----------------------+------------------+-----------+----------+--------------+------------- + @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 | | + @v_select_bit2 | "bit" | | extended | | + @v_select_bitvaryng2 | bit varying | | extended | | + @v_select_time2 | text | | extended | | +Has OIDs: no +Options: orientation=row, compression=no + +select * from res_select2; + @v_select_bool2 | @v_select_int2 | @v_select_float2 | @v_select_number2 | @v_select_text2 | @v_select_bit2 | @v_select_bitvaryng2 | @v_select_time2 +-----------------+----------------+------------------+-------------------+-----------------+----------------+----------------------+-------------------------- + 0 | 1 | 12.5 | 18.8888888888889 | | 01000001 | 01000001 | Sat Sep 29 03:00:00 2001 +(1 row) + +create table select_tt2(f1 int, f2 bool); +insert into select_tt2 values(10, true); +-- error +set @v := (select * from select_tt2); +ERROR: subquery must return only one column +LINE 1: set @v := (select * from select_tt2); + ^ +create table select_tt3(f1 int); +insert into select_tt3 values(10), (11); +-- error +set @v := (select * from select_tt3); +ERROR: more than one row returned by a subquery used as an expression +CONTEXT: referenced column: f1 +set @v_exists := exists (select 10), @v_notexists := not exists (select 10); +select @v_exists, @v_notexists; + @v_exists | @v_notexists +-----------+-------------- + 1 | 0 +(1 row) + +set @v := 1; +set @v_in := @v in (select 1), @v_notin := @v not in (select 2), @v_any := @v < any (select 3), @v_some := @v < some (select 1), @v_all := @v < all (select 2); +select @v_in, @v_notin, @v_any, @v_some, @v_all; + @v_in | @v_notin | @v_any | @v_some | @v_all +-------+----------+--------+---------+-------- + 1 | 1 | 1 | 0 | 1 +(1 row) + +-- array expression +set @v_in := 8000 + 500 IN (10000, 9000), @v_notin := 8000 + 500 NOT IN (10000, 9000); +set @v_some := 8000 + 500 < SOME (array[10000, 9000]), @v_any := 8000 + 500 < ANY (array[10000, 9000]); +set @v_all := 8000 + 500 < ALL (array[10000, 9000]); +select @v_in, @v_notin, @v_any, @v_some, @v_all; + @v_in | @v_notin | @v_any | @v_some | @v_all +-------+----------+--------+---------+-------- + 0 | 1 | 1 | 1 | 1 +(1 row) + +-- row expression +set @v_row := ROW(1,2,NULL) < ROW(1,3,0); +select @v_row; + @v_row +-------- + 1 +(1 row) + +-- test multi-variable +\c test_set +set @v1 := @v2 := @v3 := @v4 := 10; +select @v1, @v2, @v3, @v4; + @v1 | @v2 | @v3 | @v4 +-----+-----+-----+----- + 10 | 10 | 10 | 10 +(1 row) + +set @v1 = @v2 := @v3 := @v4 := 20; +select @v1, @v2, @v3, @v4; + @v1 | @v2 | @v3 | @v4 +-----+-----+-----+----- + 20 | 20 | 20 | 20 +(1 row) + +-- error +set @v1 = @v2 := (@v3) = @v4 := v5 := 30; +ERROR: syntax error at or near ":=" +LINE 1: set @v1 = @v2 := (@v3) = @v4 := v5 := 30; + ^ +set @vx := 40; +set @v1 := @v2 := @v3 := @v4 := @vx = 40; +select @v1, @v2, @v3, @v4, @vx; + @v1 | @v2 | @v3 | @v4 | @vx +-----+-----+-----+-----+----- + 1 | 1 | 1 | 1 | 40 +(1 row) + +\c test_set +set @v1 := 1, @v2 := 2, @v3 := 3; +select @v1, @v2, @v3; + @v1 | @v2 | @v3 +-----+-----+----- + 1 | 2 | 3 +(1 row) + +set @v1 = 11, @v2 = 22, @v3 = 33; +select @v1, @v2, @v3; + @v1 | @v2 | @v3 +-----+-----+----- + 11 | 22 | 33 +(1 row) + +set @v1 := -1, @v2 := -2, @v3 = -3, @v4 := -4; +select @v1, @v2, @v3, @v4; + @v1 | @v2 | @v3 | @v4 +-----+-----+-----+----- + -1 | -2 | -3 | -4 +(1 row) + +-- test in application scenario +\c test_set +set @v1 := -1, @v2 := 'hello'; +select @v1 + 1, abs(@v1), concat(@v2, ' world!'); + ?column? | abs | concat +----------+-----+-------------- + 0 | 1 | hello world! +(1 row) + +drop table if exists test1; +NOTICE: table "test1" does not exist, skipping +drop table if exists test2; +NOTICE: table "test2" does not exist, skipping +create table test1(f1 int, f2 int, f3 varchar(20)); +insert into test1 values(@v1 + 1, abs(@v1), concat(@v2, ' world!')); +select * from test1; + f1 | f2 | f3 +----+----+-------------- + 0 | 1 | hello world! +(1 row) + +create table test2 as select @v1 + 1, abs(@v1), concat(@v2, ' world!'); +select * from test2; + ?column? | abs | concat +----------+-----+-------------- + 0 | 1 | hello world! +(1 row) + +update test1 set f3 = left(@v2, @v1 :: int); +select * from test1; + f1 | f2 | f3 +----+----+------ + 0 | 1 | hell +(1 row) + +-- test prepare +-- selectStmt +\c test_set +set @v1 := 'select * from test1'; +prepare stmt1 as @v1; +execute stmt1; + f1 | f2 | f3 +----+----+------ + 0 | 1 | hell +(1 row) + +-- insertStmt +set @v2 := 'insert into test1 values(1, 2, 123)'; +prepare stmt2 as @v2; +execute stmt2; +select * from test1; + f1 | f2 | f3 +----+----+------ + 0 | 1 | hell + 1 | 2 | 123 +(2 rows) + +-- updateStmt +set @vx := 2, @vy := 'world'; +set @v3 := 'update test1 set f3 = left(@vy, (@vx) :: int)'; +prepare stmt3 as @v3; +execute stmt3; +select * from test1; + f1 | f2 | f3 +----+----+---- + 0 | 1 | wo + 1 | 2 | wo +(2 rows) + +-- deleteStmt +set @v4 := 'delete from test1 where f1 = 1'; +prepare stmt4 as @v4; +execute stmt4; +select * from test1; + f1 | f2 | f3 +----+----+---- + 0 | 1 | wo +(1 row) + +-- mergeStmt +set @v5 := 'merge into test1 using test2 on (test2.abs = test1.f2) WHEN MATCHED THEN update set test1.f3 = test2.concat'; +prepare stmt5 as @v5; +execute stmt5; +select * from test1; + f1 | f2 | f3 +----+----+-------------- + 0 | 1 | hello world! +(1 row) + +-- otherStmt +set @v6 := 'drop table test2'; +-- error +prepare stmt6 as @v6; +ERROR: the statement in prepate is not supported. +-- multiStmt +set @v7 := 'select 1; select 2;'; +-- error +prepare stmt6 as @v7; +ERROR: prepare user_defined variable can contain only one SQL statement. +-- other scenario +\c test_set +select @ 1; + ?column? +---------- + 1 +(1 row) + +drop table if exists test_opr; +NOTICE: table "test_opr" does not exist, skipping +create table test_opr(f1 int); +insert into test_opr(-1), (-2); +ERROR: syntax error at or near "-" +LINE 1: insert into test_opr(-1), (-2); + ^ +select @ f1 from test_opr; + ?column? +---------- +(0 rows) + +select @v1; + @v1 +----- + +(1 row) + +set @v1 := -10; +select @ @v1; + ?column? +---------- + 10 +(1 row) + +set @v1 := 10, @v2 := 'abc'; +drop table if exists test_pro; +NOTICE: table "test_pro" does not exist, skipping +create table test_pro(f1 int, f2 varchar(20)); +create or replace procedure pro_insert() +as +begin + insert into test_pro values(@v1, @v2); +end; +/ +call pro_insert(); + pro_insert +------------ + +(1 row) + +select * from test_pro; + f1 | f2 +----+----- + 10 | abc +(1 row) + +set @v1 := 11, @v2 := 'xxx'; +call pro_insert(); + pro_insert +------------ + +(1 row) + +select * from test_pro; + f1 | f2 +----+----- + 10 | abc + 11 | xxx +(2 rows) + +\c test_set +call pro_insert(); + pro_insert +------------ + +(1 row) + +select * from test_pro; + f1 | f2 +----+----- + 10 | abc + 11 | xxx + | +(3 rows) + +set @v1 := 10, @v2 := 20; +set @in1 := -2, @in2 := -5; +create or replace function func_add_sql(num1 bigint, num2 bigint) return bigint +as +begin + return num1 - num2 + @v1; +end; +/ +call func_add_sql(-2, -5); + func_add_sql +-------------- + 13 +(1 row) + +call func_add_sql(num1 => @in1, num2 => @in2); + func_add_sql +-------------- + 13 +(1 row) + +call func_add_sql(num2 := @in2, num1 := @in1); + func_add_sql +-------------- + 13 +(1 row) + +call func_add_sql(@in1, @in2); + func_add_sql +-------------- + 13 +(1 row) + +create or replace function func_test() return bigint +as +declare + v1 bigint := 5; +begin + v1 := -5; + return @v1 + @v2 + v1; +end; +/ +call func_test(); + func_test +----------- + 25 +(1 row) + +\c test_set +drop table if exists t2; +NOTICE: table "t2" does not exist, skipping +create table t2(a int, b int); +insert into t2 values(1,2); +select * from t2; + a | b +---+--- + 1 | 2 +(1 row) + +create or replace procedure autonomous_4(a int, b int) as +declare + num3 int := a; + num4 int := b; + pragma autonomous_transaction; +begin + insert into t2 values(num3, num4); + insert into t2 values(@v1, @v2); +end; +/ +create or replace procedure autonomous_5(a int, b int) as +declare +begin + insert into t2 values(111, 222); + autonomous_4(a,b); + rollback; +end; +/ +set @v1 = 1111, @v2 = 2222; +select autonomous_5(11, 22); + autonomous_5 +-------------- + +(1 row) + +select * from t2; + a | b +----+---- + 1 | 2 + 11 | 22 + | +(3 rows) + +-- AUTONOMOUS TRANSACTION will start a new session, so user_defined_variable is null. +\c test_set +drop table if exists tt; +NOTICE: table "tt" does not exist, skipping +create table tt(f1 int, f2 text, f3 float, f4 bool); +create or replace procedure pro_test1(in a int, in b text, in c float, in d bool) as +declare + num1 int := a; + num2 text := b; + num3 float := c; + num4 bool := d; +begin + set @v1 := num1, @v2 := num2, @v3 := num3, @v4 := num4; + insert into tt values(@v1, @v2, @v3, @v4); + insert into tt values(@v1 + 1, concat(@v2, ' world'), @v3 / 2, @v4); +end; +/ +call pro_test1(1, 'hello', 12.12, true); + pro_test1 +----------- + +(1 row) + +select * from tt; + f1 | f2 | f3 | f4 +----+-------------+-------+---- + 1 | hello | 12.12 | t + 2 | hello world | 6.06 | t +(2 rows) + +call pro_test1((select 1) + 1, (select 'hello'), 12.12, exists (select 10)); + pro_test1 +----------- + +(1 row) + +select * from tt; + f1 | f2 | f3 | f4 +----+-------------+-------+---- + 1 | hello | 12.12 | t + 2 | hello world | 6.06 | t + 2 | hello | 12.12 | t + 3 | hello world | 6.06 | t +(4 rows) + +delete tt; +\c test_set +call pro_test1(1, 'hello', 12.12, true); + pro_test1 +----------- + +(1 row) + +select * from tt; + f1 | f2 | f3 | f4 +----+-------------+-------+---- + 1 | hello | 12.12 | t + 2 | hello world | 6.06 | t +(2 rows) + +call pro_test1((select 1) + 1, (select 'hello'), 12.12, exists (select 10)); + pro_test1 +----------- + +(1 row) + +select * from tt; + f1 | f2 | f3 | f4 +----+-------------+-------+---- + 1 | hello | 12.12 | t + 2 | hello world | 6.06 | t + 2 | hello | 12.12 | t + 3 | hello world | 6.06 | t +(4 rows) + +delete tt; +set @v := 0; +create or replace procedure pro_test2(in a int, in b text, in c float, in d bool) +is +begin + set @v1 := a, @v2 := b, @v3 := c, @v4 := d; + insert into tt values(@v1, @v2, @v3, @v4); + set @v1 := a + (select 1), @v2 := concat(b, (select ' world')), @v3 := abs(c / 2), @v4 := @v in (select 1) or d; + insert into tt values(@v1, @v2, @v3, @v4); + insert into tt values(@v1 + 1, concat(@v2, ' world'), @v3 / 2, @v4); + set @v5 := @v1 + 1, @v6 := concat(@v2, '123'), @v7 := @v3 * 2, @v8 := @v4 and @v; + insert into tt values(@v5, @v6, @v7, @v8); +end; +/ +call pro_test2(1, 'hello', 12.12, true); + pro_test2 +----------- + +(1 row) + +select * from tt; + f1 | f2 | f3 | f4 +----+-------------------+-------+---- + 1 | hello | 12.12 | t + 2 | hello world | 6.06 | t + 3 | hello world world | 3.03 | t + 3 | hello world123 | 12.12 | f +(4 rows) + +call pro_test2((select 1) + 1, (select 'hello'), 12.12, exists (select 10)); + pro_test2 +----------- + +(1 row) + +select * from tt; + f1 | f2 | f3 | f4 +----+-------------------+-------+---- + 1 | hello | 12.12 | t + 2 | hello world | 6.06 | t + 3 | hello world world | 3.03 | t + 3 | hello world123 | 12.12 | f + 2 | hello | 12.12 | t + 3 | hello world | 6.06 | t + 4 | hello world world | 3.03 | t + 4 | hello world123 | 12.12 | f +(8 rows) + +delete tt; +\c test_set +set @v := 0; +call pro_test2(1, 'hello', 12.12, true); + pro_test2 +----------- + +(1 row) + +select * from tt; + f1 | f2 | f3 | f4 +----+-------------------+-------+---- + 1 | hello | 12.12 | t + 2 | hello world | 6.06 | t + 3 | hello world world | 3.03 | t + 3 | hello world123 | 12.12 | f +(4 rows) + +call pro_test2((select 1) + 1, (select 'hello'), 12.12, exists (select 10)); + pro_test2 +----------- + +(1 row) + +select * from tt; + f1 | f2 | f3 | f4 +----+-------------------+-------+---- + 1 | hello | 12.12 | t + 2 | hello world | 6.06 | t + 3 | hello world world | 3.03 | t + 3 | hello world123 | 12.12 | f + 2 | hello | 12.12 | t + 3 | hello world | 6.06 | t + 4 | hello world world | 3.03 | t + 4 | hello world123 | 12.12 | f +(8 rows) + +delete tt; +\c test_set +start TRANSACTION; +set @v1 := 1; +select @v1; + @v1 +----- + 1 +(1 row) + +set client_encoding = SQL_ASCII; +ROLLBACK; +show client_encoding; + client_encoding +----------------- + UTF8 +(1 row) + +select @v1; + @v1 +----- + 1 +(1 row) + +-- exception scenario +\c test_set +set @v2 := 2; +select 100>@v2; +ERROR: column "v2" does not exist +LINE 1: select 100>@v2; + ^ +select 100<@v2; +ERROR: column "v2" does not exist +LINE 1: select 100<@v2; + ^ +select 100=@v2; +ERROR: column "v2" does not exist +LINE 1: select 100=@v2; + ^ +set @v1=@v2+1; +ERROR: syntax error at or near "=@" +LINE 1: set @v1=@v2+1; + ^ +select 100> @v2, 100< @v2, 100= @v2; + ?column? | ?column? | ?column? +----------+----------+---------- + t | f | f +(1 row) + +set @v1= @v2+1; +select @v1; + @v1 +----- + 3 +(1 row) + +set @ v1:=10; +ERROR: syntax error at or near "@" +LINE 1: set @ v1:=10; + ^ +select @v1:=10; +ERROR: unrecognized node type: 5009 +\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 +\! sleep 1 +show enable_set_variable_b_format; + enable_set_variable_b_format +------------------------------ + off +(1 row) + diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index cccf21017..fc1c54746 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -16,6 +16,9 @@ test: pg_proc_test test: parse_page test: parse_xlog +#test user_defined_variable +test: set_user_defined_variables_test + test: gs_dump_package test: out_param_func