forked from openGauss-Ecosystem/openGauss-server
!2069 实现兼容MySQL SET自定义用户变量的功能
Merge pull request !2069 from yuhuanhuan/demand_set
This commit is contained in:
commit
5e987cdd3a
|
@ -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>
|
|
@ -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|
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue