!4793 修复: prepare 结合@变量 导致的计划缓存问题
Merge pull request !4793 from chenbd/fix_mas_prepare
This commit is contained in:
@ -134,6 +134,60 @@ void TryMotJitCodegenQuery(const char* queryString, CachedPlanSource* psrc, Quer
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static void QueryRewritePrepareStmt(Node* parsetree)
|
||||
{
|
||||
char *sqlstr = NULL;
|
||||
List* raw_parsetree_list = NIL;
|
||||
PrepareStmt *stmt = (PrepareStmt *)parsetree;
|
||||
ParseState* state = make_parsestate(NULL);
|
||||
UserVar *uservar = (UserVar *)transformExpr(state, (Node *)stmt->query, EXPR_KIND_EXECUTE_PARAMETER);
|
||||
free_parsestate(state);
|
||||
Const* value = (Const *)uservar->value;
|
||||
|
||||
if (value->consttype != TEXTOID) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
|
||||
errmsg("userdefined variable in prepare statement must be text type.")));
|
||||
}
|
||||
if (value->constvalue == (Datum)0) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("Query was empty")));
|
||||
}
|
||||
|
||||
sqlstr = TextDatumGetCString(value->constvalue);
|
||||
|
||||
raw_parsetree_list = pg_parse_query(sqlstr);
|
||||
if (raw_parsetree_list == NIL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("Query was empty")));
|
||||
}
|
||||
|
||||
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 prepare is not supported.")));
|
||||
break;
|
||||
}
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the 'PREPARE' utility statement.
|
||||
@ -155,6 +209,9 @@ void PrepareQuery(PrepareStmt* stmt, const char* queryString)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION), errmsg("invalid statement name: must not be empty")));
|
||||
|
||||
if (IsA(stmt->query, UserVar)) {
|
||||
QueryRewritePrepareStmt((Node*)stmt);
|
||||
}
|
||||
/*
|
||||
* Create the CachedPlanSource before we do parse analysis, since it needs
|
||||
* to see the unmodified raw parse tree.
|
||||
|
@ -5156,62 +5156,6 @@ 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 prepare statement must be text type.")));
|
||||
}
|
||||
if (value->constvalue == (Datum)0) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("Query was empty")));
|
||||
}
|
||||
|
||||
sqlstr = TextDatumGetCString(value->constvalue);
|
||||
|
||||
raw_parsetree_list = pg_parse_query(sqlstr);
|
||||
if (raw_parsetree_list == NIL) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("Query was empty")));
|
||||
}
|
||||
|
||||
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 prepare 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:
|
||||
|
@ -1276,9 +1276,7 @@ static List* pg_rewrite_query(Query* query)
|
||||
}
|
||||
} else if (IsA(query->utilityStmt, PrepareStmt)) {
|
||||
PrepareStmt *stmt = (PrepareStmt *)query->utilityStmt;
|
||||
if (IsA(stmt->query, UserVar)) {
|
||||
querytree_list = QueryRewritePrepareStmt(query);
|
||||
} else if (IsA(stmt->query, Const)) {
|
||||
if (IsA(stmt->query, Const)) {
|
||||
if (((Const *)stmt->query)->constisnull) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
|
||||
errmsg("userdefined variable in prepare statement must be text type.")));
|
||||
|
@ -33,7 +33,6 @@ extern bool view_has_instead_trigger(Relation view, CmdType event);
|
||||
extern List* QueryRewriteCTAS(Query* parsetree);
|
||||
extern List* QueryRewriteRefresh(Query *parsetree);
|
||||
#endif
|
||||
extern List* QueryRewritePrepareStmt(Query *parsetree);
|
||||
extern Node* QueryRewriteNonConstant(Node *node);
|
||||
extern List* QueryRewriteSelectIntoVarList(Node *node, int res_len);
|
||||
extern Const* processResToConst(char* value, Oid atttypid, Oid collid);
|
||||
|
@ -842,6 +842,45 @@ begin
|
||||
perform @plg := @plg + 1 from t_plg;
|
||||
end;
|
||||
$$;
|
||||
-- bugfix for in plpgsql
|
||||
prepare stmtabv as select 123;
|
||||
CREATE OR REPLACE PROCEDURE pppabc (id text) as
|
||||
begin
|
||||
set @abcttt = concat('select ' , id );
|
||||
DEALLOCATE prepare stmtabv;
|
||||
prepare stmtabv as @abcttt;
|
||||
end;
|
||||
/
|
||||
execute stmtabv;
|
||||
?column?
|
||||
----------
|
||||
123
|
||||
(1 row)
|
||||
|
||||
call pppabc(12334);
|
||||
pppabc
|
||||
--------
|
||||
|
||||
(1 row)
|
||||
|
||||
execute stmtabv;
|
||||
?column?
|
||||
----------
|
||||
12334
|
||||
(1 row)
|
||||
|
||||
call pppabc(123345);
|
||||
pppabc
|
||||
--------
|
||||
|
||||
(1 row)
|
||||
|
||||
execute stmtabv;
|
||||
?column?
|
||||
----------
|
||||
123345
|
||||
(1 row)
|
||||
|
||||
--test in where
|
||||
CREATE TABLE employee (
|
||||
id int ,
|
||||
|
@ -250,6 +250,25 @@ begin
|
||||
perform @plg := @plg + 1 from t_plg;
|
||||
end;
|
||||
$$;
|
||||
-- bugfix for in plpgsql
|
||||
prepare stmtabv as select 123;
|
||||
CREATE OR REPLACE PROCEDURE pppabc (id text) as
|
||||
begin
|
||||
set @abcttt = concat('select ' , id );
|
||||
DEALLOCATE prepare stmtabv;
|
||||
prepare stmtabv as @abcttt;
|
||||
end;
|
||||
/
|
||||
|
||||
execute stmtabv;
|
||||
|
||||
call pppabc(12334);
|
||||
|
||||
execute stmtabv;
|
||||
|
||||
call pppabc(123345);
|
||||
|
||||
execute stmtabv;
|
||||
|
||||
--test in where
|
||||
CREATE TABLE employee (
|
||||
|
Reference in New Issue
Block a user