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

@ -1,4 +1,4 @@
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.33 2003/04/28 05:17:31 tgl Exp $ --> <!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.34 2003/05/05 00:44:55 tgl Exp $ -->
<chapter id="protocol"> <chapter id="protocol">
<title>Frontend/Backend Protocol</title> <title>Frontend/Backend Protocol</title>
@ -595,7 +595,11 @@
<para> <para>
If successfully created, a named prepared-statement object lasts till If successfully created, a named prepared-statement object lasts till
the end of the current session, unless explicitly destroyed. An unnamed the end of the current session, unless explicitly destroyed. An unnamed
prepared statement lasts only until the next Parse message is issued. prepared statement lasts only until the next Parse statement specifying
the unnamed statement as destination is issued. (Note that a simple
Query message also destroys the unnamed statement.) Named prepared
statements must be explicitly closed before they can be redefined by
a Parse message, but this is not required for the unnamed statement.
Named prepared statements can also be created and accessed at the SQL Named prepared statements can also be created and accessed at the SQL
command level, using <command>PREPARE</> and <command>EXECUTE</>. command level, using <command>PREPARE</> and <command>EXECUTE</>.
</para> </para>
@ -611,10 +615,13 @@
</para> </para>
<para> <para>
If successfully created, a named portal object lasts till If successfully created, a named portal object lasts till the end of the
the end of the current transaction, unless explicitly destroyed. An current transaction, unless explicitly destroyed. An unnamed portal is
unnamed portal is destroyed at the end of the transaction, or as soon destroyed at the end of the transaction, or as soon as the next Bind
as the next Parse or Bind message is executed. statement specifying the unnamed portal as destination is issued. (Note
that a simple Query message also destroys the unnamed portal.) Named
portals must be explicitly closed before they can be redefined by a Bind
message, but this is not required for the unnamed portal.
Named portals can also be created and accessed at the SQL Named portals can also be created and accessed at the SQL
command level, using <command>DECLARE CURSOR</> and <command>FETCH</>. command level, using <command>DECLARE CURSOR</> and <command>FETCH</>.
</para> </para>
@ -691,17 +698,19 @@
The Describe message (statement variant) specifies the name of an existing The Describe message (statement variant) specifies the name of an existing
prepared statement (or an empty string for the unnamed prepared prepared statement (or an empty string for the unnamed prepared
statement). The response is a ParameterDescription message describing the statement). The response is a ParameterDescription message describing the
parameters needed by the statement (if any), followed by a RowDescription parameters needed by the statement. ErrorResponse is issued if there is
message describing the rows that will be returned when the statement is no such prepared statement. This message may be useful if the client
eventually executed (or NoData if there is no SELECT-type query in the library is uncertain about the parameters needed by a prepared statement.
prepared statement). ErrorResponse is issued if there is no such prepared
statement. This message may be useful if the client library is
uncertain about the parameters needed by a prepared statement.
</para> </para>
<para> <para>
The Close message closes an existing prepared statement or portal The Close message closes an existing prepared statement or portal
and releases resources. and releases resources. It is not an error to issue Close against
a nonexistent statement or portal name. The response is normally
CloseComplete, but could be ErrorResponse if some difficulty is
encountered while releasing resources. Note that closing a prepared
statement implicitly closes any open portals that were constructed
from that statement.
</para> </para>
<para> <para>
@ -709,19 +718,20 @@
but forces the backend to deliver any data pending in its output but forces the backend to deliver any data pending in its output
buffers. A Flush must be sent after any extended-query command except buffers. A Flush must be sent after any extended-query command except
Sync, if the frontend wishes to examine the results of that command before Sync, if the frontend wishes to examine the results of that command before
issuing more commands. Without Flush, returning data will be combined issuing more commands. Without Flush, messages returned by the backend
into the minimum possible number of packets to minimize network overhead. will be combined into the minimum possible number of packets to minimize
network overhead.
</para> </para>
<note> <note>
<para> <para>
The simple Query message is approximately equivalent to the series Parse, The simple Query message is approximately equivalent to the series Parse,
Bind, portal Describe, Execute, Sync, using the unnamed prepared statement Bind, portal Describe, Execute, Close, Sync, using the unnamed prepared
and portal objects and no parameters. One difference is that it statement and portal objects and no parameters. One difference is that
will accept multiple SQL statements in the query string, automatically it will accept multiple SQL statements in the query string, automatically
performing the bind/describe/execute sequence for each one in succession. performing the bind/describe/execute sequence for each one in succession.
Another is that it will not return ParseComplete, BindComplete, or Another difference is that it will not return ParseComplete, BindComplete,
NoData messages. CloseComplete, or NoData messages.
</para> </para>
</note> </note>
</sect2> </sect2>
@ -1917,6 +1927,41 @@ Close (F)
</VarListEntry> </VarListEntry>
<VarListEntry>
<Term>
CloseComplete (B)
</Term>
<ListItem>
<Para>
<VariableList>
<VarListEntry>
<Term>
Byte1('3')
</Term>
<ListItem>
<Para>
Identifies the message as a Close-complete indicator.
</Para>
</ListItem>
</VarListEntry>
<VarListEntry>
<Term>
Int32(4)
</Term>
<ListItem>
<Para>
Length of message contents in bytes, including self.
</Para>
</ListItem>
</VarListEntry>
</VariableList>
</Para>
</ListItem>
</VarListEntry>
<VarListEntry> <VarListEntry>
<Term> <Term>
CommandComplete (B) CommandComplete (B)
@ -3875,6 +3920,15 @@ The ReadyForQuery ('<literal>Z</>') message includes a transaction status
indicator. indicator.
</para> </para>
<para>
There is a new <quote>extended query</> sub-protocol, which adds the frontend
message types Parse, Bind, Execute, Describe, Close, Flush, and Sync, and the
backend message types ParseComplete, BindComplete, PortalSuspended,
ParameterDescription, NoData, and CloseComplete. Existing clients do not
have to concern themselves with this sub-protocol, but making use of it
may allow improvements in performance or functionality.
</para>
<para> <para>
COPY data is now encapsulated into CopyData and CopyDone messages. There COPY data is now encapsulated into CopyData and CopyDone messages. There
is a well-defined way to recover from errors during COPY. The special is a well-defined way to recover from errors during COPY. The special

View File

@ -9,7 +9,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 typedef struct
{ {
DestReceiver pub; /* publicly-known function pointers */ DestReceiver pub; /* publicly-known function pointers */
bool sendDescrip; /* send RowDescription at startup? */
TupleDesc attrinfo; /* The attr info we are set up for */ TupleDesc attrinfo; /* The attr info we are set up for */
int nattrs; int nattrs;
PrinttupAttrInfo *myinfo; /* Cached info about each attr */ PrinttupAttrInfo *myinfo; /* Cached info about each attr */
@ -58,7 +59,7 @@ typedef struct
* ---------------- * ----------------
*/ */
DestReceiver * DestReceiver *
printtup_create_DR(bool isBinary) printtup_create_DR(bool isBinary, bool sendDescrip)
{ {
DR_printtup *self = (DR_printtup *) palloc(sizeof(DR_printtup)); 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.setup = printtup_setup;
self->pub.cleanup = printtup_cleanup; self->pub.cleanup = printtup_cleanup;
self->sendDescrip = sendDescrip;
self->attrinfo = NULL; self->attrinfo = NULL;
self->nattrs = 0; self->nattrs = 0;
self->myinfo = NULL; self->myinfo = NULL;
@ -77,6 +80,8 @@ static void
printtup_setup(DestReceiver *self, int operation, printtup_setup(DestReceiver *self, int operation,
const char *portalName, TupleDesc typeinfo) const char *portalName, TupleDesc typeinfo)
{ {
DR_printtup *myState = (DR_printtup *) self;
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) 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 * If this is a retrieve, and we are supposed to emit row descriptions,
* the tuples. * then we send back the tuple descriptor of the tuples.
*/ */
if (operation == CMD_SELECT) if (operation == CMD_SELECT && myState->sendDescrip)
{ SendRowDescriptionMessage(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);
}
/* ---------------- /* ----------------
* We could set up the derived attr info at this time, but we postpone it * 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 static void
printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
{ {

View File

@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * 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 * Disallow empty-string cursor name (conflicts with protocol-level
* unnamed portal). * unnamed portal).
*/ */
if (strlen(stmt->portalname) == 0) if (!stmt->portalname || stmt->portalname[0] == '\0')
elog(ERROR, "Invalid cursor name: must not be empty"); elog(ERROR, "Invalid cursor name: must not be empty");
/* /*
@ -148,6 +148,13 @@ PerformPortalFetch(FetchStmt *stmt,
Portal portal; Portal portal;
long nprocessed; 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 */ /* get the portal from the portal name */
portal = GetPortalByName(stmt->portalname); portal = GetPortalByName(stmt->portalname);
if (!PortalIsValid(portal)) if (!PortalIsValid(portal))
@ -164,7 +171,9 @@ PerformPortalFetch(FetchStmt *stmt,
* Adjust dest if needed. MOVE wants dest = None. * Adjust dest if needed. MOVE wants dest = None.
* *
* If fetching from a binary cursor and the requested destination is * 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) if (stmt->ismove)
dest = None; dest = None;
@ -189,10 +198,17 @@ PerformPortalFetch(FetchStmt *stmt,
* Close a cursor. * Close a cursor.
*/ */
void void
PerformPortalClose(char *name) PerformPortalClose(const char *name)
{ {
Portal portal; 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 * get the portal from the portal name
*/ */

View File

@ -3,10 +3,14 @@
* prepare.c * prepare.c
* Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE * 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 * 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" #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 * The hash table in which prepared queries are stored. This is
* per-backend: query plans are not shared between backends. * per-backend: query plans are not shared between backends.
* The keys for this hash table are the arguments to PREPARE * The keys for this hash table are the arguments to PREPARE and EXECUTE
* and EXECUTE ("plan names"); the entries are QueryHashEntry structs. * (statement names); the entries are PreparedStatement structs.
*/ */
static HTAB *prepared_queries = NULL; static HTAB *prepared_queries = NULL;
static void InitQueryHashTable(void); 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, static ParamListInfo EvaluateParams(EState *estate,
List *params, List *argtypes); List *params, List *argtypes);
@ -59,14 +47,36 @@ static ParamListInfo EvaluateParams(EState *estate,
void void
PrepareQuery(PrepareStmt *stmt) PrepareQuery(PrepareStmt *stmt)
{ {
const char *commandTag;
List *query_list, List *query_list,
*plan_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) switch (stmt->query->commandType)
elog(ERROR, "Utility statements cannot be prepared"); {
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 * 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); plan_list = pg_plan_queries(query_list, false);
/* Save the results. */ /* 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 void
ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest) ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
{ {
QueryHashEntry *entry; PreparedStatement *entry;
char *query_string;
List *query_list, List *query_list,
*plan_list; *plan_list;
MemoryContext qcontext; MemoryContext qcontext;
@ -98,8 +114,9 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
Portal portal; Portal portal;
/* Look it up in the hash table */ /* 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; query_list = entry->query_list;
plan_list = entry->plan_list; plan_list = entry->plan_list;
qcontext = entry->context; qcontext = entry->context;
@ -135,6 +152,8 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
if (query_string)
query_string = pstrdup(query_string);
query_list = copyObject(query_list); query_list = copyObject(query_list);
plan_list = copyObject(plan_list); plan_list = copyObject(plan_list);
qcontext = PortalGetHeapMemory(portal); qcontext = PortalGetHeapMemory(portal);
@ -150,8 +169,8 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
} }
PortalDefineQuery(portal, PortalDefineQuery(portal,
NULL, /* XXX fixme: can we save query text? */ query_string,
NULL, /* no command tag known either */ entry->commandTag,
query_list, query_list,
plan_list, plan_list,
qcontext); qcontext);
@ -228,8 +247,8 @@ InitQueryHashTable(void)
MemSet(&hash_ctl, 0, sizeof(hash_ctl)); MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = HASH_KEY_LEN; hash_ctl.keysize = NAMEDATALEN;
hash_ctl.entrysize = sizeof(QueryHashEntry); hash_ctl.entrysize = sizeof(PreparedStatement);
prepared_queries = hash_create("Prepared Queries", prepared_queries = hash_create("Prepared Queries",
32, 32,
@ -244,15 +263,24 @@ InitQueryHashTable(void)
* Store all the data pertaining to a query in the hash table using * 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 * 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. * 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 void
StoreQuery(const char *stmt_name, List *query_list, StorePreparedStatement(const char *stmt_name,
List *plan_list, List *argtype_list) const char *query_string,
const char *commandTag,
List *query_list,
List *plan_list,
List *argtype_list)
{ {
QueryHashEntry *entry; PreparedStatement *entry;
MemoryContext oldcxt, MemoryContext oldcxt,
entrycxt; entrycxt;
char key[HASH_KEY_LEN]; char *qstring;
char key[NAMEDATALEN];
bool found; bool found;
/* Initialize the hash table, if necessary */ /* Initialize the hash table, if necessary */
@ -260,7 +288,7 @@ StoreQuery(const char *stmt_name, List *query_list,
InitQueryHashTable(); InitQueryHashTable();
/* Check for pre-existing entry of same name */ /* Check for pre-existing entry of same name */
/* See notes in FetchQuery */ /* See notes in FetchPreparedStatement */
MemSet(key, 0, sizeof(key)); MemSet(key, 0, sizeof(key));
strncpy(key, stmt_name, 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 * out-of-memory failure only wastes memory and doesn't leave us with
* an incomplete (ie corrupt) hashtable entry. * an incomplete (ie corrupt) hashtable entry.
*/ */
qstring = query_string ? pstrdup(query_string) : (char *) NULL;
query_list = (List *) copyObject(query_list); query_list = (List *) copyObject(query_list);
plan_list = (List *) copyObject(plan_list); plan_list = (List *) copyObject(plan_list);
argtype_list = listCopy(argtype_list); argtype_list = listCopy(argtype_list);
/* Now we can add entry to hash table */ /* Now we can add entry to hash table */
entry = (QueryHashEntry *) hash_search(prepared_queries, entry = (PreparedStatement *) hash_search(prepared_queries,
key, key,
HASH_ENTER, HASH_ENTER,
&found); &found);
/* Shouldn't get a failure, nor a duplicate entry */ /* Shouldn't get a failure, nor a duplicate entry */
if (!entry || found) if (!entry || found)
@ -301,6 +330,8 @@ StoreQuery(const char *stmt_name, List *query_list,
stmt_name); stmt_name);
/* Fill in the hash table entry with copied data */ /* Fill in the hash table entry with copied data */
entry->query_string = qstring;
entry->commandTag = commandTag;
entry->query_list = query_list; entry->query_list = query_list;
entry->plan_list = plan_list; entry->plan_list = plan_list;
entry->argtype_list = argtype_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 * 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 * PreparedStatement *
FetchQuery(const char *plan_name) FetchPreparedStatement(const char *stmt_name, bool throwError)
{ {
char key[HASH_KEY_LEN]; char key[NAMEDATALEN];
QueryHashEntry *entry; PreparedStatement *entry;
/* /*
* If the hash table hasn't been initialized, it can't be storing * If the hash table hasn't been initialized, it can't be storing
* anything, therefore it couldn't possibly store our plan. * 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", elog(ERROR, "Prepared statement with name \"%s\" does not exist",
plan_name); 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, 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; 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. * If found, return the list of argument type OIDs.
*/ */
List * 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; return entry->argtype_list;
} }
@ -368,20 +400,34 @@ FetchQueryParams(const char *plan_name)
void void
DeallocateQuery(DeallocateStmt *stmt) 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 */ /* Find the query's hash table entry; raise error if wanted */
Assert(MemoryContextIsValid(entry->context)); entry = FetchPreparedStatement(stmt_name, showError);
DropDependentPortals(entry->context);
/* Flush the context holding the subsidiary data */ if (entry)
MemoryContextDelete(entry->context); {
/* 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 */ /* Flush the context holding the subsidiary data */
hash_search(prepared_queries, entry->key, HASH_REMOVE, NULL); 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) ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
{ {
ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt; ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
QueryHashEntry *entry; PreparedStatement *entry;
List *l, List *l,
*query_list, *query_list,
*plan_list; *plan_list;
@ -402,7 +448,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
Assert(execstmt && IsA(execstmt, ExecuteStmt)); Assert(execstmt && IsA(execstmt, ExecuteStmt));
/* Look it up in the hash table */ /* Look it up in the hash table */
entry = FetchQuery(execstmt->name); entry = FetchPreparedStatement(execstmt->name, true);
query_list = entry->query_list; query_list = entry->query_list;
plan_list = entry->plan_list; plan_list = entry->plan_list;

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 */ IndexStmt *pkey; /* PRIMARY KEY index, if any */
} CreateStmtContext; } CreateStmtContext;
typedef struct
{
Oid *paramTypes;
int numParams;
} check_parameter_resolution_context;
static List *do_parse_analyze(Node *parseTree, ParseState *pstate); static List *do_parse_analyze(Node *parseTree, ParseState *pstate);
static Query *transformStmt(ParseState *pstate, Node *stmt, static Query *transformStmt(ParseState *pstate, Node *stmt,
@ -124,6 +130,8 @@ static void transformColumnType(ParseState *pstate, ColumnDef *column);
static bool relationHasPrimaryKey(Oid relationOid); static bool relationHasPrimaryKey(Oid relationOid);
static void release_pstate_resources(ParseState *pstate); static void release_pstate_resources(ParseState *pstate);
static FromExpr *makeFromExpr(List *fromlist, Node *quals); 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); 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; return result;
} }
@ -2465,7 +2483,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
result->commandType = CMD_UTILITY; result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) stmt; result->utilityStmt = (Node *) stmt;
paramtypes = FetchQueryParams(stmt->name); paramtypes = FetchPreparedStatementParams(stmt->name);
if (stmt->params || paramtypes) if (stmt->params || paramtypes)
{ {
@ -2879,3 +2897,44 @@ analyzeCreateSchemaStmt(CreateSchemaStmt *stmt)
return result; 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 * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) switch (dest)
{ {
case Remote: case Remote:
return printtup_create_DR(false); return printtup_create_DR(false, true);
case RemoteInternal: 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: case Debug:
return &debugtupDR; return &debugtupDR;
@ -104,9 +114,6 @@ DestToFunction(CommandDest dest)
case Tuplestore: case Tuplestore:
return tstoreReceiverCreateDR(); return tstoreReceiverCreateDR();
case None:
return &donothingDR;
} }
/* should never get here */ /* should never get here */
@ -124,13 +131,15 @@ EndCommand(const char *commandTag, CommandDest dest)
{ {
case Remote: case Remote:
case RemoteInternal: case RemoteInternal:
case RemoteExecute:
case RemoteExecuteInternal:
pq_puttextmessage('C', commandTag); pq_puttextmessage('C', commandTag);
break; break;
case None: case None:
case Debug: case Debug:
case Tuplestore:
case SPI: case SPI:
case Tuplestore:
break; break;
} }
} }
@ -152,8 +161,10 @@ NullCommand(CommandDest dest)
{ {
switch (dest) switch (dest)
{ {
case RemoteInternal:
case Remote: case Remote:
case RemoteInternal:
case RemoteExecute:
case RemoteExecuteInternal:
/* /*
* tell the fe that we saw an empty query string. In protocols * tell the fe that we saw an empty query string. In protocols
@ -165,10 +176,10 @@ NullCommand(CommandDest dest)
pq_puttextmessage('I', ""); pq_puttextmessage('I', "");
break; break;
case Debug:
case Tuplestore:
case None: case None:
default: case Debug:
case SPI:
case Tuplestore:
break; break;
} }
} }
@ -189,8 +200,10 @@ ReadyForQuery(CommandDest dest)
{ {
switch (dest) switch (dest)
{ {
case RemoteInternal:
case Remote: case Remote:
case RemoteInternal:
case RemoteExecute:
case RemoteExecuteInternal:
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{ {
StringInfoData buf; StringInfoData buf;
@ -205,10 +218,10 @@ ReadyForQuery(CommandDest dest)
pq_flush(); pq_flush();
break; break;
case Debug:
case Tuplestore:
case None: case None:
default: case Debug:
case SPI:
case Tuplestore:
break; break;
} }
} }

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* This cruft is the server side of PQfn. * This cruft is the server side of PQfn.
@ -310,6 +310,14 @@ HandleFunctionRequest(StringInfo msgBuf)
if (aclresult != ACLCHECK_OK) if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, get_func_name(fid)); 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) if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)", elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
nargs, fip->flinfo.fn_nargs); nargs, fip->flinfo.fn_nargs);
@ -359,12 +367,8 @@ HandleFunctionRequest(StringInfo msgBuf)
} }
} }
/* /* Verify we reached the end of the message where expected. */
* Set up a query snapshot in case function needs one. (It is not safe pq_getmsgend(msgBuf);
* to do this if we are in transaction-abort state, so we have to postpone
* it till now. Ugh.)
*/
SetQuerySnapshot();
#ifdef NO_FASTPATH #ifdef NO_FASTPATH
/* force a NULL return */ /* 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 * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 * PortalDefineQuery
* A simple subroutine to establish a portal's query. * A simple subroutine to establish a portal's query.
* *
* Notes: the passed commandTag must be a pointer to a constant string, * Notes: commandTag shall be NULL if and only if the original query string
* since it is not copied. The caller is responsible for ensuring that * (before rewriting) was an empty string. Also, the passed commandTag must
* the passed sourceText (if any), parse and plan trees have adequate * be a pointer to a constant string, since it is not copied. The caller is
* lifetime. Also, queryContext must accurately describe the location * responsible for ensuring that the passed sourceText (if any), parse and
* of the parse and plan trees. * plan trees have adequate lifetime. Also, queryContext must accurately
* describe the location of the parse and plan trees.
*/ */
void void
PortalDefineQuery(Portal portal, PortalDefineQuery(Portal portal,
@ -241,6 +242,8 @@ PortalDefineQuery(Portal portal,
Assert(length(parseTrees) == length(planTrees)); Assert(length(parseTrees) == length(planTrees));
Assert(commandTag != NULL || parseTrees == NIL);
portal->sourceText = sourceText; portal->sourceText = sourceText;
portal->commandTag = commandTag; portal->commandTag = commandTag;
portal->parseTrees = parseTrees; portal->parseTrees = parseTrees;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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" #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, extern void debugSetup(DestReceiver *self, int operation,
const char *portalName, TupleDesc typeinfo); const char *portalName, TupleDesc typeinfo);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, extern void PerformPortalFetch(FetchStmt *stmt, CommandDest dest,
char *completionTag); char *completionTag);
extern void PerformPortalClose(char *name); extern void PerformPortalClose(const char *name);
extern void PortalCleanup(Portal portal, bool isError); extern void PortalCleanup(Portal portal, bool isError);

View File

@ -1,12 +1,12 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* prepare.h * 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" #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 PrepareQuery(PrepareStmt *stmt);
extern void ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest); extern void ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest);
extern void DeallocateQuery(DeallocateStmt *stmt); extern void DeallocateQuery(DeallocateStmt *stmt);
extern List *FetchQueryParams(const char *plan_name);
extern void ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate); 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 */ #endif /* PREPARE_H */

View File

@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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. */ /* The earliest and latest frontend/backend protocol version supported. */
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0) #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 */ typedef uint32 ProtocolVersion; /* FE/BE protocol version number */

View File

@ -44,7 +44,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * CommandDest is a simplistic means of identifying the desired
* destination. Someday this will probably need to be improved. * 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 typedef enum
@ -71,7 +75,9 @@ typedef enum
RemoteInternal, /* results sent to frontend process in RemoteInternal, /* results sent to frontend process in
* internal (binary) form */ * internal (binary) form */
SPI, /* results sent to SPI manager */ 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; } CommandDest;
/* ---------------- /* ----------------

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * OLD COMMENTS
* This file was created so that other c files could get the two * 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 #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_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(Node *parsetree, extern List *pg_analyze_and_rewrite(Node *parsetree,
Oid *paramTypes, int numParams); Oid *paramTypes, int numParams);
extern List *pg_parse_and_rewrite(const char *query_string, extern List *pg_rewrite_queries(List *querytree_list);
Oid *paramTypes, int numParams);
extern Plan *pg_plan_query(Query *querytree); extern Plan *pg_plan_query(Query *querytree);
extern List *pg_plan_queries(List *querytrees, bool needSnapshot); extern List *pg_plan_queries(List *querytrees, bool needSnapshot);

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -375,6 +375,17 @@ connectOptions1(PGconn *conn, const char *conninfo)
static bool static bool
connectOptions2(PGconn *conn) 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 * Supply default password if none given
*/ */

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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. * 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. * POSTGRES backend dependent Constants.