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">
|
<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
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue