postgres/src/backend/tcop/fastpath.c

616 lines
16 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* fastpath.c
* routines to handle function requests from the frontend
*
2017-01-03 13:48:53 -05:00
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
2010-09-20 22:08:53 +02:00
* src/backend/tcop/fastpath.c
*
* NOTES
* This cruft is the server side of PQfn.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_proc.h"
1999-07-16 05:00:38 +00:00
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
1999-07-16 05:00:38 +00:00
#include "tcop/fastpath.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/snapmgr.h"
1999-07-16 05:00:38 +00:00
#include "utils/syscache.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 */
Oid namespace; /* other stuff from pg_proc */
Oid rettype;
Oid argtypes[FUNC_MAX_ARGS];
Phase 2 of pgindent updates. Change pg_bsd_indent to follow upstream rules for placement of comments to the right of code, and remove pgindent hack that caused comments following #endif to not obey the general rule. Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using the published version of pg_bsd_indent, but a hacked-up version that tried to minimize the amount of movement of comments to the right of code. The situation of interest is where such a comment has to be moved to the right of its default placement at column 33 because there's code there. BSD indent has always moved right in units of tab stops in such cases --- but in the previous incarnation, indent was working in 8-space tab stops, while now it knows we use 4-space tabs. So the net result is that in about half the cases, such comments are placed one tab stop left of before. This is better all around: it leaves more room on the line for comment text, and it means that in such cases the comment uniformly starts at the next 4-space tab stop after the code, rather than sometimes one and sometimes two tabs after. Also, ensure that comments following #endif are indented the same as comments following other preprocessor commands such as #else. That inconsistency turns out to have been self-inflicted damage from a poorly-thought-through post-indent "fixup" in pgindent. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
char fname[NAMEDATALEN]; /* function name for logging */
};
2017-06-21 14:39:04 -04:00
static int16 parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
2003-08-04 00:43:34 +00:00
FunctionCallInfo fcinfo);
2017-06-21 14:39:04 -04:00
static int16 parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
2003-08-04 00:43:34 +00:00
FunctionCallInfo fcinfo);
/* ----------------
* GetOldFunctionMessage
*
* In pre-3.0 protocol, there is no length word on the message, so we have
* to have code that understands the message layout to absorb the message
* into a buffer. We want to do this before we start execution, so that
* we do not lose sync with the frontend if there's an error.
*
* The caller should already have initialized buf to empty.
* ----------------
*/
Be more careful to not lose sync in the FE/BE protocol. If any error occurred while we were in the middle of reading a protocol message from the client, we could lose sync, and incorrectly try to interpret a part of another message as a new protocol message. That will usually lead to an "invalid frontend message" error that terminates the connection. However, this is a security issue because an attacker might be able to deliberately cause an error, inject a Query message in what's supposed to be just user data, and have the server execute it. We were quite careful to not have CHECK_FOR_INTERRUPTS() calls or other operations that could ereport(ERROR) in the middle of processing a message, but a query cancel interrupt or statement timeout could nevertheless cause it to happen. Also, the V2 fastpath and COPY handling were not so careful. It's very difficult to recover in the V2 COPY protocol, so we will just terminate the connection on error. In practice, that's what happened previously anyway, as we lost protocol sync. To fix, add a new variable in pqcomm.c, PqCommReadingMsg, that is set whenever we're in the middle of reading a message. When it's set, we cannot safely ERROR out and continue running, because we might've read only part of a message. PqCommReadingMsg acts somewhat similarly to critical sections in that if an error occurs while it's set, the error handler will force the connection to be terminated, as if the error was FATAL. It's not implemented by promoting ERROR to FATAL in elog.c, like ERROR is promoted to PANIC in critical sections, because we want to be able to use PG_TRY/CATCH to recover and regain protocol sync. pq_getmessage() takes advantage of that to prevent an OOM error from terminating the connection. To prevent unnecessary connection terminations, add a holdoff mechanism similar to HOLD/RESUME_INTERRUPTS() that can be used hold off query cancel interrupts, but still allow die interrupts. The rules on which interrupts are processed when are now a bit more complicated, so refactor ProcessInterrupts() and the calls to it in signal handlers so that the signal handlers always call it if ImmediateInterruptOK is set, and ProcessInterrupts() can decide to not do anything if the other conditions are not met. Reported by Emil Lenngren. Patch reviewed by Noah Misch and Andres Freund. Backpatch to all supported versions. Security: CVE-2015-0244
2015-02-02 17:08:45 +02:00
int
GetOldFunctionMessage(StringInfo buf)
{
int32 ibuf;
int nargs;
/* Dummy string argument */
if (pq_getstring(buf))
return EOF;
/* Function OID */
if (pq_getbytes((char *) &ibuf, 4))
return EOF;
appendBinaryStringInfo(buf, (char *) &ibuf, 4);
/* Number of arguments */
if (pq_getbytes((char *) &ibuf, 4))
return EOF;
appendBinaryStringInfo(buf, (char *) &ibuf, 4);
nargs = ntohl(ibuf);
/* For each argument ... */
while (nargs-- > 0)
{
int argsize;
/* argsize */
if (pq_getbytes((char *) &ibuf, 4))
return EOF;
appendBinaryStringInfo(buf, (char *) &ibuf, 4);
argsize = ntohl(ibuf);
if (argsize < -1)
{
/* FATAL here since no hope of regaining message sync */
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2005-10-15 02:49:52 +00:00
errmsg("invalid argument size %d in function call message",
argsize)));
}
/* and arg contents */
if (argsize > 0)
{
/* Allocate space for arg */
enlargeStringInfo(buf, argsize);
/* And grab it */
if (pq_getbytes(buf->data + buf->len, argsize))
return EOF;
buf->len += argsize;
/* Place a trailing null per StringInfo convention */
buf->data[buf->len] = '\0';
}
}
return 0;
}
/* ----------------
* SendFunctionResult
*
* Note: although this routine doesn't check, the format had better be 1
* (binary) when talking to a pre-3.0 client.
* ----------------
*/
static void
SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
{
bool newstyle = (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3);
StringInfoData buf;
pq_beginmessage(&buf, 'V');
if (isnull)
{
if (newstyle)
pq_sendint(&buf, -1, 4);
}
else
{
if (!newstyle)
pq_sendbyte(&buf, 'G');
if (format == 0)
{
Oid typoutput;
bool typisvarlena;
char *outputstr;
getTypeOutputInfo(rettype, &typoutput, &typisvarlena);
outputstr = OidOutputFunctionCall(typoutput, retval);
pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
pfree(outputstr);
}
else if (format == 1)
{
Oid typsend;
bool typisvarlena;
bytea *outputbytes;
getTypeBinaryOutputInfo(rettype, &typsend, &typisvarlena);
outputbytes = OidSendFunctionCall(typsend, retval);
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
pq_sendbytes(&buf, VARDATA(outputbytes),
VARSIZE(outputbytes) - VARHDRSZ);
pfree(outputbytes);
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unsupported format code: %d", format)));
}
if (!newstyle)
pq_sendbyte(&buf, '0');
pq_endmessage(&buf);
}
/*
* fetch_fp_info
*
* Performs catalog lookups to load a struct fp_info 'fip' for the
* function 'func_id'.
*/
static void
2017-06-21 14:39:04 -04:00
fetch_fp_info(Oid func_id, struct fp_info *fip)
{
HeapTuple func_htp;
Form_pg_proc pp;
Assert(OidIsValid(func_id));
Assert(fip != NULL);
/*
* Since the validity of this structure is determined by whether the
* funcid is OK, we clear the funcid here. It must not be set to the
2005-10-15 02:49:52 +00:00
* correct value until we are about to return with a good struct fp_info,
* since we can be interrupted (i.e., with an ereport(ERROR, ...)) at any
* time. [No longer really an issue since we don't save the struct
* fp_info across transactions anymore, but keep it anyway.]
*/
MemSet(fip, 0, sizeof(struct fp_info));
fip->funcid = InvalidOid;
fmgr_info(func_id, &fip->flinfo);
func_htp = SearchSysCache1(PROCOID, ObjectIdGetDatum(func_id));
if (!HeapTupleIsValid(func_htp))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function with OID %u does not exist", func_id)));
pp = (Form_pg_proc) GETSTRUCT(func_htp);
/* watch out for catalog entries with more than FUNC_MAX_ARGS args */
if (pp->pronargs > FUNC_MAX_ARGS)
elog(ERROR, "function %s has more than %d arguments",
NameStr(pp->proname), FUNC_MAX_ARGS);
fip->namespace = pp->pronamespace;
fip->rettype = pp->prorettype;
memcpy(fip->argtypes, pp->proargtypes.values, pp->pronargs * sizeof(Oid));
strlcpy(fip->fname, NameStr(pp->proname), NAMEDATALEN);
ReleaseSysCache(func_htp);
/*
* This must be last!
*/
fip->funcid = func_id;
}
/*
* HandleFunctionRequest
*
* Server side of PQfn (fastpath function calls from the frontend).
* This corresponds to the libpq protocol symbol "F".
*
* INPUT:
* postgres.c has already read the message body and will pass it in
* msgBuf.
*
* Note: palloc()s done here and in the called function do not need to be
* cleaned up explicitly. We are called from PostgresMain() in the
* MessageContext memory context, which will be automatically reset when
* control returns to PostgresMain.
*/
void
HandleFunctionRequest(StringInfo msgBuf)
{
Oid fid;
AclResult aclresult;
FunctionCallInfoData fcinfo;
int16 rformat;
Datum retval;
struct fp_info my_fp;
struct fp_info *fip;
bool callit;
bool was_logged = false;
char msec_str[32];
/*
* We only accept COMMIT/ABORT if we are in an aborted transaction, and
* COMMIT/ABORT cannot be executed through the fastpath interface.
*/
if (IsAbortedTransactionBlockState())
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
2005-10-15 02:49:52 +00:00
"commands ignored until end of transaction block")));
/*
2006-10-04 00:30:14 +00:00
* Now that we know we are in a valid transaction, set snapshot in case
* needed by function itself or one of the datatype I/O routines.
*/
PushActiveSnapshot(GetTransactionSnapshot());
/*
* Begin parsing the buffer contents.
*/
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
2006-10-04 00:30:14 +00:00
(void) pq_getmsgstring(msgBuf); /* dummy string */
Phase 2 of pgindent updates. Change pg_bsd_indent to follow upstream rules for placement of comments to the right of code, and remove pgindent hack that caused comments following #endif to not obey the general rule. Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using the published version of pg_bsd_indent, but a hacked-up version that tried to minimize the amount of movement of comments to the right of code. The situation of interest is where such a comment has to be moved to the right of its default placement at column 33 because there's code there. BSD indent has always moved right in units of tab stops in such cases --- but in the previous incarnation, indent was working in 8-space tab stops, while now it knows we use 4-space tabs. So the net result is that in about half the cases, such comments are placed one tab stop left of before. This is better all around: it leaves more room on the line for comment text, and it means that in such cases the comment uniformly starts at the next 4-space tab stop after the code, rather than sometimes one and sometimes two tabs after. Also, ensure that comments following #endif are indented the same as comments following other preprocessor commands such as #else. That inconsistency turns out to have been self-inflicted damage from a poorly-thought-through post-indent "fixup" in pgindent. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
fid = (Oid) pq_getmsgint(msgBuf, 4); /* function oid */
/*
* There used to be a lame attempt at caching lookup info here. Now we
* just do the lookups on every call.
*/
fip = &my_fp;
fetch_fp_info(fid, fip);
/* Log as soon as we have the function OID and name */
if (log_statement == LOGSTMT_ALL)
{
ereport(LOG,
(errmsg("fastpath function call: \"%s\" (OID %u)",
fip->fname, fid)));
was_logged = true;
}
/*
* Check permission to access and call function. Since we didn't go
* through a normal name lookup, we need to check schema usage too.
*/
aclresult = pg_namespace_aclcheck(fip->namespace, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(fip->namespace));
InvokeNamespaceSearchHook(fip->namespace, true);
aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(fid));
InvokeFunctionExecuteHook(fid);
/*
* Prepare function call info block and insert arguments.
*
* Note: for now we pass collation = InvalidOid, so collation-sensitive
* functions can't be called this way. Perhaps we should pass
* DEFAULT_COLLATION_OID, instead?
*/
InitFunctionCallInfoData(fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo);
else
rformat = parse_fcall_arguments_20(msgBuf, fip, &fcinfo);
/* Verify we reached the end of the message where expected. */
pq_getmsgend(msgBuf);
/*
* If func is strict, must not call it for null args.
*/
callit = true;
if (fip->flinfo.fn_strict)
{
2003-08-04 00:43:34 +00:00
int i;
for (i = 0; i < fcinfo.nargs; i++)
{
if (fcinfo.argnull[i])
{
callit = false;
break;
}
}
}
if (callit)
{
/* Okay, do it ... */
retval = FunctionCallInvoke(&fcinfo);
}
else
{
fcinfo.isnull = true;
retval = (Datum) 0;
}
/* ensure we do at least one CHECK_FOR_INTERRUPTS per function call */
CHECK_FOR_INTERRUPTS();
SendFunctionResult(retval, fcinfo.isnull, fip->rettype, rformat);
/* We no longer need the snapshot */
PopActiveSnapshot();
/*
* Emit duration logging if appropriate.
*/
switch (check_log_duration(msec_str, was_logged))
{
case 1:
ereport(LOG,
(errmsg("duration: %s ms", msec_str)));
break;
case 2:
ereport(LOG,
(errmsg("duration: %s ms fastpath function call: \"%s\" (OID %u)",
msec_str, fip->fname, fid)));
break;
}
}
/*
* Parse function arguments in a 3.0 protocol message
*
* Argument values are loaded into *fcinfo, and the desired result format
* is returned.
*/
static int16
2017-06-21 14:39:04 -04:00
parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
FunctionCallInfo fcinfo)
{
int nargs;
int i;
int numAFormats;
int16 *aformats = NULL;
StringInfoData abuf;
/* 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)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("function call message contains %d arguments but function requires %d",
nargs, fip->flinfo.fn_nargs)));
fcinfo->nargs = nargs;
if (numAFormats > 1 && numAFormats != nargs)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("function call message contains %d argument formats but %d arguments",
numAFormats, nargs)));
initStringInfo(&abuf);
/*
* Copy supplied arguments into arg vector.
*/
for (i = 0; i < nargs; ++i)
{
int argsize;
int16 aformat;
argsize = pq_getmsgint(msgBuf, 4);
if (argsize == -1)
{
fcinfo->argnull[i] = true;
}
else
{
fcinfo->argnull[i] = false;
if (argsize < 0)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2006-10-04 00:30:14 +00:00
errmsg("invalid argument size %d in function call message",
argsize)));
/* Reset abuf to empty, and insert raw data into it */
resetStringInfo(&abuf);
appendBinaryStringInfo(&abuf,
pq_getmsgbytes(msgBuf, argsize),
argsize);
}
if (numAFormats > 1)
aformat = aformats[i];
else if (numAFormats > 0)
aformat = aformats[0];
else
aformat = 0; /* default = text */
if (aformat == 0)
{
Oid typinput;
Oid typioparam;
char *pstring;
getTypeInputInfo(fip->argtypes[i], &typinput, &typioparam);
2003-08-04 00:43:34 +00:00
/*
2003-08-04 00:43:34 +00:00
* Since stringinfo.c keeps a trailing null in place even for
* binary data, the contents of abuf are a valid C string. We
2003-08-04 00:43:34 +00:00
* have to do encoding conversion before calling the typinput
* routine, though.
*/
if (argsize == -1)
pstring = NULL;
else
pstring = pg_client_to_server(abuf.data, argsize);
fcinfo->arg[i] = OidInputFunctionCall(typinput, pstring,
typioparam, -1);
/* Free result of encoding conversion, if any */
if (pstring && pstring != abuf.data)
pfree(pstring);
}
else if (aformat == 1)
{
Oid typreceive;
Oid typioparam;
StringInfo bufptr;
/* Call the argument type's binary input converter */
getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
if (argsize == -1)
bufptr = NULL;
else
bufptr = &abuf;
fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, bufptr,
typioparam, -1);
/* Trouble if it didn't eat the whole buffer */
if (argsize != -1 && abuf.cursor != abuf.len)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
2005-10-15 02:49:52 +00:00
errmsg("incorrect binary data format in function argument %d",
i + 1)));
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unsupported format code: %d", aformat)));
}
/* Return result format code */
return (int16) pq_getmsgint(msgBuf, 2);
}
/*
* Parse function arguments in a 2.0 protocol message
*
* Argument values are loaded into *fcinfo, and the desired result format
* is returned.
*/
static int16
2017-06-21 14:39:04 -04:00
parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info *fip,
FunctionCallInfo fcinfo)
{
int nargs;
int i;
StringInfoData abuf;
nargs = pq_getmsgint(msgBuf, 4); /* # of arguments */
if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("function call message contains %d arguments but function requires %d",
nargs, fip->flinfo.fn_nargs)));
fcinfo->nargs = nargs;
initStringInfo(&abuf);
/*
* Copy supplied arguments into arg vector. In protocol 2.0 these are
* always assumed to be supplied in binary format.
*
* Note: although the original protocol 2.0 code did not have any way for
* the frontend to specify a NULL argument, we now choose to interpret
* length == -1 as meaning a NULL.
*/
for (i = 0; i < nargs; ++i)
{
int argsize;
Oid typreceive;
Oid typioparam;
getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
argsize = pq_getmsgint(msgBuf, 4);
if (argsize == -1)
{
fcinfo->argnull[i] = true;
fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, NULL,
typioparam, -1);
continue;
}
fcinfo->argnull[i] = false;
if (argsize < 0)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
2006-10-04 00:30:14 +00:00
errmsg("invalid argument size %d in function call message",
argsize)));
/* Reset abuf to empty, and insert raw data into it */
resetStringInfo(&abuf);
appendBinaryStringInfo(&abuf,
pq_getmsgbytes(msgBuf, argsize),
argsize);
fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, &abuf,
typioparam, -1);
/* Trouble if it didn't eat the whole buffer */
if (abuf.cursor != abuf.len)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
2005-10-15 02:49:52 +00:00
errmsg("incorrect binary data format in function argument %d",
i + 1)));
}
/* Desired result format is always binary in protocol 2.0 */
return 1;
}