diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/Makefile | 4 | ||||
-rw-r--r-- | src/backend/commands/prepare.c | 407 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 46 | ||||
-rw-r--r-- | src/backend/nodes/equalfuncs.c | 48 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 140 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 109 | ||||
-rw-r--r-- | src/backend/parser/keywords.c | 4 | ||||
-rw-r--r-- | src/backend/parser/parser.c | 37 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 16 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 25 |
10 files changed, 792 insertions, 44 deletions
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index 92961049d77..db91bf1d0e0 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -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 diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c new file mode 100644 index 00000000000..9dbe2146162 --- /dev/null +++ b/src/backend/commands/prepare.c @@ -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); +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 4de9ba5b8de..b3920e38b2d 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c96389f1e8b..408a94ff1b3 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -1491,6 +1491,43 @@ _equalDropCastStmt(DropCastStmt *a, DropCastStmt *b) } 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) { if (a->oper != b->oper) @@ -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); diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 01e6d867ce4..ffa371d9260 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 80aa372f9c5..0f512c7d31b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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() diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 2bb6772054a..9a3064ad661 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -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}, diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index f4cd24e0c4f..8c129cb9161 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -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(); @@ -66,6 +70,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). * * The filter is needed because in some cases SQL92 requires more than one diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 605f44ba70b..28576b8fad6 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -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)); diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 1ae0a89fd6b..b16adef54db 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -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: |