aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/common/printtup.c84
-rw-r--r--src/backend/commands/portalcmds.c24
-rw-r--r--src/backend/commands/prepare.c204
-rw-r--r--src/backend/parser/analyze.c63
-rw-r--r--src/backend/tcop/dest.c39
-rw-r--r--src/backend/tcop/fastpath.c18
-rw-r--r--src/backend/tcop/postgres.c959
-rw-r--r--src/backend/utils/mmgr/portalmem.c15
-rw-r--r--src/include/access/printtup.h6
-rw-r--r--src/include/commands/portalcmds.h4
-rw-r--r--src/include/commands/prepare.h42
-rw-r--r--src/include/libpq/pqcomm.h4
-rw-r--r--src/include/tcop/dest.h10
-rw-r--r--src/include/tcop/tcopprot.h7
-rw-r--r--src/interfaces/libpq/fe-connect.c13
-rw-r--r--src/interfaces/libpq/libpq-int.h4
16 files changed, 1255 insertions, 241 deletions
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index 160b703223f..584233c1873 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -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)
{
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 35ed8a270b5..82058ff5d10 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -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,11 +198,18 @@ 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
*/
portal = GetPortalByName(name);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 5a3e3f589d1..3f8beac53c1 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -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)
- 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));
+ 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 = (QueryHashEntry *) hash_search(prepared_queries,
- key,
- HASH_FIND,
- NULL);
+ entry = (PreparedStatement *) hash_search(prepared_queries,
+ key,
+ HASH_FIND,
+ NULL);
+ }
+ else
+ entry = NULL;
- if (!entry)
+ if (!entry && throwError)
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);
+}
+
+/*
+ * 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;
- /* Find the query's hash table entry */
- entry = FetchQuery(stmt->name);
+ /* Find the query's hash table entry; raise error if wanted */
+ entry = FetchPreparedStatement(stmt_name, showError);
- /* Drop any open portals that depend on this prepared statement */
- Assert(MemoryContextIsValid(entry->context));
- DropDependentPortals(entry->context);
+ if (entry)
+ {
+ /* Drop any open portals that depend on this prepared statement */
+ Assert(MemoryContextIsValid(entry->context));
+ DropDependentPortals(entry->context);
- /* Flush the context holding the subsidiary data */
- MemoryContextDelete(entry->context);
+ /* Flush the context holding the subsidiary data */
+ MemoryContextDelete(entry->context);
- /* Now we can remove the hash table entry */
- hash_search(prepared_queries, entry->key, HASH_REMOVE, NULL);
+ /* 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;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a488d1d91e5..ad2d5ab5681 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.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);
+}
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index 41906a348a4..a5905dedc7f 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -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 None:
case Debug:
+ case SPI:
case Tuplestore:
- case None:
- default:
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 None:
case Debug:
+ case SPI:
case Tuplestore:
- case None:
- default:
break;
}
}
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 78fcfdb7e0e..65161c54ff3 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -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 */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index b60898270a6..d57ccd973b2 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.331 2003/05/03 05:13:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.332 2003/05/05 00:44:56 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -33,8 +33,11 @@
#include <getopt.h>
#endif
+#include "access/printtup.h"
#include "access/xlog.h"
+#include "catalog/pg_type.h"
#include "commands/async.h"
+#include "commands/prepare.h"
#include "commands/trigger.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
@@ -54,6 +57,7 @@
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
#include "utils/guc.h"
+#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "mb/pg_wchar.h"
@@ -82,7 +86,15 @@ bool InError = false;
extern bool autocommit;
-static bool EchoQuery = false; /* default don't echo */
+/*
+ * Flags for expensive function optimization -- JMH 3/9/92
+ */
+int XfuncMode = 0;
+
+/* ----------------
+ * private variables
+ * ----------------
+ */
/*
* Flag to mark SIGHUP. Whenever the main loop comes around it
@@ -91,23 +103,41 @@ static bool EchoQuery = false; /* default don't echo */
*/
static volatile bool got_SIGHUP = false;
-/* ----------------
- * people who want to use EOF should #define DONTUSENEWLINE in
- * tcop/tcopdebug.h
- * ----------------
+/*
+ * Flag to keep track of whether we have started a transaction.
+ * For extended query protocol this has to be remembered across messages.
+ */
+static bool xact_started = false;
+
+/*
+ * Flags to implement skip-till-Sync-after-error behavior for messages of
+ * the extended query protocol.
+ */
+static bool doing_extended_query_message = false;
+static bool ignore_till_sync = false;
+
+/*
+ * If an unnamed prepared statement exists, it's stored here.
+ * We keep it separate from the hashtable kept by commands/prepare.c
+ * in order to reduce overhead for short-lived queries.
+ */
+static MemoryContext unnamed_stmt_context = NULL;
+static PreparedStatement *unnamed_stmt_pstmt = NULL;
+
+
+static bool EchoQuery = false; /* default don't echo */
+
+/*
+ * people who want to use EOF should #define DONTUSENEWLINE in
+ * tcop/tcopdebug.h
*/
#ifndef TCOP_DONTUSENEWLINE
-int UseNewLine = 1; /* Use newlines query delimiters (the
+static int UseNewLine = 1; /* Use newlines query delimiters (the
* default) */
-
#else
-int UseNewLine = 0; /* Use EOF as query delimiters */
+static int UseNewLine = 0; /* Use EOF as query delimiters */
#endif /* TCOP_DONTUSENEWLINE */
-/*
-** Flags for expensive function optimization -- JMH 3/9/92
-*/
-int XfuncMode = 0;
/* ----------------------------------------------------------------
* decls for routines only used in this file
@@ -254,10 +284,14 @@ SocketBackend(StringInfo inBuf)
* Validate message type code before trying to read body; if we have
* lost sync, better to say "command unknown" than to run out of memory
* because we used garbage as a length word.
+ *
+ * This also gives us a place to set the doing_extended_query_message
+ * flag as soon as possible.
*/
switch (qtype)
{
case 'Q': /* simple query */
+ doing_extended_query_message = false;
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
{
/* old style without length word; convert */
@@ -270,15 +304,43 @@ SocketBackend(StringInfo inBuf)
break;
case 'F': /* fastpath function call */
+ /* we let fastpath.c cope with old-style input of this */
+ doing_extended_query_message = false;
break;
case 'X': /* terminate */
+ doing_extended_query_message = false;
+ break;
+
+ case 'B': /* bind */
+ case 'C': /* close */
+ case 'D': /* describe */
+ case 'E': /* execute */
+ case 'H': /* flush */
+ case 'P': /* parse */
+ doing_extended_query_message = true;
+ /* these are only legal in protocol 3 */
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ elog(FATAL, "Socket command type %c unknown", qtype);
+ break;
+
+ case 'S': /* sync */
+ /* stop any active skip-till-Sync */
+ ignore_till_sync = false;
+ /* mark not-extended, so that a new error doesn't begin skip */
+ doing_extended_query_message = false;
+ /* only legal in protocol 3 */
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ elog(FATAL, "Socket command type %c unknown", qtype);
break;
case 'd': /* copy data */
case 'c': /* copy done */
case 'f': /* copy fail */
- /* Accept but ignore these messages, per protocol spec */
+ doing_extended_query_message = false;
+ /* these are only legal in protocol 3 */
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ elog(FATAL, "Socket command type %c unknown", qtype);
break;
default:
@@ -410,9 +472,6 @@ List *
pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams)
{
List *querytree_list;
- List *list_item;
- Query *querytree;
- List *new_list;
/*
* (1) Perform parse analysis.
@@ -423,21 +482,35 @@ pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams)
querytree_list = parse_analyze(parsetree, paramTypes, numParams);
if (log_parser_stats)
- {
ShowUsage("PARSE ANALYSIS STATISTICS");
- ResetUsage();
- }
/*
* (2) Rewrite the queries, as necessary
- *
+ */
+ querytree_list = pg_rewrite_queries(querytree_list);
+
+ return querytree_list;
+}
+
+/*
+ * Perform rewriting of a list of queries produced by parse analysis.
+ */
+List *
+pg_rewrite_queries(List *querytree_list)
+{
+ List *new_list = NIL;
+ List *list_item;
+
+ if (log_parser_stats)
+ ResetUsage();
+
+ /*
* rewritten queries are collected in new_list. Note there may be more
* or fewer than in the original list.
*/
- new_list = NIL;
foreach(list_item, querytree_list)
{
- querytree = (Query *) lfirst(list_item);
+ Query *querytree = (Query *) lfirst(list_item);
if (Debug_print_parse)
elog_node_display(LOG, "parse tree", querytree,
@@ -471,7 +544,7 @@ pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams)
new_list = (List *) copyObject(querytree_list);
/* This checks both copyObject() and the equal() routines... */
if (!equal(new_list, querytree_list))
- elog(WARNING, "pg_analyze_and_rewrite: copyObject failed on parse tree");
+ elog(WARNING, "pg_rewrite_queries: copyObject failed on parse tree");
else
querytree_list = new_list;
#endif
@@ -576,15 +649,13 @@ pg_plan_queries(List *querytrees, bool needSnapshot)
/*
- * exec_simple_query()
+ * exec_simple_query
*
* Execute a "simple Query" protocol message.
*/
static void
-exec_simple_query(const char *query_string, /* string to execute */
- CommandDest dest) /* where results should go */
+exec_simple_query(const char *query_string)
{
- bool xact_started;
MemoryContext oldcontext;
List *parsetree_list,
*parsetree_item;
@@ -619,13 +690,28 @@ exec_simple_query(const char *query_string, /* string to execute */
* that this will normally change current memory context.)
*/
start_xact_command();
- xact_started = true;
+
+ /*
+ * Zap any pre-existing unnamed statement. (While not strictly
+ * necessary, it seems best to define simple-Query mode as if it
+ * used the unnamed statement and portal; this ensures we recover
+ * any storage used by prior unnamed operations.)
+ */
+ unnamed_stmt_pstmt = NULL;
+ if (unnamed_stmt_context)
+ {
+ DropDependentPortals(unnamed_stmt_context);
+ MemoryContextDelete(unnamed_stmt_context);
+ }
+ unnamed_stmt_context = NULL;
/*
* Switch to appropriate context for constructing parsetrees.
*/
oldcontext = MemoryContextSwitchTo(MessageContext);
+ QueryContext = CurrentMemoryContext;
+
/*
* Do basic parsing of the query or queries (this should be safe even
* if we are in aborted transaction state!)
@@ -659,7 +745,7 @@ exec_simple_query(const char *query_string, /* string to execute */
set_ps_display(commandTag);
- BeginCommand(commandTag, dest);
+ BeginCommand(commandTag, whereToSendOutput);
/*
* If we are in an aborted transaction, reject all commands except
@@ -688,11 +774,7 @@ exec_simple_query(const char *query_string, /* string to execute */
}
/* Make sure we are in a transaction command */
- if (!xact_started)
- {
- start_xact_command();
- xact_started = true;
- }
+ start_xact_command();
/* If we got a cancel signal in parsing or prior command, quit */
CHECK_FOR_INTERRUPTS();
@@ -735,37 +817,40 @@ exec_simple_query(const char *query_string, /* string to execute */
*/
PortalStart(portal, NULL);
- (void) PortalRun(portal, FETCH_ALL, dest, dest, completionTag);
+ (void) PortalRun(portal,
+ FETCH_ALL,
+ whereToSendOutput,
+ whereToSendOutput,
+ completionTag);
PortalDrop(portal, false);
- /*
- * If this was a transaction control statement or a variable
- * set/show/reset statement, commit it and arrange to start a
- * new xact command for the next command (if any).
- */
+
if (IsA(parsetree, TransactionStmt) ||
IsA(parsetree, VariableSetStmt) ||
IsA(parsetree, VariableShowStmt) ||
IsA(parsetree, VariableResetStmt))
{
+ /*
+ * If this was a transaction control statement or a variable
+ * set/show/reset statement, commit it. We will start a
+ * new xact command for the next command (if any).
+ */
finish_xact_command(true);
- xact_started = false;
}
- /*
- * If this is the last parsetree of the query string, close down
- * transaction statement before reporting command-complete. This
- * is so that any end-of-transaction errors are reported before
- * the command-complete message is issued, to avoid confusing
- * clients who will expect either a command-complete message or an
- * error, not one and then the other. But for compatibility with
- * historical Postgres behavior, we do not force a transaction
- * boundary between queries appearing in a single query string.
- */
else if (lnext(parsetree_item) == NIL || !autocommit)
{
+ /*
+ * If this is the last parsetree of the query string, close down
+ * transaction statement before reporting command-complete. This
+ * is so that any end-of-transaction errors are reported before
+ * the command-complete message is issued, to avoid confusing
+ * clients who will expect either a command-complete message or an
+ * error, not one and then the other. But for compatibility with
+ * historical Postgres behavior, we do not force a transaction
+ * boundary between queries appearing in a single query string.
+ */
finish_xact_command(false);
- xact_started = false;
}
else
{
@@ -783,20 +868,21 @@ exec_simple_query(const char *query_string, /* string to execute */
* (But a command aborted by error will not send an EndCommand
* report at all.)
*/
- EndCommand(completionTag, dest);
+ EndCommand(completionTag, whereToSendOutput);
} /* end loop over parsetrees */
/*
* If there were no parsetrees, return EmptyQueryResponse message.
*/
if (!parsetree_list)
- NullCommand(dest);
+ NullCommand(whereToSendOutput);
+
+ QueryContext = NULL;
/*
* Close down transaction statement, if one is open.
*/
- if (xact_started)
- finish_xact_command(false);
+ finish_xact_command(false);
/*
* Finish up monitoring.
@@ -821,38 +907,608 @@ exec_simple_query(const char *query_string, /* string to execute */
}
/*
+ * exec_parse_message
+ *
+ * Execute a "Parse" protocol message.
+ */
+static void
+exec_parse_message(const char *query_string, /* string to execute */
+ const char *stmt_name, /* name for prepared stmt */
+ Oid *paramTypes, /* parameter types */
+ int numParams) /* number of parameters */
+{
+ MemoryContext oldcontext;
+ List *parsetree_list;
+ const char *commandTag;
+ List *querytree_list,
+ *plantree_list,
+ *param_list;
+ bool is_named;
+ bool save_log_statement_stats = log_statement_stats;
+
+ /*
+ * Report query to various monitoring facilities.
+ */
+ debug_query_string = query_string;
+
+ pgstat_report_activity(query_string);
+
+ set_ps_display("PARSE");
+
+ if (save_log_statement_stats)
+ ResetUsage();
+
+ /*
+ * Start up a transaction command so we can run parse analysis etc.
+ * (Note that this will normally change current memory context.)
+ * Nothing happens if we are already in one.
+ */
+ start_xact_command();
+
+ /*
+ * Switch to appropriate context for constructing parsetrees.
+ *
+ * We have two strategies depending on whether the prepared statement
+ * is named or not. For a named prepared statement, we do parsing
+ * in MessageContext and copy the finished trees into the prepared
+ * statement's private context; then the reset of MessageContext releases
+ * temporary space used by parsing and planning. For an unnamed prepared
+ * statement, we assume the statement isn't going to hang around long,
+ * so getting rid of temp space quickly is probably not worth the costs
+ * of copying parse/plan trees. So in this case, we set up a special
+ * context for the unnamed statement, and do all the parsing/planning
+ * therein.
+ */
+ is_named = (stmt_name[0] != '\0');
+ if (is_named)
+ {
+ /* Named prepared statement --- parse in MessageContext */
+ oldcontext = MemoryContextSwitchTo(MessageContext);
+ }
+ else
+ {
+ /* Unnamed prepared statement --- release any prior unnamed stmt */
+ unnamed_stmt_pstmt = NULL;
+ if (unnamed_stmt_context)
+ {
+ DropDependentPortals(unnamed_stmt_context);
+ MemoryContextDelete(unnamed_stmt_context);
+ }
+ unnamed_stmt_context = NULL;
+ /* create context for parsing/planning */
+ unnamed_stmt_context =
+ AllocSetContextCreate(TopMemoryContext,
+ "unnamed prepared statement",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcontext = MemoryContextSwitchTo(unnamed_stmt_context);
+ }
+
+ QueryContext = CurrentMemoryContext;
+
+ /*
+ * Do basic parsing of the query or queries (this should be safe even
+ * if we are in aborted transaction state!)
+ */
+ parsetree_list = pg_parse_query(query_string);
+
+ /*
+ * We only allow a single user statement in a prepared statement.
+ * This is mainly to keep the protocol simple --- otherwise we'd need
+ * to worry about multiple result tupdescs and things like that.
+ */
+ if (length(parsetree_list) > 1)
+ elog(ERROR, "Cannot insert multiple commands into a prepared statement");
+
+ if (parsetree_list != NIL)
+ {
+ Node *parsetree = (Node *) lfirst(parsetree_list);
+ int i;
+
+ /*
+ * Get the command name for possible use in status display.
+ */
+ commandTag = CreateCommandTag(parsetree);
+
+ /*
+ * If we are in an aborted transaction, reject all commands except
+ * COMMIT/ROLLBACK. It is important that this test occur before we
+ * try to do parse analysis, rewrite, or planning, since all those
+ * phases try to do database accesses, which may fail in abort
+ * state. (It might be safe to allow some additional utility
+ * commands in this state, but not many...)
+ */
+ if (IsAbortedTransactionBlockState())
+ {
+ bool allowit = false;
+
+ if (IsA(parsetree, TransactionStmt))
+ {
+ TransactionStmt *stmt = (TransactionStmt *) parsetree;
+
+ if (stmt->kind == TRANS_STMT_COMMIT ||
+ stmt->kind == TRANS_STMT_ROLLBACK)
+ allowit = true;
+ }
+
+ if (!allowit)
+ elog(ERROR, "current transaction is aborted, "
+ "queries ignored until end of transaction block");
+ }
+
+ /*
+ * OK to analyze, rewrite, and plan this query. Note that the
+ * originally specified parameter set is not required to be
+ * complete, so we have to use parse_analyze_varparams().
+ */
+ if (log_parser_stats)
+ ResetUsage();
+
+ querytree_list = parse_analyze_varparams(parsetree,
+ &paramTypes,
+ &numParams);
+
+ /*
+ * Check all parameter types got determined, and convert array
+ * representation to a list for storage.
+ */
+ param_list = NIL;
+ for (i = 0; i < numParams; i++)
+ {
+ Oid ptype = paramTypes[i];
+
+ if (ptype == InvalidOid || ptype == UNKNOWNOID)
+ elog(ERROR, "Could not determine datatype of parameter $%d",
+ i + 1);
+ param_list = lappendo(param_list, ptype);
+ }
+
+ if (log_parser_stats)
+ ShowUsage("PARSE ANALYSIS STATISTICS");
+
+ querytree_list = pg_rewrite_queries(querytree_list);
+
+ plantree_list = pg_plan_queries(querytree_list, true);
+ }
+ else
+ {
+ /* Empty input string. This is legal. */
+ commandTag = NULL;
+ querytree_list = NIL;
+ plantree_list = NIL;
+ param_list = NIL;
+ }
+
+ /* If we got a cancel signal in analysis or planning, quit */
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Store the query as a prepared statement. See above comments.
+ */
+ if (is_named)
+ {
+ StorePreparedStatement(stmt_name,
+ query_string,
+ commandTag,
+ querytree_list,
+ plantree_list,
+ param_list);
+ }
+ else
+ {
+ PreparedStatement *pstmt;
+
+ pstmt = (PreparedStatement *) palloc0(sizeof(PreparedStatement));
+ /* query_string needs to be copied into unnamed_stmt_context */
+ pstmt->query_string = pstrdup(query_string);
+ /* the rest is there already */
+ pstmt->commandTag = commandTag;
+ pstmt->query_list = querytree_list;
+ pstmt->plan_list = plantree_list;
+ pstmt->argtype_list = param_list;
+ pstmt->context = unnamed_stmt_context;
+ /* Now the unnamed statement is complete and valid */
+ unnamed_stmt_pstmt = pstmt;
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ QueryContext = NULL;
+
+ /*
+ * We do NOT close the open transaction command here; that only happens
+ * when the client sends Sync. Instead, do CommandCounterIncrement just
+ * in case something happened during parse/plan.
+ */
+ CommandCounterIncrement();
+
+ /*
+ * Send ParseComplete.
+ */
+ if (whereToSendOutput == Remote)
+ pq_putemptymessage('1');
+
+ if (save_log_statement_stats)
+ ShowUsage("PARSE MESSAGE STATISTICS");
+
+ debug_query_string = NULL;
+}
+
+/*
+ * exec_bind_message
+ *
+ * Process a "Bind" message to create a portal from a prepared statement
+ */
+static void
+exec_bind_message(StringInfo input_message)
+{
+ const char *portal_name;
+ const char *stmt_name;
+ int is_binary;
+ int numParams;
+ PreparedStatement *pstmt;
+ Portal portal;
+ ParamListInfo params;
+
+ pgstat_report_activity("<BIND>");
+
+ set_ps_display("BIND");
+
+ /*
+ * Start up a transaction command so we can call functions etc.
+ * (Note that this will normally change current memory context.)
+ * Nothing happens if we are already in one.
+ */
+ start_xact_command();
+
+ /* Get the fixed part of the message */
+ portal_name = pq_getmsgstring(input_message);
+ stmt_name = pq_getmsgstring(input_message);
+ is_binary = pq_getmsgbyte(input_message);
+ numParams = pq_getmsgint(input_message, 4);
+
+ if (is_binary)
+ elog(ERROR, "Binary BIND not implemented yet");
+
+ /* Find prepared statement */
+ if (stmt_name[0] != '\0')
+ pstmt = FetchPreparedStatement(stmt_name, true);
+ else
+ {
+ /* special-case the unnamed statement */
+ pstmt = unnamed_stmt_pstmt;
+ if (!pstmt)
+ elog(ERROR, "Unnamed prepared statement does not exist");
+ }
+
+ if (numParams != length(pstmt->argtype_list))
+ elog(ERROR, "Bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
+ numParams, stmt_name, length(pstmt->argtype_list));
+
+ /*
+ * Create the portal. Allow silent replacement of an existing portal
+ * only if the unnamed portal is specified.
+ */
+ if (portal_name[0] == '\0')
+ portal = CreatePortal(portal_name, true, true);
+ else
+ portal = CreatePortal(portal_name, false, false);
+
+ PortalDefineQuery(portal,
+ pstmt->query_string,
+ pstmt->commandTag,
+ pstmt->query_list,
+ pstmt->plan_list,
+ pstmt->context);
+
+ /*
+ * Fetch parameters, if any, and store in the portal's memory context.
+ *
+ * In an aborted transaction, we can't risk calling user-defined functions,
+ * so bind all parameters to null values.
+ */
+ if (numParams > 0)
+ {
+ bool isaborted = IsAbortedTransactionBlockState();
+ int i = 0;
+ List *l;
+ MemoryContext oldContext;
+
+ oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+
+ params = (ParamListInfo)
+ palloc0((numParams + 1) * sizeof(ParamListInfoData));
+
+ foreach(l, pstmt->argtype_list)
+ {
+ Oid ptype = lfirsto(l);
+ bool isNull;
+
+ isNull = (pq_getmsgbyte(input_message) != 0) ? false : true;
+ if (!isNull)
+ {
+ const char *ptext = pq_getmsgstring(input_message);
+
+ if (isaborted)
+ isNull = true;
+ else
+ {
+ Oid typInput;
+ Oid typElem;
+
+ getTypeInputInfo(ptype, &typInput, &typElem);
+ params[i].value =
+ OidFunctionCall3(typInput,
+ CStringGetDatum(ptext),
+ ObjectIdGetDatum(typElem),
+ Int32GetDatum(-1));
+ }
+ }
+ params[i].kind = PARAM_NUM;
+ params[i].id = i + 1;
+ params[i].isnull = isNull;
+
+ i++;
+ }
+
+ params[i].kind = PARAM_INVALID;
+
+ MemoryContextSwitchTo(oldContext);
+ }
+ else
+ params = NULL;
+
+ pq_getmsgend(input_message);
+
+ /*
+ * Start portal execution.
+ */
+ PortalStart(portal, params);
+
+ /*
+ * Send BindComplete.
+ */
+ if (whereToSendOutput == Remote)
+ pq_putemptymessage('2');
+}
+
+/*
+ * exec_execute_message
+ *
+ * Process an "Execute" message for a portal
+ */
+static void
+exec_execute_message(const char *portal_name, int is_binary, long max_rows)
+{
+ CommandDest dest;
+ Portal portal;
+ bool is_trans_stmt = false;
+ bool is_trans_exit = false;
+ bool completed;
+ char completionTag[COMPLETION_TAG_BUFSIZE];
+
+ /* Adjust destination to tell printtup.c what to do */
+ dest = whereToSendOutput;
+ if (dest == Remote)
+ dest = is_binary ? RemoteExecuteInternal : RemoteExecute;
+
+ portal = GetPortalByName(portal_name);
+ if (!PortalIsValid(portal))
+ elog(ERROR, "Portal \"%s\" not found", portal_name);
+
+ /*
+ * If the original query was a null string, just return EmptyQueryResponse.
+ */
+ if (portal->commandTag == NULL)
+ {
+ Assert(portal->parseTrees == NIL);
+ NullCommand(dest);
+ return;
+ }
+
+ if (portal->sourceText)
+ {
+ debug_query_string = portal->sourceText;
+ pgstat_report_activity(portal->sourceText);
+ }
+ else
+ {
+ debug_query_string = "execute message";
+ pgstat_report_activity("<EXECUTE>");
+ }
+
+ set_ps_display(portal->commandTag);
+
+ BeginCommand(portal->commandTag, dest);
+
+ /* Check for transaction-control commands */
+ if (length(portal->parseTrees) == 1)
+ {
+ Query *query = (Query *) lfirst(portal->parseTrees);
+
+ if (query->commandType == CMD_UTILITY &&
+ query->utilityStmt != NULL &&
+ IsA(query->utilityStmt, TransactionStmt))
+ {
+ TransactionStmt *stmt = (TransactionStmt *) query->utilityStmt;
+
+ is_trans_stmt = true;
+ if (stmt->kind == TRANS_STMT_COMMIT ||
+ stmt->kind == TRANS_STMT_ROLLBACK)
+ is_trans_exit = true;
+ }
+ }
+
+ /*
+ * Ensure we are in a transaction command (this should normally be
+ * the case already due to prior BIND).
+ */
+ start_xact_command();
+
+ /*
+ * If we are in aborted transaction state, the only portals we can
+ * actually run are those containing COMMIT or ROLLBACK commands.
+ */
+ if (IsAbortedTransactionBlockState())
+ {
+ if (!is_trans_exit)
+ elog(ERROR, "current transaction is aborted, "
+ "queries ignored until end of transaction block");
+ }
+
+ /* Check for cancel signal before we start execution */
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Okay to run the portal.
+ */
+ if (max_rows <= 0)
+ max_rows = FETCH_ALL;
+
+ completed = PortalRun(portal,
+ max_rows,
+ dest,
+ dest,
+ completionTag);
+
+ if (completed)
+ {
+ if (is_trans_stmt)
+ {
+ /*
+ * If this was a transaction control statement, commit it. We will
+ * start a new xact command for the next command (if any).
+ */
+ finish_xact_command(true);
+ }
+ else
+ {
+ /*
+ * We need a CommandCounterIncrement after every query,
+ * except those that start or end a transaction block.
+ */
+ CommandCounterIncrement();
+ }
+
+ /* Send appropriate CommandComplete to client */
+ EndCommand(completionTag, dest);
+ }
+ else
+ {
+ /* Portal run not complete, so send PortalSuspended */
+ if (whereToSendOutput == Remote)
+ pq_putemptymessage('s');
+ }
+
+ debug_query_string = NULL;
+}
+
+/*
+ * exec_describe_statement_message
+ *
+ * Process a "Describe" message for a prepared statement
+ */
+static void
+exec_describe_statement_message(const char *stmt_name)
+{
+ PreparedStatement *pstmt;
+ List *l;
+ StringInfoData buf;
+
+ /* Find prepared statement */
+ if (stmt_name[0] != '\0')
+ pstmt = FetchPreparedStatement(stmt_name, true);
+ else
+ {
+ /* special-case the unnamed statement */
+ pstmt = unnamed_stmt_pstmt;
+ if (!pstmt)
+ elog(ERROR, "Unnamed prepared statement does not exist");
+ }
+
+ if (whereToSendOutput != Remote)
+ return; /* can't actually do anything... */
+
+ pq_beginmessage(&buf, 't'); /* parameter description message type */
+ pq_sendint(&buf, length(pstmt->argtype_list), 4);
+
+ foreach(l, pstmt->argtype_list)
+ {
+ Oid ptype = lfirsto(l);
+
+ pq_sendint(&buf, (int) ptype, 4);
+ }
+ pq_endmessage(&buf);
+}
+
+/*
+ * exec_describe_portal_message
+ *
+ * Process a "Describe" message for a portal
+ */
+static void
+exec_describe_portal_message(const char *portal_name)
+{
+ Portal portal;
+
+ portal = GetPortalByName(portal_name);
+ if (!PortalIsValid(portal))
+ elog(ERROR, "Portal \"%s\" not found", portal_name);
+
+ if (whereToSendOutput != Remote)
+ return; /* can't actually do anything... */
+
+ if (portal->tupDesc)
+ SendRowDescriptionMessage(portal->tupDesc);
+ else
+ pq_putemptymessage('n'); /* NoData */
+}
+
+
+/*
* Convenience routines for starting/committing a single command.
*/
static void
start_xact_command(void)
{
- elog(DEBUG1, "StartTransactionCommand");
- StartTransactionCommand(false);
+ if (!xact_started)
+ {
+ elog(DEBUG2, "StartTransactionCommand");
+ StartTransactionCommand(false);
+
+ /* Set statement timeout running, if any */
+ if (StatementTimeout > 0)
+ enable_sig_alarm(StatementTimeout, true);
- /* Set statement timeout running, if any */
- if (StatementTimeout > 0)
- enable_sig_alarm(StatementTimeout, true);
+ xact_started = true;
+ }
}
static void
finish_xact_command(bool forceCommit)
{
- /* Invoke IMMEDIATE constraint triggers */
- DeferredTriggerEndQuery();
+ if (xact_started)
+ {
+ /* Invoke IMMEDIATE constraint triggers */
+ DeferredTriggerEndQuery();
- /* Cancel any active statement timeout before committing */
- disable_sig_alarm(true);
+ /* Cancel any active statement timeout before committing */
+ disable_sig_alarm(true);
- /* Now commit the command */
- elog(DEBUG1, "CommitTransactionCommand");
+ /* Now commit the command */
+ elog(DEBUG2, "CommitTransactionCommand");
- CommitTransactionCommand(forceCommit);
+ CommitTransactionCommand(forceCommit);
#ifdef SHOW_MEMORY_STATS
- /* Print mem stats at each commit for leak tracking */
- if (ShowStats)
- MemoryContextStats(TopMemoryContext);
+ /* Print mem stats at each commit for leak tracking */
+ if (ShowStats)
+ MemoryContextStats(TopMemoryContext);
#endif
+
+ xact_started = false;
+ }
}
@@ -1679,7 +2335,7 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.331 $ $Date: 2003/05/03 05:13:20 $\n");
+ puts("$Revision: 1.332 $ $Date: 2003/05/05 00:44:56 $\n");
}
/*
@@ -1756,6 +2412,14 @@ PostgresMain(int argc, char *argv[], const char *username)
* successfully. (Flag was set in elog.c before longjmp().)
*/
InError = false;
+ xact_started = false;
+
+ /*
+ * If we were handling an extended-query-protocol message,
+ * initiate skip till next Sync.
+ */
+ if (doing_extended_query_message)
+ ignore_till_sync = true;
/*
* Exit interrupt holdoff section we implicitly established above.
@@ -1776,6 +2440,12 @@ PostgresMain(int argc, char *argv[], const char *username)
for (;;)
{
/*
+ * At top of loop, reset extended-query-message flag, so that
+ * any errors encountered in "idle" state don't provoke skip.
+ */
+ doing_extended_query_message = false;
+
+ /*
* Release storage left over from prior query cycle, and create a
* new query input buffer in the cleared MessageContext.
*/
@@ -1853,20 +2523,74 @@ PostgresMain(int argc, char *argv[], const char *username)
}
/*
- * (6) process the command.
+ * (6) process the command. But ignore it if we're skipping till Sync.
*/
+ if (ignore_till_sync)
+ continue;
+
switch (firstchar)
{
case 'Q': /* simple query */
{
- const char *query_string = pq_getmsgstring(input_message);
+ const char *query_string;
+
+ query_string = pq_getmsgstring(input_message);
+ pq_getmsgend(input_message);
- exec_simple_query(query_string, whereToSendOutput);
+ exec_simple_query(query_string);
send_rfq = true;
}
break;
+ case 'P': /* parse */
+ {
+ const char *stmt_name;
+ const char *query_string;
+ int numParams;
+ Oid *paramTypes = NULL;
+
+ stmt_name = pq_getmsgstring(input_message);
+ query_string = pq_getmsgstring(input_message);
+ numParams = pq_getmsgint(input_message, 4);
+ if (numParams > 0)
+ {
+ int i;
+
+ paramTypes = (Oid *) palloc(numParams * sizeof(Oid));
+ for (i = 0; i < numParams; i++)
+ paramTypes[i] = pq_getmsgint(input_message, 4);
+ }
+ pq_getmsgend(input_message);
+
+ exec_parse_message(query_string, stmt_name,
+ paramTypes, numParams);
+ }
+ break;
+
+ case 'B': /* bind */
+ /*
+ * this message is complex enough that it seems best to put
+ * the field extraction out-of-line
+ */
+ exec_bind_message(input_message);
+ break;
+
+ case 'E': /* execute */
+ {
+ const char *portal_name;
+ int is_binary;
+ int max_rows;
+
+ portal_name = pq_getmsgstring(input_message);
+ is_binary = pq_getmsgbyte(input_message);
+ max_rows = pq_getmsgint(input_message, 4);
+ pq_getmsgend(input_message);
+
+ exec_execute_message(portal_name, is_binary, max_rows);
+ }
+ break;
+
case 'F': /* fastpath function call */
/* Tell the collector what we're doing */
pgstat_report_activity("<FASTPATH> function call");
@@ -1894,6 +2618,89 @@ PostgresMain(int argc, char *argv[], const char *username)
send_rfq = true;
break;
+ case 'C': /* close */
+ {
+ int close_type;
+ const char *close_target;
+
+ close_type = pq_getmsgbyte(input_message);
+ close_target = pq_getmsgstring(input_message);
+ pq_getmsgend(input_message);
+
+ switch (close_type)
+ {
+ case 'S':
+ if (close_target[0] != '\0')
+ DropPreparedStatement(close_target, false);
+ else
+ {
+ /* special-case the unnamed statement */
+ unnamed_stmt_pstmt = NULL;
+ if (unnamed_stmt_context)
+ {
+ DropDependentPortals(unnamed_stmt_context);
+ MemoryContextDelete(unnamed_stmt_context);
+ }
+ unnamed_stmt_context = NULL;
+ }
+ break;
+ case 'P':
+ {
+ Portal portal;
+
+ portal = GetPortalByName(close_target);
+ if (PortalIsValid(portal))
+ PortalDrop(portal, false);
+ }
+ break;
+ default:
+ elog(ERROR, "Invalid Close message subtype %d",
+ close_type);
+ break;
+ }
+
+ if (whereToSendOutput == Remote)
+ pq_putemptymessage('3'); /* CloseComplete */
+ }
+ break;
+
+ case 'D': /* describe */
+ {
+ int describe_type;
+ const char *describe_target;
+
+ describe_type = pq_getmsgbyte(input_message);
+ describe_target = pq_getmsgstring(input_message);
+ pq_getmsgend(input_message);
+
+ switch (describe_type)
+ {
+ case 'S':
+ exec_describe_statement_message(describe_target);
+ break;
+ case 'P':
+ exec_describe_portal_message(describe_target);
+ break;
+ default:
+ elog(ERROR, "Invalid Describe message subtype %d",
+ describe_type);
+ break;
+ }
+ }
+ break;
+
+ case 'H': /* flush */
+ pq_getmsgend(input_message);
+ if (whereToSendOutput == Remote)
+ pq_flush();
+ break;
+
+ case 'S': /* sync */
+ pq_getmsgend(input_message);
+ finish_xact_command(false);
+ send_rfq = true;
+ break;
+
/*
* 'X' means that the frontend is closing down the socket.
* EOF means unexpected loss of frontend connection.
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 974e69a2f1a..2fb62f4abae 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -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;
diff --git a/src/include/access/printtup.h b/src/include/access/printtup.h
index 728b0c89906..688a75cd2db 100644
--- a/src/include/access/printtup.h
+++ b/src/include/access/printtup.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: printtup.h,v 1.23 2003/01/21 22:06:12 tgl Exp $
+ * $Id: printtup.h,v 1.24 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,7 +16,9 @@
#include "tcop/dest.h"
-extern DestReceiver *printtup_create_DR(bool isBinary);
+extern DestReceiver *printtup_create_DR(bool isBinary, bool sendDescrip);
+
+extern void SendRowDescriptionMessage(TupleDesc typeinfo);
extern void debugSetup(DestReceiver *self, int operation,
const char *portalName, TupleDesc typeinfo);
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
index efa60869fa6..f89ac36fa57 100644
--- a/src/include/commands/portalcmds.h
+++ b/src/include/commands/portalcmds.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: portalcmds.h,v 1.8 2003/05/02 20:54:35 tgl Exp $
+ * $Id: portalcmds.h,v 1.9 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,7 +22,7 @@ extern void PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest);
extern void PerformPortalFetch(FetchStmt *stmt, CommandDest dest,
char *completionTag);
-extern void PerformPortalClose(char *name);
+extern void PerformPortalClose(const char *name);
extern void PortalCleanup(Portal portal, bool isError);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index aad64166757..5d24c579a40 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -1,12 +1,12 @@
/*-------------------------------------------------------------------------
*
* prepare.h
- * PREPARE, EXECUTE and DEALLOCATE command prototypes
+ * PREPARE, EXECUTE and DEALLOCATE commands, and prepared-stmt storage
*
*
- * Copyright (c) 2002, PostgreSQL Global Development Group
+ * Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
- * $Id: prepare.h,v 1.3 2003/02/02 23:46:38 tgl Exp $
+ * $Id: prepare.h,v 1.4 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,10 +18,44 @@
#include "tcop/dest.h"
+/*
+ * The data structure representing a prepared statement
+ *
+ * Note: all subsidiary storage lives in the context denoted by the context
+ * field. However, the string referenced by commandTag is not subsidiary
+ * storage; it is assumed to be a compile-time-constant string. As with
+ * portals, commandTag shall be NULL if and only if the original query string
+ * (before rewriting) was an empty string.
+ */
+typedef struct
+{
+ /* dynahash.c requires key to be first field */
+ char stmt_name[NAMEDATALEN];
+ char *query_string; /* text of query, or NULL */
+ const char *commandTag; /* command tag (a constant!), or NULL */
+ 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 */
+} PreparedStatement;
+
+
+/* Utility statements PREPARE, EXECUTE, DEALLOCATE, EXPLAIN EXECUTE */
extern void PrepareQuery(PrepareStmt *stmt);
extern void ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest);
extern void DeallocateQuery(DeallocateStmt *stmt);
-extern List *FetchQueryParams(const char *plan_name);
extern void ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate);
+/* Low-level access to stored prepared statements */
+extern void StorePreparedStatement(const char *stmt_name,
+ const char *query_string,
+ const char *commandTag,
+ List *query_list,
+ List *plan_list,
+ List *argtype_list);
+extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
+ bool throwError);
+extern void DropPreparedStatement(const char *stmt_name, bool showError);
+extern List *FetchPreparedStatementParams(const char *stmt_name);
+
#endif /* PREPARE_H */
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index d5374a68fe6..b6961e824cd 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pqcomm.h,v 1.81 2003/04/26 20:22:59 tgl Exp $
+ * $Id: pqcomm.h,v 1.82 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -106,7 +106,7 @@ typedef union SockAddr
/* The earliest and latest frontend/backend protocol version supported. */
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0)
-#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,105) /* XXX temporary value */
+#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,106) /* XXX temporary value */
typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 39063af6e16..5fbe9d33afe 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -44,7 +44,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: dest.h,v 1.34 2003/04/19 00:02:30 tgl Exp $
+ * $Id: dest.h,v 1.35 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -61,6 +61,10 @@
/* ----------------
* CommandDest is a simplistic means of identifying the desired
* destination. Someday this will probably need to be improved.
+ *
+ * Note: only the values None, Debug, Remote are legal for the global
+ * variable whereToSendOutput. The other values may be selected
+ * as the destination for individual commands.
* ----------------
*/
typedef enum
@@ -71,7 +75,9 @@ typedef enum
RemoteInternal, /* results sent to frontend process in
* internal (binary) form */
SPI, /* results sent to SPI manager */
- Tuplestore /* results sent to Tuplestore */
+ Tuplestore, /* results sent to Tuplestore */
+ RemoteExecute, /* sent to frontend, in Execute command */
+ RemoteExecuteInternal /* same, but binary format */
} CommandDest;
/* ----------------
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index c1fa9c1a6d7..b5e171e1d36 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: tcopprot.h,v 1.56 2003/05/02 20:54:36 tgl Exp $
+ * $Id: tcopprot.h,v 1.57 2003/05/05 00:44:56 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
@@ -35,11 +35,12 @@ extern DLLIMPORT const char *debug_query_string;
#ifndef BOOTSTRAP_INCLUDE
+extern List *pg_parse_and_rewrite(const char *query_string,
+ Oid *paramTypes, int numParams);
extern List *pg_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(Node *parsetree,
Oid *paramTypes, int numParams);
-extern List *pg_parse_and_rewrite(const char *query_string,
- Oid *paramTypes, int numParams);
+extern List *pg_rewrite_queries(List *querytree_list);
extern Plan *pg_plan_query(Query *querytree);
extern List *pg_plan_queries(List *querytrees, bool needSnapshot);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 3bf27da4278..75bcb43433b 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.239 2003/04/28 04:52:13 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.240 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -376,6 +376,17 @@ static bool
connectOptions2(PGconn *conn)
{
/*
+ * If database name was not given, default it to equal user name
+ */
+ if ((conn->dbName == NULL || conn->dbName[0] == '\0')
+ && conn->pguser != NULL)
+ {
+ if (conn->dbName)
+ free(conn->dbName);
+ conn->dbName = strdup(conn->pguser);
+ }
+
+ /*
* Supply default password if none given
*/
if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index a5e6bceef42..3fcecd63e23 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-int.h,v 1.66 2003/04/26 20:23:00 tgl Exp $
+ * $Id: libpq-int.h,v 1.67 2003/05/05 00:44:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -56,7 +56,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
* pqcomm.h describe what the backend knows, not what libpq knows.
*/
-#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,105) /* XXX temporary value */
+#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,106) /* XXX temporary value */
/*
* POSTGRES backend dependent Constants.