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:
parent
a59793f82c
commit
16503e6fa4
@ -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">
|
||||
<title>Frontend/Backend Protocol</title>
|
||||
@ -595,7 +595,11 @@
|
||||
<para>
|
||||
If successfully created, a named prepared-statement object lasts till
|
||||
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
|
||||
command level, using <command>PREPARE</> and <command>EXECUTE</>.
|
||||
</para>
|
||||
@ -611,10 +615,13 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If successfully created, a named portal object lasts till
|
||||
the end of the current transaction, unless explicitly destroyed. An
|
||||
unnamed portal is destroyed at the end of the transaction, or as soon
|
||||
as the next Parse or Bind message is executed.
|
||||
If successfully created, a named portal object lasts till the end of the
|
||||
current transaction, unless explicitly destroyed. An unnamed portal is
|
||||
destroyed at the end of the transaction, or as soon as the next Bind
|
||||
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
|
||||
command level, using <command>DECLARE CURSOR</> and <command>FETCH</>.
|
||||
</para>
|
||||
@ -691,17 +698,19 @@
|
||||
The Describe message (statement variant) specifies the name of an existing
|
||||
prepared statement (or an empty string for the unnamed prepared
|
||||
statement). The response is a ParameterDescription message describing the
|
||||
parameters needed by the statement (if any), followed by a RowDescription
|
||||
message describing the rows that will be returned when the statement is
|
||||
eventually executed (or NoData if there is no SELECT-type query in the
|
||||
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.
|
||||
parameters needed by the 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>
|
||||
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>
|
||||
@ -709,19 +718,20 @@
|
||||
but forces the backend to deliver any data pending in its output
|
||||
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
|
||||
issuing more commands. Without Flush, returning data will be combined
|
||||
into the minimum possible number of packets to minimize network overhead.
|
||||
issuing more commands. Without Flush, messages returned by the backend
|
||||
will be combined into the minimum possible number of packets to minimize
|
||||
network overhead.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
The simple Query message is approximately equivalent to the series Parse,
|
||||
Bind, portal Describe, Execute, Sync, using the unnamed prepared statement
|
||||
and portal objects and no parameters. One difference is that it
|
||||
will accept multiple SQL statements in the query string, automatically
|
||||
Bind, portal Describe, Execute, Close, Sync, using the unnamed prepared
|
||||
statement and portal objects and no parameters. One difference is that
|
||||
it will accept multiple SQL statements in the query string, automatically
|
||||
performing the bind/describe/execute sequence for each one in succession.
|
||||
Another is that it will not return ParseComplete, BindComplete, or
|
||||
NoData messages.
|
||||
Another difference is that it will not return ParseComplete, BindComplete,
|
||||
CloseComplete, or NoData messages.
|
||||
</para>
|
||||
</note>
|
||||
</sect2>
|
||||
@ -1917,6 +1927,41 @@ Close (F)
|
||||
</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>
|
||||
<Term>
|
||||
CommandComplete (B)
|
||||
@ -3875,6 +3920,15 @@ The ReadyForQuery ('<literal>Z</>') message includes a transaction status
|
||||
indicator.
|
||||
</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>
|
||||
COPY data is now encapsulated into CopyData and CopyDone messages. There
|
||||
is a well-defined way to recover from errors during COPY. The special
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.13 2003/05/02 20:54:33 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.14 2003/05/05 00:44:55 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -49,7 +49,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
|
||||
* Disallow empty-string cursor name (conflicts with protocol-level
|
||||
* unnamed portal).
|
||||
*/
|
||||
if (strlen(stmt->portalname) == 0)
|
||||
if (!stmt->portalname || stmt->portalname[0] == '\0')
|
||||
elog(ERROR, "Invalid cursor name: must not be empty");
|
||||
|
||||
/*
|
||||
@ -148,6 +148,13 @@ PerformPortalFetch(FetchStmt *stmt,
|
||||
Portal portal;
|
||||
long nprocessed;
|
||||
|
||||
/*
|
||||
* Disallow empty-string cursor name (conflicts with protocol-level
|
||||
* unnamed portal).
|
||||
*/
|
||||
if (!stmt->portalname || stmt->portalname[0] == '\0')
|
||||
elog(ERROR, "Invalid cursor name: must not be empty");
|
||||
|
||||
/* get the portal from the portal name */
|
||||
portal = GetPortalByName(stmt->portalname);
|
||||
if (!PortalIsValid(portal))
|
||||
@ -164,7 +171,9 @@ PerformPortalFetch(FetchStmt *stmt,
|
||||
* Adjust dest if needed. MOVE wants dest = None.
|
||||
*
|
||||
* If fetching from a binary cursor and the requested destination is
|
||||
* Remote, change it to RemoteInternal.
|
||||
* Remote, change it to RemoteInternal. Note we do NOT change if the
|
||||
* destination is RemoteExecute --- so the Execute message's format
|
||||
* specification wins out over the cursor's type.
|
||||
*/
|
||||
if (stmt->ismove)
|
||||
dest = None;
|
||||
@ -189,10 +198,17 @@ PerformPortalFetch(FetchStmt *stmt,
|
||||
* Close a cursor.
|
||||
*/
|
||||
void
|
||||
PerformPortalClose(char *name)
|
||||
PerformPortalClose(const char *name)
|
||||
{
|
||||
Portal portal;
|
||||
|
||||
/*
|
||||
* Disallow empty-string cursor name (conflicts with protocol-level
|
||||
* unnamed portal).
|
||||
*/
|
||||
if (!name || name[0] == '\0')
|
||||
elog(ERROR, "Invalid cursor name: must not be empty");
|
||||
|
||||
/*
|
||||
* get the portal from the portal name
|
||||
*/
|
||||
|
@ -3,10 +3,14 @@
|
||||
* prepare.c
|
||||
* Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
|
||||
*
|
||||
* Copyright (c) 2002, PostgreSQL Global Development Group
|
||||
* This module also implements storage of prepared statements that are
|
||||
* accessed via the extended FE/BE query protocol.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.14 2003/05/02 20:54:33 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.15 2003/05/05 00:44:55 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -25,31 +29,15 @@
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
#define HASH_KEY_LEN NAMEDATALEN
|
||||
|
||||
/* All the data we need to remember about a stored query */
|
||||
typedef struct
|
||||
{
|
||||
/* dynahash.c requires key to be first field */
|
||||
char key[HASH_KEY_LEN];
|
||||
List *query_list; /* list of queries */
|
||||
List *plan_list; /* list of plans */
|
||||
List *argtype_list; /* list of parameter type OIDs */
|
||||
MemoryContext context; /* context containing this query */
|
||||
} QueryHashEntry;
|
||||
|
||||
/*
|
||||
* The hash table in which prepared queries are stored. This is
|
||||
* per-backend: query plans are not shared between backends.
|
||||
* The keys for this hash table are the arguments to PREPARE
|
||||
* and EXECUTE ("plan names"); the entries are QueryHashEntry structs.
|
||||
* The keys for this hash table are the arguments to PREPARE and EXECUTE
|
||||
* (statement names); the entries are PreparedStatement structs.
|
||||
*/
|
||||
static HTAB *prepared_queries = NULL;
|
||||
|
||||
static void InitQueryHashTable(void);
|
||||
static void StoreQuery(const char *stmt_name, List *query_list,
|
||||
List *plan_list, List *argtype_list);
|
||||
static QueryHashEntry *FetchQuery(const char *plan_name);
|
||||
static ParamListInfo EvaluateParams(EState *estate,
|
||||
List *params, List *argtypes);
|
||||
|
||||
@ -59,14 +47,36 @@ static ParamListInfo EvaluateParams(EState *estate,
|
||||
void
|
||||
PrepareQuery(PrepareStmt *stmt)
|
||||
{
|
||||
const char *commandTag;
|
||||
List *query_list,
|
||||
*plan_list;
|
||||
|
||||
if (!stmt->name)
|
||||
elog(ERROR, "No statement name given");
|
||||
/*
|
||||
* Disallow empty-string statement name (conflicts with protocol-level
|
||||
* unnamed statement).
|
||||
*/
|
||||
if (!stmt->name || stmt->name[0] == '\0')
|
||||
elog(ERROR, "Invalid statement name: must not be empty");
|
||||
|
||||
if (stmt->query->commandType == CMD_UTILITY)
|
||||
elog(ERROR, "Utility statements cannot be prepared");
|
||||
switch (stmt->query->commandType)
|
||||
{
|
||||
case CMD_SELECT:
|
||||
commandTag = "SELECT";
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
commandTag = "INSERT";
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
commandTag = "UPDATE";
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
commandTag = "DELETE";
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Utility statements cannot be prepared");
|
||||
commandTag = NULL; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse analysis is already done, but we must still rewrite and plan
|
||||
@ -80,7 +90,12 @@ PrepareQuery(PrepareStmt *stmt)
|
||||
plan_list = pg_plan_queries(query_list, false);
|
||||
|
||||
/* Save the results. */
|
||||
StoreQuery(stmt->name, query_list, plan_list, stmt->argtype_oids);
|
||||
StorePreparedStatement(stmt->name,
|
||||
NULL, /* text form not available */
|
||||
commandTag,
|
||||
query_list,
|
||||
plan_list,
|
||||
stmt->argtype_oids);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -89,7 +104,8 @@ PrepareQuery(PrepareStmt *stmt)
|
||||
void
|
||||
ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
|
||||
{
|
||||
QueryHashEntry *entry;
|
||||
PreparedStatement *entry;
|
||||
char *query_string;
|
||||
List *query_list,
|
||||
*plan_list;
|
||||
MemoryContext qcontext;
|
||||
@ -98,8 +114,9 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
|
||||
Portal portal;
|
||||
|
||||
/* Look it up in the hash table */
|
||||
entry = FetchQuery(stmt->name);
|
||||
entry = FetchPreparedStatement(stmt->name, true);
|
||||
|
||||
query_string = entry->query_string;
|
||||
query_list = entry->query_list;
|
||||
plan_list = entry->plan_list;
|
||||
qcontext = entry->context;
|
||||
@ -135,6 +152,8 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
|
||||
|
||||
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||
|
||||
if (query_string)
|
||||
query_string = pstrdup(query_string);
|
||||
query_list = copyObject(query_list);
|
||||
plan_list = copyObject(plan_list);
|
||||
qcontext = PortalGetHeapMemory(portal);
|
||||
@ -150,8 +169,8 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
|
||||
}
|
||||
|
||||
PortalDefineQuery(portal,
|
||||
NULL, /* XXX fixme: can we save query text? */
|
||||
NULL, /* no command tag known either */
|
||||
query_string,
|
||||
entry->commandTag,
|
||||
query_list,
|
||||
plan_list,
|
||||
qcontext);
|
||||
@ -228,8 +247,8 @@ InitQueryHashTable(void)
|
||||
|
||||
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
|
||||
|
||||
hash_ctl.keysize = HASH_KEY_LEN;
|
||||
hash_ctl.entrysize = sizeof(QueryHashEntry);
|
||||
hash_ctl.keysize = NAMEDATALEN;
|
||||
hash_ctl.entrysize = sizeof(PreparedStatement);
|
||||
|
||||
prepared_queries = hash_create("Prepared Queries",
|
||||
32,
|
||||
@ -244,15 +263,24 @@ InitQueryHashTable(void)
|
||||
* Store all the data pertaining to a query in the hash table using
|
||||
* the specified key. A copy of the data is made in a memory context belonging
|
||||
* to the hash entry, so the caller can dispose of their copy.
|
||||
*
|
||||
* Exception: commandTag is presumed to be a pointer to a constant string,
|
||||
* or possibly NULL, so it need not be copied. Note that commandTag should
|
||||
* be NULL only if the original query (before rewriting) was empty.
|
||||
*/
|
||||
static void
|
||||
StoreQuery(const char *stmt_name, List *query_list,
|
||||
List *plan_list, List *argtype_list)
|
||||
void
|
||||
StorePreparedStatement(const char *stmt_name,
|
||||
const char *query_string,
|
||||
const char *commandTag,
|
||||
List *query_list,
|
||||
List *plan_list,
|
||||
List *argtype_list)
|
||||
{
|
||||
QueryHashEntry *entry;
|
||||
PreparedStatement *entry;
|
||||
MemoryContext oldcxt,
|
||||
entrycxt;
|
||||
char key[HASH_KEY_LEN];
|
||||
char *qstring;
|
||||
char key[NAMEDATALEN];
|
||||
bool found;
|
||||
|
||||
/* Initialize the hash table, if necessary */
|
||||
@ -260,7 +288,7 @@ StoreQuery(const char *stmt_name, List *query_list,
|
||||
InitQueryHashTable();
|
||||
|
||||
/* Check for pre-existing entry of same name */
|
||||
/* See notes in FetchQuery */
|
||||
/* See notes in FetchPreparedStatement */
|
||||
MemSet(key, 0, sizeof(key));
|
||||
strncpy(key, stmt_name, sizeof(key));
|
||||
|
||||
@ -285,15 +313,16 @@ StoreQuery(const char *stmt_name, List *query_list,
|
||||
* out-of-memory failure only wastes memory and doesn't leave us with
|
||||
* an incomplete (ie corrupt) hashtable entry.
|
||||
*/
|
||||
qstring = query_string ? pstrdup(query_string) : (char *) NULL;
|
||||
query_list = (List *) copyObject(query_list);
|
||||
plan_list = (List *) copyObject(plan_list);
|
||||
argtype_list = listCopy(argtype_list);
|
||||
|
||||
/* Now we can add entry to hash table */
|
||||
entry = (QueryHashEntry *) hash_search(prepared_queries,
|
||||
key,
|
||||
HASH_ENTER,
|
||||
&found);
|
||||
entry = (PreparedStatement *) hash_search(prepared_queries,
|
||||
key,
|
||||
HASH_ENTER,
|
||||
&found);
|
||||
|
||||
/* Shouldn't get a failure, nor a duplicate entry */
|
||||
if (!entry || found)
|
||||
@ -301,6 +330,8 @@ StoreQuery(const char *stmt_name, List *query_list,
|
||||
stmt_name);
|
||||
|
||||
/* Fill in the hash table entry with copied data */
|
||||
entry->query_string = qstring;
|
||||
entry->commandTag = commandTag;
|
||||
entry->query_list = query_list;
|
||||
entry->plan_list = plan_list;
|
||||
entry->argtype_list = argtype_list;
|
||||
@ -311,52 +342,53 @@ StoreQuery(const char *stmt_name, List *query_list,
|
||||
|
||||
/*
|
||||
* Lookup an existing query in the hash table. If the query does not
|
||||
* actually exist, an elog(ERROR) is thrown.
|
||||
* actually exist, throw elog(ERROR) or return NULL per second parameter.
|
||||
*/
|
||||
static QueryHashEntry *
|
||||
FetchQuery(const char *plan_name)
|
||||
PreparedStatement *
|
||||
FetchPreparedStatement(const char *stmt_name, bool throwError)
|
||||
{
|
||||
char key[HASH_KEY_LEN];
|
||||
QueryHashEntry *entry;
|
||||
char key[NAMEDATALEN];
|
||||
PreparedStatement *entry;
|
||||
|
||||
/*
|
||||
* If the hash table hasn't been initialized, it can't be storing
|
||||
* anything, therefore it couldn't possibly store our plan.
|
||||
*/
|
||||
if (!prepared_queries)
|
||||
if (prepared_queries)
|
||||
{
|
||||
/*
|
||||
* We can't just use the statement name as supplied by the user: the
|
||||
* hash package is picky enough that it needs to be NULL-padded out to
|
||||
* the appropriate length to work correctly.
|
||||
*/
|
||||
MemSet(key, 0, sizeof(key));
|
||||
strncpy(key, stmt_name, sizeof(key));
|
||||
|
||||
entry = (PreparedStatement *) hash_search(prepared_queries,
|
||||
key,
|
||||
HASH_FIND,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
entry = NULL;
|
||||
|
||||
if (!entry && throwError)
|
||||
elog(ERROR, "Prepared statement with name \"%s\" does not exist",
|
||||
plan_name);
|
||||
|
||||
/*
|
||||
* We can't just use the statement name as supplied by the user: the
|
||||
* hash package is picky enough that it needs to be NULL-padded out to
|
||||
* the appropriate length to work correctly.
|
||||
*/
|
||||
MemSet(key, 0, sizeof(key));
|
||||
strncpy(key, plan_name, sizeof(key));
|
||||
|
||||
entry = (QueryHashEntry *) hash_search(prepared_queries,
|
||||
key,
|
||||
HASH_FIND,
|
||||
NULL);
|
||||
|
||||
if (!entry)
|
||||
elog(ERROR, "Prepared statement with name \"%s\" does not exist",
|
||||
plan_name);
|
||||
stmt_name);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a plan name, look up the stored plan (giving error if not found).
|
||||
* Look up a prepared statement given the name (giving error if not found).
|
||||
* If found, return the list of argument type OIDs.
|
||||
*/
|
||||
List *
|
||||
FetchQueryParams(const char *plan_name)
|
||||
FetchPreparedStatementParams(const char *stmt_name)
|
||||
{
|
||||
QueryHashEntry *entry;
|
||||
PreparedStatement *entry;
|
||||
|
||||
entry = FetchQuery(plan_name);
|
||||
entry = FetchPreparedStatement(stmt_name, true);
|
||||
|
||||
return entry->argtype_list;
|
||||
}
|
||||
@ -368,20 +400,34 @@ FetchQueryParams(const char *plan_name)
|
||||
void
|
||||
DeallocateQuery(DeallocateStmt *stmt)
|
||||
{
|
||||
QueryHashEntry *entry;
|
||||
DropPreparedStatement(stmt->name, true);
|
||||
}
|
||||
|
||||
/* Find the query's hash table entry */
|
||||
entry = FetchQuery(stmt->name);
|
||||
/*
|
||||
* Internal version of DEALLOCATE
|
||||
*
|
||||
* If showError is false, dropping a nonexistent statement is a no-op.
|
||||
*/
|
||||
void
|
||||
DropPreparedStatement(const char *stmt_name, bool showError)
|
||||
{
|
||||
PreparedStatement *entry;
|
||||
|
||||
/* Drop any open portals that depend on this prepared statement */
|
||||
Assert(MemoryContextIsValid(entry->context));
|
||||
DropDependentPortals(entry->context);
|
||||
/* Find the query's hash table entry; raise error if wanted */
|
||||
entry = FetchPreparedStatement(stmt_name, showError);
|
||||
|
||||
/* Flush the context holding the subsidiary data */
|
||||
MemoryContextDelete(entry->context);
|
||||
if (entry)
|
||||
{
|
||||
/* Drop any open portals that depend on this prepared statement */
|
||||
Assert(MemoryContextIsValid(entry->context));
|
||||
DropDependentPortals(entry->context);
|
||||
|
||||
/* Now we can remove the hash table entry */
|
||||
hash_search(prepared_queries, entry->key, HASH_REMOVE, NULL);
|
||||
/* Flush the context holding the subsidiary data */
|
||||
MemoryContextDelete(entry->context);
|
||||
|
||||
/* Now we can remove the hash table entry */
|
||||
hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -391,7 +437,7 @@ void
|
||||
ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
|
||||
{
|
||||
ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
|
||||
QueryHashEntry *entry;
|
||||
PreparedStatement *entry;
|
||||
List *l,
|
||||
*query_list,
|
||||
*plan_list;
|
||||
@ -402,7 +448,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
|
||||
Assert(execstmt && IsA(execstmt, ExecuteStmt));
|
||||
|
||||
/* Look it up in the hash table */
|
||||
entry = FetchQuery(execstmt->name);
|
||||
entry = FetchPreparedStatement(execstmt->name, true);
|
||||
|
||||
query_list = entry->query_list;
|
||||
plan_list = entry->plan_list;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.54 2003/04/26 20:22:59 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.55 2003/05/05 00:44:56 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -91,10 +91,20 @@ DestToFunction(CommandDest dest)
|
||||
switch (dest)
|
||||
{
|
||||
case Remote:
|
||||
return printtup_create_DR(false);
|
||||
return printtup_create_DR(false, true);
|
||||
|
||||
case RemoteInternal:
|
||||
return printtup_create_DR(true);
|
||||
return printtup_create_DR(true, true);
|
||||
|
||||
case RemoteExecute:
|
||||
/* like Remote, but suppress output of T message */
|
||||
return printtup_create_DR(false, false);
|
||||
|
||||
case RemoteExecuteInternal:
|
||||
return printtup_create_DR(true, false);
|
||||
|
||||
case None:
|
||||
return &donothingDR;
|
||||
|
||||
case Debug:
|
||||
return &debugtupDR;
|
||||
@ -104,9 +114,6 @@ DestToFunction(CommandDest dest)
|
||||
|
||||
case Tuplestore:
|
||||
return tstoreReceiverCreateDR();
|
||||
|
||||
case None:
|
||||
return &donothingDR;
|
||||
}
|
||||
|
||||
/* should never get here */
|
||||
@ -124,13 +131,15 @@ EndCommand(const char *commandTag, CommandDest dest)
|
||||
{
|
||||
case Remote:
|
||||
case RemoteInternal:
|
||||
case RemoteExecute:
|
||||
case RemoteExecuteInternal:
|
||||
pq_puttextmessage('C', commandTag);
|
||||
break;
|
||||
|
||||
case None:
|
||||
case Debug:
|
||||
case Tuplestore:
|
||||
case SPI:
|
||||
case Tuplestore:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -152,8 +161,10 @@ NullCommand(CommandDest dest)
|
||||
{
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
case Remote:
|
||||
case RemoteInternal:
|
||||
case RemoteExecute:
|
||||
case RemoteExecuteInternal:
|
||||
|
||||
/*
|
||||
* tell the fe that we saw an empty query string. In protocols
|
||||
@ -165,10 +176,10 @@ NullCommand(CommandDest dest)
|
||||
pq_puttextmessage('I', "");
|
||||
break;
|
||||
|
||||
case Debug:
|
||||
case Tuplestore:
|
||||
case None:
|
||||
default:
|
||||
case Debug:
|
||||
case SPI:
|
||||
case Tuplestore:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -189,8 +200,10 @@ ReadyForQuery(CommandDest dest)
|
||||
{
|
||||
switch (dest)
|
||||
{
|
||||
case RemoteInternal:
|
||||
case Remote:
|
||||
case RemoteInternal:
|
||||
case RemoteExecute:
|
||||
case RemoteExecuteInternal:
|
||||
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
|
||||
{
|
||||
StringInfoData buf;
|
||||
@ -205,10 +218,10 @@ ReadyForQuery(CommandDest dest)
|
||||
pq_flush();
|
||||
break;
|
||||
|
||||
case Debug:
|
||||
case Tuplestore:
|
||||
case None:
|
||||
default:
|
||||
case Debug:
|
||||
case SPI:
|
||||
case Tuplestore:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.60 2003/05/02 20:54:35 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.61 2003/05/05 00:44:56 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* This cruft is the server side of PQfn.
|
||||
@ -310,6 +310,14 @@ HandleFunctionRequest(StringInfo msgBuf)
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, get_func_name(fid));
|
||||
|
||||
/*
|
||||
* Set up a query snapshot in case function needs one.
|
||||
*/
|
||||
SetQuerySnapshot();
|
||||
|
||||
/*
|
||||
* Prepare function call info block.
|
||||
*/
|
||||
if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
|
||||
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
|
||||
nargs, fip->flinfo.fn_nargs);
|
||||
@ -359,12 +367,8 @@ HandleFunctionRequest(StringInfo msgBuf)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a query snapshot in case function needs one. (It is not safe
|
||||
* to do this if we are in transaction-abort state, so we have to postpone
|
||||
* it till now. Ugh.)
|
||||
*/
|
||||
SetQuerySnapshot();
|
||||
/* Verify we reached the end of the message where expected. */
|
||||
pq_getmsgend(msgBuf);
|
||||
|
||||
#ifdef NO_FASTPATH
|
||||
/* force a NULL return */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/* ----------------
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -375,6 +375,17 @@ connectOptions1(PGconn *conn, const char *conninfo)
|
||||
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
|
||||
*/
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user