1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-15 03:41:20 +03:00

PREPARE/EXECUTE statements. Patch by Neil Conway, some kibitzing

from Tom Lane.
This commit is contained in:
Tom Lane
2002-08-27 04:55:12 +00:00
parent bc8f725a4a
commit 28e82066a1
24 changed files with 1512 additions and 55 deletions

View File

@@ -4,7 +4,7 @@
# Makefile for backend/commands
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.30 2002/07/29 22:14:10 tgl Exp $
# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.31 2002/08/27 04:55:07 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -16,7 +16,7 @@ OBJS = aggregatecmds.o analyze.o async.o cluster.o comment.o \
conversioncmds.o copy.o \
dbcommands.o define.o explain.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o proclang.o \
portalcmds.o prepare.o proclang.o \
schemacmds.o sequence.o tablecmds.o trigger.o typecmds.o user.o \
vacuum.o vacuumlazy.o variable.o view.o

View File

@@ -0,0 +1,407 @@
/*-------------------------------------------------------------------------
*
* prepare.c
* Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
*
* Copyright (c) 2002, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.1 2002/08/27 04:55:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "commands/prepare.h"
#include "executor/executor.h"
#include "utils/guc.h"
#include "optimizer/planner.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
#define HASH_KEY_LEN NAMEDATALEN
/* All the data we need to remember about a stored query */
typedef struct
{
/* dynahash.c requires key to be first field */
char key[HASH_KEY_LEN];
List *query_list; /* list of queries */
List *plan_list; /* list of plans */
List *argtype_list; /* list of parameter type OIDs */
MemoryContext context; /* context containing this query */
} QueryHashEntry;
/*
* The hash table in which prepared queries are stored. This is
* per-backend: query plans are not shared between backends.
* The keys for this hash table are the arguments to PREPARE
* and EXECUTE ("plan names"); the entries are QueryHashEntry structs.
*/
static HTAB *prepared_queries = NULL;
static void InitQueryHashTable(void);
static void StoreQuery(const char *stmt_name, List *query_list,
List *plan_list, List *argtype_list);
static QueryHashEntry *FetchQuery(const char *plan_name);
static void RunQuery(QueryDesc *qdesc, EState *state);
/*
* Implements the 'PREPARE' utility statement.
*/
void
PrepareQuery(PrepareStmt *stmt)
{
List *plan_list = NIL;
List *query_list,
*query_list_item;
if (!stmt->name)
elog(ERROR, "No statement name given");
if (stmt->query->commandType == CMD_UTILITY)
elog(ERROR, "Utility statements cannot be prepared");
/* Rewrite the query. The result could be 0, 1, or many queries. */
query_list = QueryRewrite(stmt->query);
foreach(query_list_item, query_list)
{
Query *query = (Query *) lfirst(query_list_item);
Plan *plan;
/* We can't generate plans for utility statements. */
if (query->commandType == CMD_UTILITY)
plan = NULL;
else
{
/* Call the query planner to generate a plan. */
plan = planner(query);
}
plan_list = lappend(plan_list, plan);
}
StoreQuery(stmt->name, query_list, plan_list, stmt->argtype_oids);
}
/*
* Implements the 'EXECUTE' utility statement.
*/
void
ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
{
QueryHashEntry *entry;
List *l,
*query_list,
*plan_list;
ParamListInfo paramLI = NULL;
/* Look it up in the hash table */
entry = FetchQuery(stmt->name);
/* Make working copies the executor can safely scribble on */
query_list = (List *) copyObject(entry->query_list);
plan_list = (List *) copyObject(entry->plan_list);
Assert(length(query_list) == length(plan_list));
/* Evaluate parameters, if any */
if (entry->argtype_list != NIL)
{
int nargs = length(entry->argtype_list);
int i = 0;
ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
/* Parser should have caught this error, but check */
if (nargs != length(stmt->params))
elog(ERROR, "ExecuteQuery: wrong number of arguments");
paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
MemSet(paramLI, 0, (nargs + 1) * sizeof(ParamListInfoData));
foreach (l, stmt->params)
{
Node *n = lfirst(l);
bool isNull;
paramLI[i].value = ExecEvalExprSwitchContext(n,
econtext,
&isNull,
NULL);
paramLI[i].kind = PARAM_NUM;
paramLI[i].id = i + 1;
paramLI[i].isnull = isNull;
i++;
}
paramLI[i].kind = PARAM_INVALID;
}
/* Execute each query */
foreach(l, query_list)
{
Query *query = lfirst(l);
Plan *plan = lfirst(plan_list);
bool is_last_query;
plan_list = lnext(plan_list);
is_last_query = (plan_list == NIL);
if (query->commandType == CMD_UTILITY)
ProcessUtility(query->utilityStmt, outputDest, NULL);
else
{
QueryDesc *qdesc;
EState *state;
if (Show_executor_stats)
ResetUsage();
qdesc = CreateQueryDesc(query, plan, outputDest, NULL);
state = CreateExecutorState();
state->es_param_list_info = paramLI;
if (stmt->into)
{
if (qdesc->operation != CMD_SELECT)
elog(ERROR, "INTO clause specified for non-SELECT query");
query->into = stmt->into;
qdesc->dest = None;
}
RunQuery(qdesc, state);
if (Show_executor_stats)
ShowUsage("EXECUTOR STATISTICS");
}
/*
* If we're processing multiple queries, we need to increment
* the command counter between them. For the last query,
* there's no need to do this, it's done automatically.
*/
if (! is_last_query)
CommandCounterIncrement();
}
/* No need to pfree memory, MemoryContext will be reset */
}
/*
* Initialize query hash table upon first use.
*/
static void
InitQueryHashTable(void)
{
HASHCTL hash_ctl;
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = HASH_KEY_LEN;
hash_ctl.entrysize = sizeof(QueryHashEntry);
prepared_queries = hash_create("Prepared Queries",
32,
&hash_ctl,
HASH_ELEM);
if (!prepared_queries)
elog(ERROR, "InitQueryHashTable: unable to create hash table");
}
/*
* Store all the data pertaining to a query in the hash table using
* the specified key. A copy of the data is made in a memory context belonging
* to the hash entry, so the caller can dispose of their copy.
*/
static void
StoreQuery(const char *stmt_name, List *query_list, List *plan_list,
List *argtype_list)
{
QueryHashEntry *entry;
MemoryContext oldcxt,
entrycxt;
char key[HASH_KEY_LEN];
bool found;
/* Initialize the hash table, if necessary */
if (!prepared_queries)
InitQueryHashTable();
/* Check for pre-existing entry of same name */
/* See notes in FetchQuery */
MemSet(key, 0, sizeof(key));
strncpy(key, stmt_name, sizeof(key));
hash_search(prepared_queries, key, HASH_FIND, &found);
if (found)
elog(ERROR, "Prepared statement with name \"%s\" already exists",
stmt_name);
/* Okay. Make a permanent memory context for the hashtable entry */
entrycxt = AllocSetContextCreate(TopMemoryContext,
stmt_name,
1024,
1024,
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(entrycxt);
/*
* We need to copy the data so that it is stored in the correct
* memory context. Do this before making hashtable entry, so that
* an out-of-memory failure only wastes memory and doesn't leave us
* with an incomplete (ie corrupt) hashtable entry.
*/
query_list = (List *) copyObject(query_list);
plan_list = (List *) copyObject(plan_list);
argtype_list = listCopy(argtype_list);
/* Now we can add entry to hash table */
entry = (QueryHashEntry *) hash_search(prepared_queries,
key,
HASH_ENTER,
&found);
/* Shouldn't get a failure, nor duplicate entry */
if (!entry || found)
elog(ERROR, "Unable to store prepared statement \"%s\"!",
stmt_name);
/* Fill in the hash table entry with copied data */
entry->query_list = query_list;
entry->plan_list = plan_list;
entry->argtype_list = argtype_list;
entry->context = entrycxt;
MemoryContextSwitchTo(oldcxt);
}
/*
* Lookup an existing query in the hash table.
*/
static QueryHashEntry *
FetchQuery(const char *plan_name)
{
char key[HASH_KEY_LEN];
QueryHashEntry *entry;
/*
* If the hash table hasn't been initialized, it can't be storing
* anything, therefore it couldn't possibly store our plan.
*/
if (!prepared_queries)
elog(ERROR, "Prepared statement with name \"%s\" does not exist",
plan_name);
/*
* We can't just use the statement name as supplied by the user: the
* hash package is picky enough that it needs to be NULL-padded out
* to the appropriate length to work correctly.
*/
MemSet(key, 0, sizeof(key));
strncpy(key, plan_name, sizeof(key));
entry = (QueryHashEntry *) hash_search(prepared_queries,
key,
HASH_FIND,
NULL);
if (!entry)
elog(ERROR, "Prepared statement with name \"%s\" does not exist",
plan_name);
return entry;
}
/*
* Given a plan name, look up the stored plan (giving error if not found).
* If found, return the list of argument type OIDs.
*/
List *
FetchQueryParams(const char *plan_name)
{
QueryHashEntry *entry;
entry = FetchQuery(plan_name);
return entry->argtype_list;
}
/*
* Actually execute a prepared query.
*/
static void
RunQuery(QueryDesc *qdesc, EState *state)
{
TupleDesc tupdesc;
tupdesc = ExecutorStart(qdesc, state);
ExecutorRun(qdesc, state, state->es_direction, 0L);
ExecutorEnd(qdesc, state);
}
/*
* Implements the 'DEALLOCATE' utility statement: deletes the
* specified plan from storage.
*
* The initial part of this routine is identical to FetchQuery(),
* but we repeat the coding because we need to use the key twice.
*/
void
DeallocateQuery(DeallocateStmt *stmt)
{
char key[HASH_KEY_LEN];
QueryHashEntry *entry;
/*
* If the hash table hasn't been initialized, it can't be storing
* anything, therefore it couldn't possibly store our plan.
*/
if (!prepared_queries)
elog(ERROR, "Prepared statement with name \"%s\" does not exist",
stmt->name);
/*
* We can't just use the statement name as supplied by the user: the
* hash package is picky enough that it needs to be NULL-padded out
* to the appropriate length to work correctly.
*/
MemSet(key, 0, sizeof(key));
strncpy(key, stmt->name, sizeof(key));
/*
* First lookup the entry, so we can release all the subsidiary memory
* it has allocated (when it's removed, hash_search() will return
* a dangling pointer, so it needs to be done prior to HASH_REMOVE).
* This requires an extra hash-table lookup, but DEALLOCATE
* isn't exactly a performance bottleneck.
*/
entry = (QueryHashEntry *) hash_search(prepared_queries,
key,
HASH_FIND,
NULL);
if (!entry)
elog(ERROR, "Prepared statement with name \"%s\" does not exist",
stmt->name);
/* Flush the context holding the subsidiary data */
if (MemoryContextIsValid(entry->context))
MemoryContextDelete(entry->context);
/* Now we can remove the hash table entry */
hash_search(prepared_queries, key, HASH_REMOVE, NULL);
}

View File

@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.206 2002/08/26 17:53:57 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.207 2002/08/27 04:55:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2647,6 +2647,41 @@ _copyDropCastStmt(DropCastStmt *from)
return newnode;
}
static PrepareStmt *
_copyPrepareStmt(PrepareStmt *from)
{
PrepareStmt *newnode = makeNode(PrepareStmt);
newnode->name = pstrdup(from->name);
Node_Copy(from, newnode, argtypes);
newnode->argtype_oids = listCopy(from->argtype_oids);
Node_Copy(from, newnode, query);
return newnode;
}
static ExecuteStmt *
_copyExecuteStmt(ExecuteStmt *from)
{
ExecuteStmt *newnode = makeNode(ExecuteStmt);
newnode->name = pstrdup(from->name);
Node_Copy(from, newnode, into);
Node_Copy(from, newnode, params);
return newnode;
}
static DeallocateStmt *
_copyDeallocateStmt(DeallocateStmt *from)
{
DeallocateStmt *newnode = makeNode(DeallocateStmt);
newnode->name = pstrdup(from->name);
return newnode;
}
/* ****************************************************************
* pg_list.h copy functions
@@ -3079,6 +3114,15 @@ copyObject(void *from)
case T_DropCastStmt:
retval = _copyDropCastStmt(from);
break;
case T_PrepareStmt:
retval = _copyPrepareStmt(from);
break;
case T_ExecuteStmt:
retval = _copyExecuteStmt(from);
break;
case T_DeallocateStmt:
retval = _copyDeallocateStmt(from);
break;
case T_A_Expr:
retval = _copyAExpr(from);

View File

@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.154 2002/08/26 17:53:57 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.155 2002/08/27 04:55:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1490,6 +1490,43 @@ _equalDropCastStmt(DropCastStmt *a, DropCastStmt *b)
return true;
}
static bool
_equalPrepareStmt(PrepareStmt *a, PrepareStmt *b)
{
if (!equalstr(a->name, b->name))
return false;
if (!equal(a->argtypes, b->argtypes))
return false;
if (!equali(a->argtype_oids, b->argtype_oids))
return false;
if (!equal(a->query, b->query))
return false;
return true;
}
static bool
_equalExecuteStmt(ExecuteStmt *a, ExecuteStmt *b)
{
if (!equalstr(a->name, b->name))
return false;
if (!equal(a->into, b->into))
return false;
if (!equal(a->params, b->params))
return false;
return true;
}
static bool
_equalDeallocateStmt(DeallocateStmt *a, DeallocateStmt *b)
{
if (!equalstr(a->name, b->name))
return false;
return true;
}
static bool
_equalAExpr(A_Expr *a, A_Expr *b)
{
@@ -2249,6 +2286,15 @@ equal(void *a, void *b)
case T_DropCastStmt:
retval = _equalDropCastStmt(a, b);
break;
case T_PrepareStmt:
retval = _equalPrepareStmt(a, b);
break;
case T_ExecuteStmt:
retval = _equalExecuteStmt(a, b);
break;
case T_DeallocateStmt:
retval = _equalDeallocateStmt(a, b);
break;
case T_A_Expr:
retval = _equalAExpr(a, b);

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.283 2002/08/17 15:12:07 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.284 2002/08/27 04:55:11 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -1666,7 +1666,7 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
puts("$Revision: 1.283 $ $Date: 2002/08/17 15:12:07 $\n");
puts("$Revision: 1.284 $ $Date: 2002/08/27 04:55:11 $\n");
}
/*
@@ -2406,6 +2406,18 @@ CreateCommandTag(Node *parsetree)
tag = "DROP OPERATOR CLASS";
break;
case T_PrepareStmt:
tag = "PREPARE";
break;
case T_ExecuteStmt:
tag = "EXECUTE";
break;
case T_DeallocateStmt:
tag = "DEALLOCATE";
break;
default:
elog(LOG, "CreateCommandTag: unknown parse node type %d",
nodeTag(parsetree));

View File

@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.172 2002/08/17 13:04:15 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.173 2002/08/27 04:55:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -30,6 +30,7 @@
#include "commands/explain.h"
#include "commands/lockcmds.h"
#include "commands/portalcmds.h"
#include "commands/prepare.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/sequence.h"
@@ -379,6 +380,18 @@ ProcessUtility(Node *parsetree,
}
break;
case T_PrepareStmt:
PrepareQuery((PrepareStmt *) parsetree);
break;
case T_ExecuteStmt:
ExecuteQuery((ExecuteStmt *) parsetree, dest);
break;
case T_DeallocateStmt:
DeallocateQuery((DeallocateStmt *) parsetree);
break;
/*
* schema
*/
@@ -541,11 +554,7 @@ ProcessUtility(Node *parsetree,
case T_GrantStmt:
{
GrantStmt *stmt = (GrantStmt *) parsetree;
ExecuteGrantStmt(stmt);
}
ExecuteGrantStmt((GrantStmt *) parsetree);
break;
/*
@@ -841,9 +850,7 @@ ProcessUtility(Node *parsetree,
break;
case T_CreateConversionStmt:
{
CreateConversionCommand((CreateConversionStmt *) parsetree);
}
CreateConversionCommand((CreateConversionStmt *) parsetree);
break;
case T_CreateCastStmt: