Hi,
I'm trying to recover from an exception in an fmgr hook. It seems to
work relatively well most of the time, but in some cases the backend
segfaults. Attached is a self-contained test case demonstrating the
problem. The problem I'm hitting can be reproduced with:
create function f4() returns int as $$ select 2 $$ language sql;
create function f3() returns int as $$ declare f int; begin select f4()
into f; return 1; end $$ language plpgsql;
select f3();
the backend dies at:
Program received signal SIGSEGV, Segmentation fault.
0x08406d49 in fmgr_security_definer (fcinfo=0x89b133c) at fmgr.c:975
975 result = FunctionCallInvoke(fcinfo);
10 lines of backtrace:
#0 0x08406d49 in fmgr_security_definer (fcinfo=0x89b133c) at fmgr.c:975
#1 0x082094b5 in ExecMakeFunctionResult (fcache=0x89b1300,
econtext=0x89b11ec, isNull=0x89b185c '\177' <repeats 200 times>...,
isDone=0x89b1908) at execQual.c:1917
#2 0x08209e32 in ExecEvalFunc (fcache=0x89b1300, econtext=0x89b11ec,
isNull=0x89b185c '\177' <repeats 200 times>..., isDone=0x89b1908)
at execQual.c:2356
#3 0x0820f6d9 in ExecTargetList (targetlist=0x89b18ec, econtext=0x89b11ec,
values=0x89b1848, isnull=0x89b185c '\177' <repeats 200 times>...,
itemIsDone=0x89b1908, isDone=0xbffc2118) at execQual.c:5210
#4 0x0820fc03 in ExecProject (projInfo=0x89b1870, isDone=0xbffc2118)
at execQual.c:5425
#5 0x08224972 in ExecResult (node=0x89b1160) at nodeResult.c:155
#6 0x08206337 in ExecProcNode (node=0x89b1160) at execProcnode.c:367
#7 0x082041dc in ExecutePlan (estate=0x89b10d4, planstate=0x89b1160,
operation=CMD_SELECT, sendTuples=1 '\001', numberTuples=1,
direction=ForwardScanDirection, dest=0x862170c) at execMain.c:1440
#8 0x08202887 in standard_ExecutorRun (queryDesc=0x89adcac,
direction=ForwardScanDirection, count=1) at execMain.c:314
#9 0x082026fb in ExecutorRun (queryDesc=0x89adcac,
direction=ForwardScanDirection, count=1) at execMain.c:262
#10 0x08232131 in _SPI_pquery (queryDesc=0x89adcac, fire_triggers=1 '\001',
tcount=1) at spi.c:2110
It looks like fcinfo (amongst other things) is allocated in a child of
the SPI context. My speculation is that the SPI context gets reset by
AtEOSubXact_SPI(), thus resetting the memory fcinfo points to, leading
to SIGSEGV.
Any thoughts on how to avoid this crash? Or generally, how to correctly
clean up after an exception? The attached code tries to imitate what
the PLs are doing, but it's not working. :-(
Regards,
Marko Tiikkaja
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "postgres.h"
#include "plpgsql.h"
#include "miscadmin.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/explain.h"
#include "executor/executor.h"
#include "executor/instrument.h"
#include "executor/spi.h"
#include "nodes/params.h"
#include "parser/parser.h"
#include "parser/parse_target.h"
#include "parser/parse_node.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
#include "utils/lsyscache.h"
PG_MODULE_MAGIC;
static MemoryContext MyMemoryContext = NULL;
static needs_fmgr_hook_type prev_needs_fmgr_hook = NULL;
static fmgr_hook_type prev_fmgr_hook = NULL;
void _PG_fini(void);
void _PG_init(void);
static void
do_stuff()
{
MemoryContext oldcxt;
ResourceOwner oldowner = CurrentResourceOwner;
oldcxt = MemoryContextSwitchTo(MyMemoryContext);
BeginInternalSubTransaction(NULL);
PG_TRY();
{
elog(ERROR, "BIG LETTERS");
}
PG_CATCH();
{
ErrorData *edata;
MemoryContextSwitchTo(MyMemoryContext);
edata = CopyErrorData();
FlushErrorState();
RollbackAndReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcxt);
CurrentResourceOwner = oldowner;
//SPI_restore_connection();
FreeErrorData(edata);
}
PG_END_TRY();
}
static bool
cleanup_test_needs_fmgr_hook(Oid functionId)
{
return true;
}
static void
cleanup_test_fmgr_hook(FmgrHookEventType event, FmgrInfo *flinfo,
Datum *args)
{
if (prev_fmgr_hook)
(*prev_fmgr_hook) (event, flinfo, args);
switch (event)
{
case FHET_START:
do_stuff();
break;
case FHET_ABORT:
case FHET_END:
break;
default:
elog(ERROR, "unknown FmgtHookEventType %d", event);
}
}
void
_PG_init(void)
{
MyMemoryContext = AllocSetContextCreate(TopTransactionContext,
"mymemcxt",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
prev_needs_fmgr_hook = needs_fmgr_hook;
needs_fmgr_hook = cleanup_test_needs_fmgr_hook;
prev_fmgr_hook = fmgr_hook;
fmgr_hook = cleanup_test_fmgr_hook;
}
void
_PG_fini(void)
{
needs_fmgr_hook = prev_needs_fmgr_hook;
prev_needs_fmgr_hook = NULL;
fmgr_hook = prev_fmgr_hook;
prev_fmgr_hook = NULL;
MemoryContextDelete(MyMemoryContext);
MyMemoryContext = NULL;
}
# cleanup_test/Makefile
MODULE_big = cleanup_test
OBJS = cleanup_test.o
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
--
Sent via pgsql-general mailing list (pgsql-general@xxxxxxxxxxxxxx)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-general