mirror of
https://github.com/postgres/postgres.git
synced 2025-08-22 21:53:06 +03:00
PREPARE/EXECUTE statements. Patch by Neil Conway, some kibitzing
from Tom Lane.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.243 2002/08/27 03:56:34 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.244 2002/08/27 04:55:07 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -20,7 +20,10 @@
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/prepare.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "parser/analyze.h"
|
||||
#include "parser/gramparse.h"
|
||||
#include "parser/parsetree.h"
|
||||
@@ -94,6 +97,8 @@ static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
||||
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
||||
static Query *transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt);
|
||||
static Query *transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt);
|
||||
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
|
||||
List **extras_before, List **extras_after);
|
||||
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||
@@ -277,6 +282,14 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
||||
extras_before, extras_after);
|
||||
break;
|
||||
|
||||
case T_PrepareStmt:
|
||||
result = transformPrepareStmt(pstate, (PrepareStmt *) parseTree);
|
||||
break;
|
||||
|
||||
case T_ExecuteStmt:
|
||||
result = transformExecuteStmt(pstate, (ExecuteStmt *) parseTree);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Optimizable statements
|
||||
*/
|
||||
@@ -2454,6 +2467,131 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
|
||||
return qry;
|
||||
}
|
||||
|
||||
static Query *
|
||||
transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
|
||||
{
|
||||
Query *result = makeNode(Query);
|
||||
List *extras_before = NIL,
|
||||
*extras_after = NIL;
|
||||
List *argtype_oids = NIL; /* argtype OIDs in a list */
|
||||
Oid *argtoids = NULL; /* as an array for parser_param_set */
|
||||
int nargs;
|
||||
|
||||
result->commandType = CMD_UTILITY;
|
||||
result->utilityStmt = (Node *) stmt;
|
||||
|
||||
/* Transform list of TypeNames to list (and array) of type OIDs */
|
||||
nargs = length(stmt->argtypes);
|
||||
|
||||
if (nargs)
|
||||
{
|
||||
List *l;
|
||||
int i = 0;
|
||||
|
||||
argtoids = (Oid *) palloc(nargs * sizeof(Oid));
|
||||
|
||||
foreach (l, stmt->argtypes)
|
||||
{
|
||||
TypeName *tn = lfirst(l);
|
||||
Oid toid = typenameTypeId(tn);
|
||||
|
||||
argtype_oids = lappendi(argtype_oids, toid);
|
||||
argtoids[i++] = toid;
|
||||
}
|
||||
}
|
||||
|
||||
stmt->argtype_oids = argtype_oids;
|
||||
|
||||
/*
|
||||
* We need to adjust the parameters expected by the
|
||||
* rest of the system, so that $1, ... $n are parsed properly.
|
||||
*
|
||||
* This is somewhat of a hack; however, the main parser interface
|
||||
* only allows parameters to be specified when working with a
|
||||
* raw query string, which is not helpful here.
|
||||
*/
|
||||
parser_param_set(argtoids, nargs);
|
||||
|
||||
stmt->query = transformStmt(pstate, (Node *) stmt->query,
|
||||
&extras_before, &extras_after);
|
||||
|
||||
/* Shouldn't get any extras, since grammar only allows OptimizableStmt */
|
||||
if (extras_before || extras_after)
|
||||
elog(ERROR, "transformPrepareStmt: internal error");
|
||||
|
||||
/* Remove links to our local parameters */
|
||||
parser_param_set(NULL, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static Query *
|
||||
transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
|
||||
{
|
||||
Query *result = makeNode(Query);
|
||||
List *paramtypes;
|
||||
|
||||
result->commandType = CMD_UTILITY;
|
||||
result->utilityStmt = (Node *) stmt;
|
||||
|
||||
paramtypes = FetchQueryParams(stmt->name);
|
||||
|
||||
if (stmt->params || paramtypes)
|
||||
{
|
||||
int nparams = length(stmt->params);
|
||||
int nexpected = length(paramtypes);
|
||||
List *l;
|
||||
int i = 1;
|
||||
|
||||
if (nparams != nexpected)
|
||||
elog(ERROR, "Wrong number of parameters, expected %d but got %d",
|
||||
nexpected, nparams);
|
||||
|
||||
foreach (l, stmt->params)
|
||||
{
|
||||
Node *expr = lfirst(l);
|
||||
Oid expected_type_id,
|
||||
given_type_id;
|
||||
|
||||
expr = transformExpr(pstate, expr);
|
||||
|
||||
/* Cannot contain subselects or aggregates */
|
||||
if (contain_subplans(expr))
|
||||
elog(ERROR, "Cannot use subselects in EXECUTE parameters");
|
||||
if (contain_agg_clause(expr))
|
||||
elog(ERROR, "Cannot use aggregates in EXECUTE parameters");
|
||||
|
||||
given_type_id = exprType(expr);
|
||||
expected_type_id = (Oid) lfirsti(paramtypes);
|
||||
|
||||
if (given_type_id != expected_type_id)
|
||||
{
|
||||
expr = CoerceTargetExpr(pstate,
|
||||
expr,
|
||||
given_type_id,
|
||||
expected_type_id,
|
||||
-1,
|
||||
false);
|
||||
|
||||
if (!expr)
|
||||
elog(ERROR, "Parameter $%d of type %s cannot be coerced into the expected type %s"
|
||||
"\n\tYou will need to rewrite or cast the expression",
|
||||
i,
|
||||
format_type_be(given_type_id),
|
||||
format_type_be(expected_type_id));
|
||||
}
|
||||
|
||||
fix_opids(expr);
|
||||
lfirst(l) = expr;
|
||||
|
||||
paramtypes = lnext(paramtypes);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* exported so planner can check again after rewriting, query pullup, etc */
|
||||
void
|
||||
CheckSelectForUpdate(Query *qry)
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.360 2002/08/19 15:08:47 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.361 2002/08/27 04:55:08 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -68,8 +68,6 @@
|
||||
extern List *parsetree; /* final parse result is delivered here */
|
||||
|
||||
static bool QueryIsRule = FALSE;
|
||||
static Oid *param_type_info;
|
||||
static int pfunc_num_args;
|
||||
|
||||
/*
|
||||
* If you need access to certain yacc-generated variables and find that
|
||||
@@ -149,7 +147,8 @@ static void doNegateFloat(Value *v);
|
||||
SelectStmt, TransactionStmt, TruncateStmt,
|
||||
UnlistenStmt, UpdateStmt, VacuumStmt,
|
||||
VariableResetStmt, VariableSetStmt, VariableShowStmt,
|
||||
ViewStmt, CheckPointStmt, CreateConversionStmt
|
||||
ViewStmt, CheckPointStmt, CreateConversionStmt,
|
||||
DeallocateStmt, PrepareStmt, ExecuteStmt
|
||||
|
||||
%type <node> select_no_parens, select_with_parens, select_clause,
|
||||
simple_select
|
||||
@@ -218,7 +217,8 @@ static void doNegateFloat(Value *v);
|
||||
group_clause, TriggerFuncArgs, select_limit,
|
||||
opt_select_limit, opclass_item_list, trans_options,
|
||||
TableFuncElementList, OptTableFuncElementList,
|
||||
convert_args
|
||||
convert_args, prep_type_clause, prep_type_list,
|
||||
execute_param_clause, execute_param_list
|
||||
|
||||
%type <range> into_clause, OptTempTableName
|
||||
|
||||
@@ -335,7 +335,7 @@ static void doNegateFloat(Value *v);
|
||||
CREATEUSER, CROSS, CURRENT_DATE, CURRENT_TIME,
|
||||
CURRENT_TIMESTAMP, CURRENT_USER, CURSOR, CYCLE,
|
||||
|
||||
DATABASE, DAY_P, DEC, DECIMAL, DECLARE, DEFAULT,
|
||||
DATABASE, DAY_P, DEALLOCATE, DEC, DECIMAL, DECLARE, DEFAULT,
|
||||
DEFERRABLE, DEFERRED, DEFINER, DELETE_P, DELIMITER, DELIMITERS,
|
||||
DESC, DISTINCT, DO, DOMAIN_P, DOUBLE, DROP,
|
||||
|
||||
@@ -371,7 +371,7 @@ static void doNegateFloat(Value *v);
|
||||
ORDER, OUT_P, OUTER_P, OVERLAPS, OVERLAY, OWNER,
|
||||
|
||||
PARTIAL, PASSWORD, PATH_P, PENDANT, PLACING, POSITION,
|
||||
PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE,
|
||||
PRECISION, PREPARE, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE,
|
||||
PROCEDURAL,
|
||||
|
||||
READ, REAL, RECHECK, REFERENCES, REINDEX, RELATIVE, RENAME, REPLACE,
|
||||
@@ -490,6 +490,7 @@ stmt :
|
||||
| CreateTrigStmt
|
||||
| CreateUserStmt
|
||||
| ClusterStmt
|
||||
| DeallocateStmt
|
||||
| DefineStmt
|
||||
| DropStmt
|
||||
| TruncateStmt
|
||||
@@ -502,6 +503,7 @@ stmt :
|
||||
| DropTrigStmt
|
||||
| DropRuleStmt
|
||||
| DropUserStmt
|
||||
| ExecuteStmt
|
||||
| ExplainStmt
|
||||
| FetchStmt
|
||||
| GrantStmt
|
||||
@@ -510,6 +512,7 @@ stmt :
|
||||
| UnlistenStmt
|
||||
| LockStmt
|
||||
| NotifyStmt
|
||||
| PrepareStmt
|
||||
| ReindexStmt
|
||||
| RemoveAggrStmt
|
||||
| RemoveOperStmt
|
||||
@@ -3875,6 +3878,77 @@ ExplainStmt:
|
||||
}
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY:
|
||||
* PREPARE <plan_name> [(args, ...)] AS <query>
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
PrepareStmt: PREPARE name prep_type_clause AS OptimizableStmt
|
||||
{
|
||||
PrepareStmt *n = makeNode(PrepareStmt);
|
||||
n->name = $2;
|
||||
n->argtypes = $3;
|
||||
n->query = (Query *) $5;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
prep_type_clause: '(' prep_type_list ')' { $$ = $2; }
|
||||
| /* EMPTY */ { $$ = NIL; }
|
||||
;
|
||||
|
||||
prep_type_list: Typename { $$ = makeList1($1); }
|
||||
| prep_type_list ',' Typename
|
||||
{ $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY:
|
||||
* EXECUTE <plan_name> [(params, ...)] [INTO ...]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
ExecuteStmt: EXECUTE name execute_param_clause into_clause
|
||||
{
|
||||
ExecuteStmt *n = makeNode(ExecuteStmt);
|
||||
n->name = $2;
|
||||
n->params = $3;
|
||||
n->into = $4;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
execute_param_clause: '(' execute_param_list ')' { $$ = $2; }
|
||||
| /* EMPTY */ { $$ = NIL; }
|
||||
;
|
||||
|
||||
execute_param_list: a_expr { $$ = makeList1($1); }
|
||||
| execute_param_list ',' a_expr { $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY:
|
||||
* DEALLOCATE [PREPARE] <plan_name>
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
DeallocateStmt: DEALLOCATE name
|
||||
{
|
||||
DeallocateStmt *n = makeNode(DeallocateStmt);
|
||||
n->name = $2;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| DEALLOCATE PREPARE name
|
||||
{
|
||||
DeallocateStmt *n = makeNode(DeallocateStmt);
|
||||
n->name = $3;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
* *
|
||||
@@ -6947,6 +7021,7 @@ unreserved_keyword:
|
||||
| CYCLE
|
||||
| DATABASE
|
||||
| DAY_P
|
||||
| DEALLOCATE
|
||||
| DECLARE
|
||||
| DEFERRED
|
||||
| DEFINER
|
||||
@@ -7019,6 +7094,7 @@ unreserved_keyword:
|
||||
| PATH_P
|
||||
| PENDANT
|
||||
| PRECISION
|
||||
| PREPARE
|
||||
| PRIOR
|
||||
| PRIVILEGES
|
||||
| PROCEDURAL
|
||||
@@ -7589,26 +7665,9 @@ SystemTypeName(char *name)
|
||||
* Initialize to parse one query string
|
||||
*/
|
||||
void
|
||||
parser_init(Oid *typev, int nargs)
|
||||
parser_init(void)
|
||||
{
|
||||
QueryIsRule = FALSE;
|
||||
/*
|
||||
* Keep enough information around to fill out the type of param nodes
|
||||
* used in postquel functions
|
||||
*/
|
||||
param_type_info = typev;
|
||||
pfunc_num_args = nargs;
|
||||
}
|
||||
|
||||
/* param_type()
|
||||
* Fetch a parameter type previously passed to parser_init
|
||||
*/
|
||||
Oid
|
||||
param_type(int t)
|
||||
{
|
||||
if ((t > pfunc_num_args) || (t <= 0))
|
||||
return InvalidOid;
|
||||
return param_type_info[t - 1];
|
||||
}
|
||||
|
||||
/* exprIsNullConstant()
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.125 2002/08/18 09:36:25 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.126 2002/08/27 04:55:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -96,6 +96,7 @@ static const ScanKeyword ScanKeywords[] = {
|
||||
{"cycle", CYCLE},
|
||||
{"database", DATABASE},
|
||||
{"day", DAY_P},
|
||||
{"deallocate", DEALLOCATE},
|
||||
{"dec", DEC},
|
||||
{"decimal", DECIMAL},
|
||||
{"declare", DECLARE},
|
||||
@@ -229,6 +230,7 @@ static const ScanKeyword ScanKeywords[] = {
|
||||
{"placing", PLACING},
|
||||
{"position", POSITION},
|
||||
{"precision", PRECISION},
|
||||
{"prepare", PREPARE},
|
||||
{"primary", PRIMARY},
|
||||
{"prior", PRIOR},
|
||||
{"privileges", PRIVILEGES},
|
||||
|
@@ -14,7 +14,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.53 2002/06/20 20:29:33 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.54 2002/08/27 04:55:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -30,6 +30,9 @@
|
||||
|
||||
List *parsetree; /* result of parsing is left here */
|
||||
|
||||
static Oid *param_type_info; /* state for param_type() */
|
||||
static int param_count;
|
||||
|
||||
static int lookahead_token; /* one-token lookahead */
|
||||
static bool have_lookahead; /* lookahead_token set? */
|
||||
|
||||
@@ -50,8 +53,9 @@ parser(StringInfo str, Oid *typev, int nargs)
|
||||
have_lookahead = false;
|
||||
|
||||
scanner_init(str);
|
||||
parser_init(typev, nargs);
|
||||
parser_init();
|
||||
parse_expr_init();
|
||||
parser_param_set(typev, nargs);
|
||||
|
||||
yyresult = yyparse();
|
||||
|
||||
@@ -65,6 +69,35 @@ parser(StringInfo str, Oid *typev, int nargs)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Save information needed to fill out the type of Param references ($n)
|
||||
*
|
||||
* This is used for SQL functions, PREPARE statements, etc. It's split
|
||||
* out from parser() setup because PREPARE needs to change the info after
|
||||
* the grammar runs and before parse analysis is done on the preparable
|
||||
* query.
|
||||
*/
|
||||
void
|
||||
parser_param_set(Oid *typev, int nargs)
|
||||
{
|
||||
param_type_info = typev;
|
||||
param_count = nargs;
|
||||
}
|
||||
|
||||
/*
|
||||
* param_type()
|
||||
*
|
||||
* Fetch a parameter type previously passed to parser_param_set
|
||||
*/
|
||||
Oid
|
||||
param_type(int t)
|
||||
{
|
||||
if (t > param_count || t <= 0)
|
||||
return InvalidOid;
|
||||
return param_type_info[t - 1];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Intermediate filter between parser and base lexer (base_yylex in scan.l).
|
||||
*
|
||||
|
Reference in New Issue
Block a user