aboutsummaryrefslogtreecommitdiff
path: root/src/backend/tcop
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/tcop')
-rw-r--r--src/backend/tcop/dest.c37
-rw-r--r--src/backend/tcop/fastpath.c260
-rw-r--r--src/backend/tcop/postgres.c207
-rw-r--r--src/backend/tcop/pquery.c82
4 files changed, 429 insertions, 157 deletions
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index bce77603f5b..a590cffd35a 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.57 2003/05/06 20:26:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.58 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -30,8 +30,10 @@
#include "access/printtup.h"
#include "access/xact.h"
+#include "executor/tstoreReceiver.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
+#include "utils/portal.h"
/* ----------------
@@ -44,8 +46,7 @@ donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
}
static void
-donothingStartup(DestReceiver *self, int operation,
- const char *portalName, TupleDesc typeinfo, List *targetlist)
+donothingStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
{
}
@@ -90,18 +91,21 @@ BeginCommand(const char *commandTag, CommandDest dest)
/* ----------------
* CreateDestReceiver - return appropriate receiver function set for dest
+ *
+ * Note: a Portal must be specified for destinations Remote, RemoteExecute,
+ * and Tuplestore. It can be NULL for the others.
* ----------------
*/
DestReceiver *
-CreateDestReceiver(CommandDest dest)
+CreateDestReceiver(CommandDest dest, Portal portal)
{
switch (dest)
{
case Remote:
- case RemoteInternal:
case RemoteExecute:
- case RemoteExecuteInternal:
- return printtup_create_DR(dest);
+ if (portal == NULL)
+ elog(ERROR, "CreateDestReceiver: no portal specified");
+ return printtup_create_DR(dest, portal);
case None:
return &donothingDR;
@@ -113,12 +117,13 @@ CreateDestReceiver(CommandDest dest)
return &spi_printtupDR;
case Tuplestore:
- /*
- * This is disallowed, you must use tstoreReceiver.c's
- * specialized function to create a Tuplestore DestReceiver
- */
- elog(ERROR, "CreateDestReceiver: cannot handle Tuplestore");
- break;
+ if (portal == NULL)
+ elog(ERROR, "CreateDestReceiver: no portal specified");
+ if (portal->holdStore == NULL ||
+ portal->holdContext == NULL)
+ elog(ERROR, "CreateDestReceiver: portal has no holdStore");
+ return CreateTuplestoreDestReceiver(portal->holdStore,
+ portal->holdContext);
}
/* should never get here */
@@ -135,9 +140,7 @@ EndCommand(const char *commandTag, CommandDest dest)
switch (dest)
{
case Remote:
- case RemoteInternal:
case RemoteExecute:
- case RemoteExecuteInternal:
pq_puttextmessage('C', commandTag);
break;
@@ -167,9 +170,7 @@ NullCommand(CommandDest dest)
switch (dest)
{
case Remote:
- case RemoteInternal:
case RemoteExecute:
- case RemoteExecuteInternal:
/*
* tell the fe that we saw an empty query string. In protocols
@@ -206,9 +207,7 @@ ReadyForQuery(CommandDest dest)
switch (dest)
{
case Remote:
- case RemoteInternal:
case RemoteExecute:
- case RemoteExecuteInternal:
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
StringInfoData buf;
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 65161c54ff3..0720f4e971d 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.61 2003/05/05 00:44:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.62 2003/05/08 18:16:36 tgl Exp $
*
* NOTES
* This cruft is the server side of PQfn.
@@ -47,6 +47,34 @@
#include "utils/tqual.h"
+/*
+ * Formerly, this code attempted to cache the function and type info
+ * looked up by fetch_fp_info, but only for the duration of a single
+ * transaction command (since in theory the info could change between
+ * commands). This was utterly useless, because postgres.c executes
+ * each fastpath call as a separate transaction command, and so the
+ * cached data could never actually have been reused. If it had worked
+ * as intended, it would have had problems anyway with dangling references
+ * in the FmgrInfo struct. So, forget about caching and just repeat the
+ * syscache fetches on each usage. They're not *that* expensive.
+ */
+struct fp_info
+{
+ Oid funcid;
+ FmgrInfo flinfo; /* function lookup info for funcid */
+ int16 arglen[FUNC_MAX_ARGS];
+ bool argbyval[FUNC_MAX_ARGS];
+ int16 retlen;
+ bool retbyval;
+};
+
+
+static void parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+ FunctionCallInfo fcinfo);
+static void parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+ FunctionCallInfo fcinfo);
+
+
/* ----------------
* GetOldFunctionMessage
*
@@ -121,57 +149,73 @@ SendFunctionResult(Datum retval, bool retbyval, int retlen)
pq_beginmessage(&buf, 'V');
- if (retlen != 0)
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
- pq_sendbyte(&buf, 'G');
- if (retbyval)
- { /* by-value */
- pq_sendint(&buf, retlen, 4);
- pq_sendint(&buf, DatumGetInt32(retval), retlen);
+ /* New-style message */
+ /* XXX replace this with standard binary (or text!) output */
+ if (retlen != 0)
+ {
+ if (retbyval)
+ { /* by-value */
+ pq_sendint(&buf, retlen, 4);
+ pq_sendint(&buf, DatumGetInt32(retval), retlen);
+ }
+ else
+ { /* by-reference ... */
+ if (retlen == -1)
+ { /* ... varlena */
+ struct varlena *v = PG_DETOAST_DATUM(retval);
+
+ pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
+ pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+ }
+ else
+ { /* ... fixed */
+ pq_sendint(&buf, retlen, 4);
+ pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+ }
+ }
}
else
- { /* by-reference ... */
- if (retlen == -1)
- { /* ... varlena */
- struct varlena *v = PG_DETOAST_DATUM(retval);
-
- pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
- pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+ {
+ /* NULL marker */
+ pq_sendint(&buf, -1, 4);
+ }
+ }
+ else
+ {
+ /* Old-style message */
+ if (retlen != 0)
+ {
+ pq_sendbyte(&buf, 'G');
+ if (retbyval)
+ { /* by-value */
+ pq_sendint(&buf, retlen, 4);
+ pq_sendint(&buf, DatumGetInt32(retval), retlen);
}
else
- { /* ... fixed */
- pq_sendint(&buf, retlen, 4);
- pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+ { /* by-reference ... */
+ if (retlen == -1)
+ { /* ... varlena */
+ struct varlena *v = PG_DETOAST_DATUM(retval);
+
+ pq_sendint(&buf, VARSIZE(v) - VARHDRSZ, VARHDRSZ);
+ pq_sendbytes(&buf, VARDATA(v), VARSIZE(v) - VARHDRSZ);
+ }
+ else
+ { /* ... fixed */
+ pq_sendint(&buf, retlen, 4);
+ pq_sendbytes(&buf, DatumGetPointer(retval), retlen);
+ }
}
}
+ pq_sendbyte(&buf, '0');
}
- pq_sendbyte(&buf, '0');
pq_endmessage(&buf);
}
/*
- * Formerly, this code attempted to cache the function and type info
- * looked up by fetch_fp_info, but only for the duration of a single
- * transaction command (since in theory the info could change between
- * commands). This was utterly useless, because postgres.c executes
- * each fastpath call as a separate transaction command, and so the
- * cached data could never actually have been reused. If it had worked
- * as intended, it would have had problems anyway with dangling references
- * in the FmgrInfo struct. So, forget about caching and just repeat the
- * syscache fetches on each usage. They're not *that* expensive.
- */
-struct fp_info
-{
- Oid funcid;
- FmgrInfo flinfo; /* function lookup info for funcid */
- int16 arglen[FUNC_MAX_ARGS];
- bool argbyval[FUNC_MAX_ARGS];
- int16 retlen;
- bool retbyval;
-};
-
-/*
* fetch_fp_info
*
* Performs catalog lookups to load a struct fp_info 'fip' for the
@@ -262,11 +306,9 @@ int
HandleFunctionRequest(StringInfo msgBuf)
{
Oid fid;
- int nargs;
AclResult aclresult;
FunctionCallInfoData fcinfo;
Datum retval;
- int i;
struct fp_info my_fp;
struct fp_info *fip;
@@ -294,9 +336,10 @@ HandleFunctionRequest(StringInfo msgBuf)
/*
* Parse the buffer contents.
*/
- (void) pq_getmsgstring(msgBuf); /* dummy string */
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ (void) pq_getmsgstring(msgBuf); /* dummy string */
+
fid = (Oid) pq_getmsgint(msgBuf, 4); /* function oid */
- nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */
/*
* There used to be a lame attempt at caching lookup info here. Now we
@@ -316,19 +359,61 @@ HandleFunctionRequest(StringInfo msgBuf)
SetQuerySnapshot();
/*
- * Prepare function call info block.
+ * Prepare function call info block and insert arguments.
*/
+ MemSet(&fcinfo, 0, sizeof(fcinfo));
+ fcinfo.flinfo = &fip->flinfo;
+
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ parse_fcall_arguments(msgBuf, fip, &fcinfo);
+ else
+ parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
+
+ /* Verify we reached the end of the message where expected. */
+ pq_getmsgend(msgBuf);
+
+ /* Okay, do it ... */
+ retval = FunctionCallInvoke(&fcinfo);
+
+ if (fcinfo.isnull)
+ SendFunctionResult(retval, fip->retbyval, 0);
+ else
+ SendFunctionResult(retval, fip->retbyval, fip->retlen);
+
+ return 0;
+}
+
+/*
+ * Parse function arguments in a 3.0 protocol message
+ */
+static void
+parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
+ FunctionCallInfo fcinfo)
+{
+ int nargs;
+ int i;
+ int numAFormats;
+ int16 *aformats = NULL;
+
+ /* Get the argument format codes */
+ numAFormats = pq_getmsgint(msgBuf, 2);
+ if (numAFormats > 0)
+ {
+ aformats = (int16 *) palloc(numAFormats * sizeof(int16));
+ for (i = 0; i < numAFormats; i++)
+ aformats[i] = pq_getmsgint(msgBuf, 2);
+ }
+
+ nargs = pq_getmsgint(msgBuf, 2); /* # of arguments */
+
if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
nargs, fip->flinfo.fn_nargs);
- MemSet(&fcinfo, 0, sizeof(fcinfo));
- fcinfo.flinfo = &fip->flinfo;
- fcinfo.nargs = nargs;
+ fcinfo->nargs = nargs;
/*
- * Copy supplied arguments into arg vector. Note there is no way for
- * frontend to specify a NULL argument --- this protocol is misdesigned.
+ * Copy supplied arguments into arg vector.
*/
for (i = 0; i < nargs; ++i)
{
@@ -342,7 +427,7 @@ HandleFunctionRequest(StringInfo msgBuf)
elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
argsize);
/* XXX should we demand argsize == fip->arglen[i] ? */
- fcinfo.arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
+ fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
}
else
{ /* by-reference ... */
@@ -363,25 +448,70 @@ HandleFunctionRequest(StringInfo msgBuf)
p = palloc(argsize + 1); /* +1 in case argsize is 0 */
pq_copymsgbytes(msgBuf, p, argsize);
}
- fcinfo.arg[i] = PointerGetDatum(p);
+ fcinfo->arg[i] = PointerGetDatum(p);
}
}
- /* Verify we reached the end of the message where expected. */
- pq_getmsgend(msgBuf);
+ /* XXX for the moment, ignore result format code */
+ (void) pq_getmsgint(msgBuf, 2);
+}
-#ifdef NO_FASTPATH
- /* force a NULL return */
- retval = (Datum) 0;
- fcinfo.isnull = true;
-#else
- retval = FunctionCallInvoke(&fcinfo);
-#endif /* NO_FASTPATH */
+/*
+ * Parse function arguments in a 2.0 protocol message
+ */
+static void
+parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
+ FunctionCallInfo fcinfo)
+{
+ int nargs;
+ int i;
- if (fcinfo.isnull)
- SendFunctionResult(retval, fip->retbyval, 0);
- else
- SendFunctionResult(retval, fip->retbyval, fip->retlen);
+ nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */
- return 0;
+ if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
+ elog(ERROR, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
+ nargs, fip->flinfo.fn_nargs);
+
+ fcinfo->nargs = nargs;
+
+ /*
+ * Copy supplied arguments into arg vector. Note there is no way for
+ * frontend to specify a NULL argument --- this protocol is misdesigned.
+ */
+ for (i = 0; i < nargs; ++i)
+ {
+ int argsize;
+ char *p;
+
+ argsize = pq_getmsgint(msgBuf, 4);
+ if (fip->argbyval[i])
+ { /* by-value */
+ if (argsize < 1 || argsize > 4)
+ elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+ argsize);
+ /* XXX should we demand argsize == fip->arglen[i] ? */
+ fcinfo->arg[i] = (Datum) pq_getmsgint(msgBuf, argsize);
+ }
+ else
+ { /* by-reference ... */
+ if (fip->arglen[i] == -1)
+ { /* ... varlena */
+ if (argsize < 0)
+ elog(ERROR, "HandleFunctionRequest: bogus argsize %d",
+ argsize);
+ p = palloc(argsize + VARHDRSZ);
+ VARATT_SIZEP(p) = argsize + VARHDRSZ;
+ pq_copymsgbytes(msgBuf, VARDATA(p), argsize);
+ }
+ else
+ { /* ... fixed */
+ if (argsize != fip->arglen[i])
+ elog(ERROR, "HandleFunctionRequest: bogus argsize %d, should be %d",
+ argsize, fip->arglen[i]);
+ p = palloc(argsize + 1); /* +1 in case argsize is 0 */
+ pq_copymsgbytes(msgBuf, p, argsize);
+ }
+ fcinfo->arg[i] = PointerGetDatum(p);
+ }
+ }
}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 89564558708..63b08dc969e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.339 2003/05/08 14:49:04 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.340 2003/05/08 18:16:36 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -658,7 +658,6 @@ static void
exec_simple_query(const char *query_string)
{
CommandDest dest = whereToSendOutput;
- DestReceiver *receiver;
MemoryContext oldcontext;
List *parsetree_list,
*parsetree_item;
@@ -686,12 +685,6 @@ exec_simple_query(const char *query_string)
ResetUsage();
/*
- * Create destination receiver object --- we can reuse it for all
- * queries in the string. Note it is created in MessageContext.
- */
- receiver = CreateDestReceiver(dest);
-
- /*
* Start up a transaction command. All queries generated by the
* query_string will be in this same command block, *unless* we find a
* BEGIN/COMMIT/ABORT statement; we have to force a new xact command
@@ -743,6 +736,8 @@ exec_simple_query(const char *query_string)
List *querytree_list,
*plantree_list;
Portal portal;
+ DestReceiver *receiver;
+ int16 format;
/*
* Get the command name for use in status display (it also becomes the
@@ -804,11 +799,6 @@ exec_simple_query(const char *query_string)
CHECK_FOR_INTERRUPTS();
/*
- * Switch back to transaction context for execution.
- */
- MemoryContextSwitchTo(oldcontext);
-
- /*
* Create unnamed portal to run the query or queries in.
* If there already is one, silently drop it.
*/
@@ -822,16 +812,53 @@ exec_simple_query(const char *query_string)
MessageContext);
/*
- * Run the portal to completion, and then drop it.
+ * Start the portal. No parameters here.
*/
PortalStart(portal, NULL);
+ /*
+ * Select the appropriate output format: text unless we are doing
+ * a FETCH from a binary cursor. (Pretty grotty to have to do this
+ * here --- but it avoids grottiness in other places. Ah, the joys
+ * of backward compatibility...)
+ */
+ format = 0; /* TEXT is default */
+ if (IsA(parsetree, FetchStmt))
+ {
+ FetchStmt *stmt = (FetchStmt *) parsetree;
+
+ if (!stmt->ismove)
+ {
+ Portal fportal = GetPortalByName(stmt->portalname);
+
+ if (PortalIsValid(fportal) &&
+ (fportal->cursorOptions & CURSOR_OPT_BINARY))
+ format = 1; /* BINARY */
+ }
+ }
+ PortalSetResultFormat(portal, 1, &format);
+
+ /*
+ * Now we can create the destination receiver object.
+ */
+ receiver = CreateDestReceiver(dest, portal);
+
+ /*
+ * Switch back to transaction context for execution.
+ */
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * Run the portal to completion, and then drop it (and the receiver).
+ */
(void) PortalRun(portal,
FETCH_ALL,
receiver,
receiver,
completionTag);
+ (*receiver->destroy) (receiver);
+
PortalDrop(portal, false);
@@ -886,8 +913,6 @@ exec_simple_query(const char *query_string)
if (!parsetree_list)
NullCommand(dest);
- (*receiver->destroy) (receiver);
-
QueryContext = NULL;
/*
@@ -1156,8 +1181,12 @@ exec_bind_message(StringInfo input_message)
{
const char *portal_name;
const char *stmt_name;
- int is_binary;
+ int numPFormats;
+ int16 *pformats = NULL;
int numParams;
+ int numRFormats;
+ int16 *rformats = NULL;
+ int i;
PreparedStatement *pstmt;
Portal portal;
ParamListInfo params;
@@ -1173,14 +1202,28 @@ exec_bind_message(StringInfo input_message)
*/
start_xact_command();
+ /* Switch back to message context */
+ MemoryContextSwitchTo(MessageContext);
+
/* Get the fixed part of the message */
portal_name = pq_getmsgstring(input_message);
stmt_name = pq_getmsgstring(input_message);
- is_binary = pq_getmsgbyte(input_message);
- numParams = pq_getmsgint(input_message, 4);
- if (is_binary)
- elog(ERROR, "Binary BIND not implemented yet");
+ /* Get the parameter format codes */
+ numPFormats = pq_getmsgint(input_message, 2);
+ if (numPFormats > 0)
+ {
+ pformats = (int16 *) palloc(numPFormats * sizeof(int16));
+ for (i = 0; i < numPFormats; i++)
+ pformats[i] = pq_getmsgint(input_message, 2);
+ }
+
+ /* Get the parameter value count */
+ numParams = pq_getmsgint(input_message, 2);
+
+ if (numPFormats > 1 && numPFormats != numParams)
+ elog(ERROR, "BIND message has %d parameter formats but %d parameters",
+ numPFormats, numParams);
/* Find prepared statement */
if (stmt_name[0] != '\0')
@@ -1217,45 +1260,98 @@ exec_bind_message(StringInfo input_message)
* Fetch parameters, if any, and store in the portal's memory context.
*
* In an aborted transaction, we can't risk calling user-defined functions,
- * so bind all parameters to null values.
+ * but we can't fail to Bind either, so bind all parameters to null values.
*/
if (numParams > 0)
{
bool isaborted = IsAbortedTransactionBlockState();
- int i = 0;
+ StringInfoData pbuf;
List *l;
MemoryContext oldContext;
+ /* Note that the string buffer lives in MessageContext */
+ initStringInfo(&pbuf);
+
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
params = (ParamListInfo)
palloc0((numParams + 1) * sizeof(ParamListInfoData));
+ i = 0;
foreach(l, pstmt->argtype_list)
{
Oid ptype = lfirsto(l);
+ int32 plength;
bool isNull;
- isNull = (pq_getmsgbyte(input_message) != 0) ? false : true;
+ plength = pq_getmsgint(input_message, 4);
+ isNull = (plength == -1);
+
if (!isNull)
{
- const char *ptext = pq_getmsgstring(input_message);
+ /* Reset pbuf to empty, and insert raw data into it */
+ pbuf.len = 0;
+ pbuf.data[0] = '\0';
+ pbuf.cursor = 0;
+
+ appendBinaryStringInfo(&pbuf,
+ pq_getmsgbytes(input_message, plength),
+ plength);
if (isaborted)
+ {
+ /* We don't bother to check the format in this case */
isNull = true;
+ }
else
{
- Oid typInput;
- Oid typElem;
-
- getTypeInputInfo(ptype, &typInput, &typElem);
- params[i].value =
- OidFunctionCall3(typInput,
- CStringGetDatum(ptext),
- ObjectIdGetDatum(typElem),
- Int32GetDatum(-1));
+ int16 pformat;
+
+ if (numPFormats > 1)
+ pformat = pformats[i];
+ else if (numPFormats > 0)
+ pformat = pformats[0];
+ else
+ pformat = 0; /* default = text */
+
+ if (pformat == 0)
+ {
+ Oid typInput;
+ Oid typElem;
+ char *pstring;
+
+ getTypeInputInfo(ptype, &typInput, &typElem);
+ /*
+ * Since stringinfo.c keeps a trailing null in
+ * place even for binary data, the contents of
+ * pbuf are a valid C string. We have to do
+ * encoding conversion before calling the typinput
+ * routine, though.
+ */
+ pstring = (char *)
+ pg_client_to_server((unsigned char *) pbuf.data,
+ plength);
+ params[i].value =
+ OidFunctionCall3(typInput,
+ CStringGetDatum(pstring),
+ ObjectIdGetDatum(typElem),
+ Int32GetDatum(-1));
+ /* Free result of encoding conversion, if any */
+ if (pstring != pbuf.data)
+ pfree(pstring);
+ }
+ else if (pformat == 1)
+ {
+ /* XXX something similar to above */
+ elog(ERROR, "Binary BIND not implemented yet");
+ }
+ else
+ {
+ elog(ERROR, "Invalid format code %d", pformat);
+ }
}
}
+
params[i].kind = PARAM_NUM;
params[i].id = i + 1;
params[i].isnull = isNull;
@@ -1270,6 +1366,15 @@ exec_bind_message(StringInfo input_message)
else
params = NULL;
+ /* Get the result format codes */
+ numRFormats = pq_getmsgint(input_message, 2);
+ if (numRFormats > 0)
+ {
+ rformats = (int16 *) palloc(numRFormats * sizeof(int16));
+ for (i = 0; i < numRFormats; i++)
+ rformats[i] = pq_getmsgint(input_message, 2);
+ }
+
pq_getmsgend(input_message);
/*
@@ -1278,6 +1383,11 @@ exec_bind_message(StringInfo input_message)
PortalStart(portal, params);
/*
+ * Apply the result format requests to the portal.
+ */
+ PortalSetResultFormat(portal, numRFormats, rformats);
+
+ /*
* Send BindComplete.
*/
if (whereToSendOutput == Remote)
@@ -1290,7 +1400,7 @@ exec_bind_message(StringInfo input_message)
* Process an "Execute" message for a portal
*/
static void
-exec_execute_message(const char *portal_name, int is_binary, long max_rows)
+exec_execute_message(const char *portal_name, long max_rows)
{
CommandDest dest;
DestReceiver *receiver;
@@ -1303,7 +1413,7 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
/* Adjust destination to tell printtup.c what to do */
dest = whereToSendOutput;
if (dest == Remote)
- dest = is_binary ? RemoteExecuteInternal : RemoteExecute;
+ dest = RemoteExecute;
portal = GetPortalByName(portal_name);
if (!PortalIsValid(portal))
@@ -1353,6 +1463,12 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
}
/*
+ * Create dest receiver in MessageContext (we don't want it in transaction
+ * context, because that may get deleted if portal contains VACUUM).
+ */
+ receiver = CreateDestReceiver(dest, portal);
+
+ /*
* Ensure we are in a transaction command (this should normally be
* the case already due to prior BIND).
*/
@@ -1375,8 +1491,6 @@ exec_execute_message(const char *portal_name, int is_binary, long max_rows)
/*
* Okay to run the portal.
*/
- receiver = CreateDestReceiver(dest);
-
if (max_rows <= 0)
max_rows = FETCH_ALL;
@@ -1451,7 +1565,7 @@ exec_describe_statement_message(const char *stmt_name)
* First describe the parameters...
*/
pq_beginmessage(&buf, 't'); /* parameter description message type */
- pq_sendint(&buf, length(pstmt->argtype_list), 4);
+ pq_sendint(&buf, length(pstmt->argtype_list), 2);
foreach(l, pstmt->argtype_list)
{
@@ -1473,7 +1587,7 @@ exec_describe_statement_message(const char *stmt_name)
targetlist = ((Query *) lfirst(pstmt->query_list))->targetList;
else
targetlist = NIL;
- SendRowDescriptionMessage(tupdesc, targetlist);
+ SendRowDescriptionMessage(tupdesc, targetlist, NULL);
}
else
pq_putemptymessage('n'); /* NoData */
@@ -1502,10 +1616,11 @@ exec_describe_portal_message(const char *portal_name)
List *targetlist;
if (portal->strategy == PORTAL_ONE_SELECT)
- targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
+ targetlist = ((Query *) lfirst(portal->parseTrees))->targetList;
else
targetlist = NIL;
- SendRowDescriptionMessage(portal->tupDesc, targetlist);
+ SendRowDescriptionMessage(portal->tupDesc, targetlist,
+ portal->formats);
}
else
pq_putemptymessage('n'); /* NoData */
@@ -2397,7 +2512,7 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.339 $ $Date: 2003/05/08 14:49:04 $\n");
+ puts("$Revision: 1.340 $ $Date: 2003/05/08 18:16:36 $\n");
}
/*
@@ -2613,7 +2728,7 @@ PostgresMain(int argc, char *argv[], const char *username)
stmt_name = pq_getmsgstring(input_message);
query_string = pq_getmsgstring(input_message);
- numParams = pq_getmsgint(input_message, 4);
+ numParams = pq_getmsgint(input_message, 2);
if (numParams > 0)
{
int i;
@@ -2640,15 +2755,13 @@ PostgresMain(int argc, char *argv[], const char *username)
case 'E': /* execute */
{
const char *portal_name;
- int is_binary;
int max_rows;
portal_name = pq_getmsgstring(input_message);
- is_binary = pq_getmsgbyte(input_message);
max_rows = pq_getmsgint(input_message, 4);
pq_getmsgend(input_message);
- exec_execute_message(portal_name, is_binary, max_rows);
+ exec_execute_message(portal_name, max_rows);
}
break;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index e3a37b7310e..bf63dcbc29c 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,14 +8,13 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.63 2003/05/06 21:01:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.64 2003/05/08 18:16:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "executor/executor.h"
-#include "executor/tstoreReceiver.h"
#include "miscadmin.h"
#include "tcop/tcopprot.h"
#include "tcop/pquery.h"
@@ -47,7 +46,6 @@ QueryDesc *
CreateQueryDesc(Query *parsetree,
Plan *plantree,
DestReceiver *dest,
- const char *portalName,
ParamListInfo params,
bool doInstrument)
{
@@ -57,7 +55,6 @@ CreateQueryDesc(Query *parsetree,
qd->parsetree = parsetree; /* parse tree */
qd->plantree = plantree; /* plan */
qd->dest = dest; /* output dest */
- qd->portalName = portalName; /* name, if dest is a portal */
qd->params = params; /* parameter values passed into query */
qd->doInstrument = doInstrument; /* instrumentation wanted? */
@@ -89,7 +86,6 @@ FreeQueryDesc(QueryDesc *qdesc)
* parsetree: the query tree
* plan: the plan tree for the query
* params: any parameters needed
- * portalName: name of portal being used
* dest: where to send results
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
* in which to store a command completion status string.
@@ -103,7 +99,6 @@ void
ProcessQuery(Query *parsetree,
Plan *plan,
ParamListInfo params,
- const char *portalName,
DestReceiver *dest,
char *completionTag)
{
@@ -131,8 +126,7 @@ ProcessQuery(Query *parsetree,
/*
* Create the QueryDesc object
*/
- queryDesc = CreateQueryDesc(parsetree, plan, dest, portalName, params,
- false);
+ queryDesc = CreateQueryDesc(parsetree, plan, dest, params, false);
/*
* Call ExecStart to prepare the plan for execution
@@ -269,7 +263,6 @@ PortalStart(Portal portal, ParamListInfo params)
queryDesc = CreateQueryDesc((Query *) lfirst(portal->parseTrees),
(Plan *) lfirst(portal->planTrees),
None_Receiver,
- portal->name,
params,
false);
/*
@@ -281,7 +274,7 @@ PortalStart(Portal portal, ParamListInfo params)
*/
portal->queryDesc = queryDesc;
/*
- * Remember tuple descriptor
+ * Remember tuple descriptor (computed by ExecutorStart)
*/
portal->tupDesc = queryDesc->tupDesc;
/*
@@ -321,6 +314,53 @@ PortalStart(Portal portal, ParamListInfo params)
}
/*
+ * PortalSetResultFormat
+ * Select the format codes for a portal's output.
+ *
+ * This must be run after PortalStart for a portal that will be read by
+ * a Remote or RemoteExecute destination. It is not presently needed for
+ * other destination types.
+ *
+ * formats[] is the client format request, as per Bind message conventions.
+ */
+void
+PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
+{
+ int natts;
+ int i;
+
+ /* Do nothing if portal won't return tuples */
+ if (portal->tupDesc == NULL)
+ return;
+ natts = portal->tupDesc->natts;
+ /* +1 avoids palloc(0) if no columns */
+ portal->formats = (int16 *)
+ MemoryContextAlloc(PortalGetHeapMemory(portal),
+ (natts + 1) * sizeof(int16));
+ if (nFormats > 1)
+ {
+ /* format specified for each column */
+ if (nFormats != natts)
+ elog(ERROR, "BIND message has %d result formats but query has %d columns",
+ nFormats, natts);
+ memcpy(portal->formats, formats, natts * sizeof(int16));
+ } else if (nFormats > 0)
+ {
+ /* single format specified, use for all columns */
+ int16 format1 = formats[0];
+
+ for (i = 0; i < natts; i++)
+ portal->formats[i] = format1;
+ }
+ else
+ {
+ /* use default format for all columns */
+ for (i = 0; i < natts; i++)
+ portal->formats[i] = 0;
+ }
+}
+
+/*
* PortalRun
* Run a portal's query or queries.
*
@@ -399,8 +439,7 @@ PortalRun(Portal portal, long count,
DestReceiver *treceiver;
PortalCreateHoldStore(portal);
- treceiver = CreateTuplestoreDestReceiver(portal->holdStore,
- portal->holdContext);
+ treceiver = CreateDestReceiver(Tuplestore, portal);
PortalRunUtility(portal, lfirst(portal->parseTrees),
treceiver, NULL);
(*treceiver->destroy) (treceiver);
@@ -604,16 +643,9 @@ static uint32
RunFromStore(Portal portal, ScanDirection direction, long count,
DestReceiver *dest)
{
- List *targetlist;
long current_tuple_count = 0;
- if (portal->strategy == PORTAL_ONE_SELECT)
- targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
- else
- targetlist = NIL;
-
- (*dest->startup) (dest, CMD_SELECT, portal->name, portal->tupDesc,
- targetlist);
+ (*dest->startup) (dest, CMD_SELECT, portal->tupDesc);
if (direction == NoMovementScanDirection)
{
@@ -737,11 +769,9 @@ PortalRunMulti(Portal portal,
* but the results will be discarded unless you use "simple Query"
* protocol.
*/
- if (dest->mydest == RemoteExecute ||
- dest->mydest == RemoteExecuteInternal)
+ if (dest->mydest == RemoteExecute)
dest = None_Receiver;
- if (altdest->mydest == RemoteExecute ||
- altdest->mydest == RemoteExecuteInternal)
+ if (altdest->mydest == RemoteExecute)
altdest = None_Receiver;
/*
@@ -791,14 +821,14 @@ PortalRunMulti(Portal portal,
{
/* statement can set tag string */
ProcessQuery(query, plan,
- portal->portalParams, portal->name,
+ portal->portalParams,
dest, completionTag);
}
else
{
/* stmt added by rewrite cannot set tag */
ProcessQuery(query, plan,
- portal->portalParams, portal->name,
+ portal->portalParams,
altdest, NULL);
}