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

Extended query protocol: parse, bind, execute, describe FE/BE messages.

Only lightly tested as yet, since libpq doesn't know anything about 'em.
This commit is contained in:
Tom Lane
2003-05-05 00:44:56 +00:00
parent a59793f82c
commit 16503e6fa4
17 changed files with 1336 additions and 268 deletions

View File

@@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.67 2003/04/26 20:22:58 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.68 2003/05/05 00:44:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -48,6 +48,7 @@ typedef struct
typedef struct
{
DestReceiver pub; /* publicly-known function pointers */
bool sendDescrip; /* send RowDescription at startup? */
TupleDesc attrinfo; /* The attr info we are set up for */
int nattrs;
PrinttupAttrInfo *myinfo; /* Cached info about each attr */
@@ -58,7 +59,7 @@ typedef struct
* ----------------
*/
DestReceiver *
printtup_create_DR(bool isBinary)
printtup_create_DR(bool isBinary, bool sendDescrip)
{
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup));
@@ -66,6 +67,8 @@ printtup_create_DR(bool isBinary)
self->pub.setup = printtup_setup;
self->pub.cleanup = printtup_cleanup;
self->sendDescrip = sendDescrip;
self->attrinfo = NULL;
self->nattrs = 0;
self->myinfo = NULL;
@@ -77,6 +80,8 @@ static void
printtup_setup(DestReceiver *self, int operation,
const char *portalName, TupleDesc typeinfo)
{
DR_printtup *myState = (DR_printtup *) self;
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
{
/*
@@ -91,41 +96,11 @@ printtup_setup(DestReceiver *self, int operation,
}
/*
* if this is a retrieve, then we send back the tuple descriptor of
* the tuples.
* If this is a retrieve, and we are supposed to emit row descriptions,
* then we send back the tuple descriptor of the tuples.
*/
if (operation == CMD_SELECT)
{
Form_pg_attribute *attrs = typeinfo->attrs;
int natts = typeinfo->natts;
int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
int i;
StringInfoData buf;
pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
for (i = 0; i < natts; ++i)
{
pq_sendstring(&buf, NameStr(attrs[i]->attname));
/* column ID info appears in protocol 3.0 and up */
if (proto >= 3)
{
/* XXX not yet implemented, send zeroes */
pq_sendint(&buf, 0, 4);
pq_sendint(&buf, 0, 2);
}
pq_sendint(&buf, (int) attrs[i]->atttypid,
sizeof(attrs[i]->atttypid));
pq_sendint(&buf, attrs[i]->attlen,
sizeof(attrs[i]->attlen));
/* typmod appears in protocol 2.0 and up */
if (proto >= 2)
pq_sendint(&buf, attrs[i]->atttypmod,
sizeof(attrs[i]->atttypmod));
}
pq_endmessage(&buf);
}
if (operation == CMD_SELECT && myState->sendDescrip)
SendRowDescriptionMessage(typeinfo);
/* ----------------
* We could set up the derived attr info at this time, but we postpone it
@@ -139,6 +114,43 @@ printtup_setup(DestReceiver *self, int operation,
*/
}
/*
* SendRowDescriptionMessage --- send a RowDescription message to the frontend
*/
void
SendRowDescriptionMessage(TupleDesc typeinfo)
{
Form_pg_attribute *attrs = typeinfo->attrs;
int natts = typeinfo->natts;
int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
int i;
StringInfoData buf;
pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
for (i = 0; i < natts; ++i)
{
pq_sendstring(&buf, NameStr(attrs[i]->attname));
/* column ID info appears in protocol 3.0 and up */
if (proto >= 3)
{
/* XXX not yet implemented, send zeroes */
pq_sendint(&buf, 0, 4);
pq_sendint(&buf, 0, 2);
}
pq_sendint(&buf, (int) attrs[i]->atttypid,
sizeof(attrs[i]->atttypid));
pq_sendint(&buf, attrs[i]->attlen,
sizeof(attrs[i]->attlen));
/* typmod appears in protocol 2.0 and up */
if (proto >= 2)
pq_sendint(&buf, attrs[i]->atttypmod,
sizeof(attrs[i]->atttypmod));
}
pq_endmessage(&buf);
}
static void
printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
{

View File

@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.13 2003/05/02 20:54:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.14 2003/05/05 00:44:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,7 +49,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
* Disallow empty-string cursor name (conflicts with protocol-level
* unnamed portal).
*/
if (strlen(stmt->portalname) == 0)
if (!stmt->portalname || stmt->portalname[0] == '\0')
elog(ERROR, "Invalid cursor name: must not be empty");
/*
@@ -148,6 +148,13 @@ PerformPortalFetch(FetchStmt *stmt,
Portal portal;
long nprocessed;
/*
* Disallow empty-string cursor name (conflicts with protocol-level
* unnamed portal).
*/
if (!stmt->portalname || stmt->portalname[0] == '\0')
elog(ERROR, "Invalid cursor name: must not be empty");
/* get the portal from the portal name */
portal = GetPortalByName(stmt->portalname);
if (!PortalIsValid(portal))
@@ -164,7 +171,9 @@ PerformPortalFetch(FetchStmt *stmt,
* Adjust dest if needed. MOVE wants dest = None.
*
* If fetching from a binary cursor and the requested destination is
* Remote, change it to RemoteInternal.
* Remote, change it to RemoteInternal. Note we do NOT change if the
* destination is RemoteExecute --- so the Execute message's format
* specification wins out over the cursor's type.
*/
if (stmt->ismove)
dest = None;
@@ -189,10 +198,17 @@ PerformPortalFetch(FetchStmt *stmt,
* Close a cursor.
*/
void
PerformPortalClose(char *name)
PerformPortalClose(const char *name)
{
Portal portal;
/*
* Disallow empty-string cursor name (conflicts with protocol-level
* unnamed portal).
*/
if (!name || name[0] == '\0')
elog(ERROR, "Invalid cursor name: must not be empty");
/*
* get the portal from the portal name
*/

View File

@@ -3,10 +3,14 @@
* prepare.c
* Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
*
* Copyright (c) 2002, PostgreSQL Global Development Group
* This module also implements storage of prepared statements that are
* accessed via the extended FE/BE query protocol.
*
*
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.14 2003/05/02 20:54:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.15 2003/05/05 00:44:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,31 +29,15 @@
#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.
* The keys for this hash table are the arguments to PREPARE and EXECUTE
* (statement names); the entries are PreparedStatement 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 ParamListInfo EvaluateParams(EState *estate,
List *params, List *argtypes);
@@ -59,14 +47,36 @@ static ParamListInfo EvaluateParams(EState *estate,
void
PrepareQuery(PrepareStmt *stmt)
{
const char *commandTag;
List *query_list,
*plan_list;
if (!stmt->name)
elog(ERROR, "No statement name given");
/*
* Disallow empty-string statement name (conflicts with protocol-level
* unnamed statement).
*/
if (!stmt->name || stmt->name[0] == '\0')
elog(ERROR, "Invalid statement name: must not be empty");
if (stmt->query->commandType == CMD_UTILITY)
elog(ERROR, "Utility statements cannot be prepared");
switch (stmt->query->commandType)
{
case CMD_SELECT:
commandTag = "SELECT";
break;
case CMD_INSERT:
commandTag = "INSERT";
break;
case CMD_UPDATE:
commandTag = "UPDATE";
break;
case CMD_DELETE:
commandTag = "DELETE";
break;
default:
elog(ERROR, "Utility statements cannot be prepared");
commandTag = NULL; /* keep compiler quiet */
break;
}
/*
* Parse analysis is already done, but we must still rewrite and plan
@@ -80,7 +90,12 @@ PrepareQuery(PrepareStmt *stmt)
plan_list = pg_plan_queries(query_list, false);
/* Save the results. */
StoreQuery(stmt->name, query_list, plan_list, stmt->argtype_oids);
StorePreparedStatement(stmt->name,
NULL, /* text form not available */
commandTag,
query_list,
plan_list,
stmt->argtype_oids);
}
/*
@@ -89,7 +104,8 @@ PrepareQuery(PrepareStmt *stmt)
void
ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
{
QueryHashEntry *entry;
PreparedStatement *entry;
char *query_string;
List *query_list,
*plan_list;
MemoryContext qcontext;
@@ -98,8 +114,9 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
Portal portal;
/* Look it up in the hash table */
entry = FetchQuery(stmt->name);
entry = FetchPreparedStatement(stmt->name, true);
query_string = entry->query_string;
query_list = entry->query_list;
plan_list = entry->plan_list;
qcontext = entry->context;
@@ -135,6 +152,8 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
if (query_string)
query_string = pstrdup(query_string);
query_list = copyObject(query_list);
plan_list = copyObject(plan_list);
qcontext = PortalGetHeapMemory(portal);
@@ -150,8 +169,8 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
}
PortalDefineQuery(portal,
NULL, /* XXX fixme: can we save query text? */
NULL, /* no command tag known either */
query_string,
entry->commandTag,
query_list,
plan_list,
qcontext);
@@ -228,8 +247,8 @@ InitQueryHashTable(void)
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = HASH_KEY_LEN;
hash_ctl.entrysize = sizeof(QueryHashEntry);
hash_ctl.keysize = NAMEDATALEN;
hash_ctl.entrysize = sizeof(PreparedStatement);
prepared_queries = hash_create("Prepared Queries",
32,
@@ -244,15 +263,24 @@ InitQueryHashTable(void)
* 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.
*
* Exception: commandTag is presumed to be a pointer to a constant string,
* or possibly NULL, so it need not be copied. Note that commandTag should
* be NULL only if the original query (before rewriting) was empty.
*/
static void
StoreQuery(const char *stmt_name, List *query_list,
List *plan_list, List *argtype_list)
void
StorePreparedStatement(const char *stmt_name,
const char *query_string,
const char *commandTag,
List *query_list,
List *plan_list,
List *argtype_list)
{
QueryHashEntry *entry;
PreparedStatement *entry;
MemoryContext oldcxt,
entrycxt;
char key[HASH_KEY_LEN];
char *qstring;
char key[NAMEDATALEN];
bool found;
/* Initialize the hash table, if necessary */
@@ -260,7 +288,7 @@ StoreQuery(const char *stmt_name, List *query_list,
InitQueryHashTable();
/* Check for pre-existing entry of same name */
/* See notes in FetchQuery */
/* See notes in FetchPreparedStatement */
MemSet(key, 0, sizeof(key));
strncpy(key, stmt_name, sizeof(key));
@@ -285,15 +313,16 @@ StoreQuery(const char *stmt_name, List *query_list,
* out-of-memory failure only wastes memory and doesn't leave us with
* an incomplete (ie corrupt) hashtable entry.
*/
qstring = query_string ? pstrdup(query_string) : (char *) NULL;
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);
entry = (PreparedStatement *) hash_search(prepared_queries,
key,
HASH_ENTER,
&found);
/* Shouldn't get a failure, nor a duplicate entry */
if (!entry || found)
@@ -301,6 +330,8 @@ StoreQuery(const char *stmt_name, List *query_list,
stmt_name);
/* Fill in the hash table entry with copied data */
entry->query_string = qstring;
entry->commandTag = commandTag;
entry->query_list = query_list;
entry->plan_list = plan_list;
entry->argtype_list = argtype_list;
@@ -311,52 +342,53 @@ StoreQuery(const char *stmt_name, List *query_list,
/*
* Lookup an existing query in the hash table. If the query does not
* actually exist, an elog(ERROR) is thrown.
* actually exist, throw elog(ERROR) or return NULL per second parameter.
*/
static QueryHashEntry *
FetchQuery(const char *plan_name)
PreparedStatement *
FetchPreparedStatement(const char *stmt_name, bool throwError)
{
char key[HASH_KEY_LEN];
QueryHashEntry *entry;
char key[NAMEDATALEN];
PreparedStatement *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)
if (prepared_queries)
{
/*
* 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));
entry = (PreparedStatement *) hash_search(prepared_queries,
key,
HASH_FIND,
NULL);
}
else
entry = NULL;
if (!entry && throwError)
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);
stmt_name);
return entry;
}
/*
* Given a plan name, look up the stored plan (giving error if not found).
* Look up a prepared statement given the name (giving error if not found).
* If found, return the list of argument type OIDs.
*/
List *
FetchQueryParams(const char *plan_name)
FetchPreparedStatementParams(const char *stmt_name)
{
QueryHashEntry *entry;
PreparedStatement *entry;
entry = FetchQuery(plan_name);
entry = FetchPreparedStatement(stmt_name, true);
return entry->argtype_list;
}
@@ -368,20 +400,34 @@ FetchQueryParams(const char *plan_name)
void
DeallocateQuery(DeallocateStmt *stmt)
{
QueryHashEntry *entry;
DropPreparedStatement(stmt->name, true);
}
/* Find the query's hash table entry */
entry = FetchQuery(stmt->name);
/*
* Internal version of DEALLOCATE
*
* If showError is false, dropping a nonexistent statement is a no-op.
*/
void
DropPreparedStatement(const char *stmt_name, bool showError)
{
PreparedStatement *entry;
/* Drop any open portals that depend on this prepared statement */
Assert(MemoryContextIsValid(entry->context));
DropDependentPortals(entry->context);
/* Find the query's hash table entry; raise error if wanted */
entry = FetchPreparedStatement(stmt_name, showError);
/* Flush the context holding the subsidiary data */
MemoryContextDelete(entry->context);
if (entry)
{
/* Drop any open portals that depend on this prepared statement */
Assert(MemoryContextIsValid(entry->context));
DropDependentPortals(entry->context);
/* Now we can remove the hash table entry */
hash_search(prepared_queries, entry->key, HASH_REMOVE, NULL);
/* Flush the context holding the subsidiary data */
MemoryContextDelete(entry->context);
/* Now we can remove the hash table entry */
hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
}
}
/*
@@ -391,7 +437,7 @@ void
ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
{
ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
QueryHashEntry *entry;
PreparedStatement *entry;
List *l,
*query_list,
*plan_list;
@@ -402,7 +448,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
Assert(execstmt && IsA(execstmt, ExecuteStmt));
/* Look it up in the hash table */
entry = FetchQuery(execstmt->name);
entry = FetchPreparedStatement(execstmt->name, true);
query_list = entry->query_list;
plan_list = entry->plan_list;

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.269 2003/05/02 20:54:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.270 2003/05/05 00:44:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -83,6 +83,12 @@ typedef struct
IndexStmt *pkey; /* PRIMARY KEY index, if any */
} CreateStmtContext;
typedef struct
{
Oid *paramTypes;
int numParams;
} check_parameter_resolution_context;
static List *do_parse_analyze(Node *parseTree, ParseState *pstate);
static Query *transformStmt(ParseState *pstate, Node *stmt,
@@ -124,6 +130,8 @@ static void transformColumnType(ParseState *pstate, ColumnDef *column);
static bool relationHasPrimaryKey(Oid relationOid);
static void release_pstate_resources(ParseState *pstate);
static FromExpr *makeFromExpr(List *fromlist, Node *quals);
static bool check_parameter_resolution_walker(Node *node,
check_parameter_resolution_context *context);
/*
@@ -179,6 +187,16 @@ parse_analyze_varparams(Node *parseTree, Oid **paramTypes, int *numParams)
pfree(pstate);
/* make sure all is well with parameter types */
if (*numParams > 0)
{
check_parameter_resolution_context context;
context.paramTypes = *paramTypes;
context.numParams = *numParams;
check_parameter_resolution_walker((Node *) result, &context);
}
return result;
}
@@ -2465,7 +2483,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) stmt;
paramtypes = FetchQueryParams(stmt->name);
paramtypes = FetchPreparedStatementParams(stmt->name);
if (stmt->params || paramtypes)
{
@@ -2879,3 +2897,44 @@ analyzeCreateSchemaStmt(CreateSchemaStmt *stmt)
return result;
}
/*
* Traverse a fully-analyzed tree to verify that parameter symbols
* match their types. We need this because some Params might still
* be UNKNOWN, if there wasn't anything to force their coercion,
* and yet other instances seen later might have gotten coerced.
*/
static bool
check_parameter_resolution_walker(Node *node,
check_parameter_resolution_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Param))
{
Param *param = (Param *) node;
if (param->paramkind == PARAM_NUM)
{
int paramno = param->paramid;
if (paramno <= 0 || /* shouldn't happen, but... */
paramno > context->numParams)
elog(ERROR, "Parameter '$%d' is out of range", paramno);
if (param->paramtype != context->paramTypes[paramno-1])
elog(ERROR, "Could not determine datatype of parameter $%d",
paramno);
}
return false;
}
if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
return query_tree_walker((Query *) node,
check_parameter_resolution_walker,
(void *) context, 0);
}
return expression_tree_walker(node, check_parameter_resolution_walker,
(void *) context);
}

View File

@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.54 2003/04/26 20:22:59 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.55 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -91,10 +91,20 @@ DestToFunction(CommandDest dest)
switch (dest)
{
case Remote:
return printtup_create_DR(false);
return printtup_create_DR(false, true);
case RemoteInternal:
return printtup_create_DR(true);
return printtup_create_DR(true, true);
case RemoteExecute:
/* like Remote, but suppress output of T message */
return printtup_create_DR(false, false);
case RemoteExecuteInternal:
return printtup_create_DR(true, false);
case None:
return &donothingDR;
case Debug:
return &debugtupDR;
@@ -104,9 +114,6 @@ DestToFunction(CommandDest dest)
case Tuplestore:
return tstoreReceiverCreateDR();
case None:
return &donothingDR;
}
/* should never get here */
@@ -124,13 +131,15 @@ EndCommand(const char *commandTag, CommandDest dest)
{
case Remote:
case RemoteInternal:
case RemoteExecute:
case RemoteExecuteInternal:
pq_puttextmessage('C', commandTag);
break;
case None:
case Debug:
case Tuplestore:
case SPI:
case Tuplestore:
break;
}
}
@@ -152,8 +161,10 @@ NullCommand(CommandDest dest)
{
switch (dest)
{
case RemoteInternal:
case Remote:
case RemoteInternal:
case RemoteExecute:
case RemoteExecuteInternal:
/*
* tell the fe that we saw an empty query string. In protocols
@@ -165,10 +176,10 @@ NullCommand(CommandDest dest)
pq_puttextmessage('I', "");
break;
case Debug:
case Tuplestore:
case None:
default:
case Debug:
case SPI:
case Tuplestore:
break;
}
}
@@ -189,8 +200,10 @@ ReadyForQuery(CommandDest dest)
{
switch (dest)
{
case RemoteInternal:
case Remote:
case RemoteInternal:
case RemoteExecute:
case RemoteExecuteInternal:
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
StringInfoData buf;
@@ -205,10 +218,10 @@ ReadyForQuery(CommandDest dest)
pq_flush();
break;
case Debug:
case Tuplestore:
case None:
default:
case Debug:
case SPI:
case Tuplestore:
break;
}
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.60 2003/05/02 20:54:35 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.61 2003/05/05 00:44:56 tgl Exp $
*
* NOTES
* This cruft is the server side of PQfn.
@@ -310,6 +310,14 @@ HandleFunctionRequest(StringInfo msgBuf)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, get_func_name(fid));
/*
* Set up a query snapshot in case function needs one.
*/
SetQuerySnapshot();
/*
* Prepare function call info block.
*/
if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
nargs, fip->flinfo.fn_nargs);
@@ -359,12 +367,8 @@ HandleFunctionRequest(StringInfo msgBuf)
}
}
/*
* Set up a query snapshot in case function needs one. (It is not safe
* to do this if we are in transaction-abort state, so we have to postpone
* it till now. Ugh.)
*/
SetQuerySnapshot();
/* Verify we reached the end of the message where expected. */
pq_getmsgend(msgBuf);
#ifdef NO_FASTPATH
/* force a NULL return */

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.56 2003/05/02 20:54:35 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.57 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -222,11 +222,12 @@ CreateNewPortal(void)
* PortalDefineQuery
* A simple subroutine to establish a portal's query.
*
* Notes: the passed commandTag must be a pointer to a constant string,
* since it is not copied. The caller is responsible for ensuring that
* the passed sourceText (if any), parse and plan trees have adequate
* lifetime. Also, queryContext must accurately describe the location
* of the parse and plan trees.
* Notes: commandTag shall be NULL if and only if the original query string
* (before rewriting) was an empty string. Also, the passed commandTag must
* be a pointer to a constant string, since it is not copied. The caller is
* responsible for ensuring that the passed sourceText (if any), parse and
* plan trees have adequate lifetime. Also, queryContext must accurately
* describe the location of the parse and plan trees.
*/
void
PortalDefineQuery(Portal portal,
@@ -241,6 +242,8 @@ PortalDefineQuery(Portal portal,
Assert(length(parseTrees) == length(planTrees));
Assert(commandTag != NULL || parseTrees == NIL);
portal->sourceText = sourceText;
portal->commandTag = commandTag;
portal->parseTrees = parseTrees;