!2069 实现兼容MySQL SET自定义用户变量的功能

Merge pull request !2069 from yuhuanhuan/demand_set
This commit is contained in:
opengauss-bot 2022-08-31 14:22:28 +00:00 committed by Gitee
commit 5e987cdd3a
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
41 changed files with 2396 additions and 25 deletions

View File

@ -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.
</synopsis>
</refsynopsisdiv>
</refentry>

View File

@ -311,6 +311,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|

View File

@ -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);
@ -5600,6 +5622,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;
}
@ -8018,6 +8041,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;

View File

@ -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);
@ -4089,6 +4106,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;

View File

@ -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;

View File

@ -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)

View File

@ -3917,6 +3917,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");
@ -6513,6 +6530,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;

View File

@ -5754,6 +5754,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
*
@ -6213,6 +6233,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),

View File

@ -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;
@ -2245,6 +2273,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

View File

@ -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> with_clause opt_with_clause
%type <list> cte_list
%type <list> user_defined_list
%type <node> uservar_name user_defined_single
%type <list> within_group_clause pkg_body_subprogram
%type <list> window_clause window_definition_list opt_partition_clause
%type <windef> 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 <str> IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp COMMENTSTRING
%token <str> IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp COMMENTSTRING SET_USER_IDENT
%token <ival> 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
@ -18541,7 +18630,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 */
}
}
;
/*****************************************************************************
*
@ -21403,7 +21514,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); }
@ -22058,7 +22194,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; }

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -9121,6 +9121,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;

View File

@ -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",
@ -1054,6 +1059,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,
@ -8537,7 +8553,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;
@ -8784,6 +8800,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;
}
@ -12528,6 +12570,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()
{

View File

@ -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 <str> IDENT FCONST SCONST BCONST XCONST Op CmpOp COMMENTSTRING
%token <str> IDENT FCONST SCONST BCONST XCONST Op CmpOp COMMENTSTRING SET_USER_IDENT
%token <ival> ICONST PARAM
%token TYPECAST ORA_JOINOP DOT_DOT COLON_EQUALS PARA_EQUALS

View File

@ -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 <str> IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp COMMENTSTRING
%token <str> IDENT FCONST SCONST BCONST VCONST XCONST Op CmpOp COMMENTSTRING SET_USER_IDENT
%token <ival> ICONST PARAM
%token TYPECAST ORA_JOINOP DOT_DOT COLON_EQUALS PARA_EQUALS

View File

@ -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. */

View File

@ -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);
}

View File

@ -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"
@ -3288,4 +3290,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

View File

@ -1325,6 +1325,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
@ -2346,6 +2347,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
*

View File

@ -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;

View File

@ -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.
*/

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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 */

View File

@ -202,6 +202,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;

View File

@ -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 {

View File

@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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,

View File

@ -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 */

View File

@ -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"

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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