summaryrefslogtreecommitdiff
path: root/src/backend/tcop/fastpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/tcop/fastpath.c')
-rw-r--r--src/backend/tcop/fastpath.c260
1 files changed, 195 insertions, 65 deletions
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);
+ }
+ }
}