Craig Ringer wrote :
> If you're using C++ you must also:
> - Ensure that no exceptions propagate outside your code
> - Declare all hook functions that might be dlopen()ed as extern "C"
Thanks for your advices but I'm not sure to have understood. For example, when
I try to implement the "normal_rand" function from "/contrib/tablefunc" like
below, I have a server crash.
/******************
* test.h *
******************/
extern "C" {
#include "postgres.h"
#include "fmgr.h"
#include "funcapi.h"
#include "executor/spi.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include <math.h>
}
extern "C" __declspec (dllexport) Datum normal_rand(PG_FUNCTION_ARGS);
/*********************
* test.cpp *
*********************/
#include "test.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(normal_rand);
Datum normal_rand(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
int call_cntr;
int max_calls;
normal_rand_fctx *fctx;
float8 mean;
float8 stddev;
float8 carry_val;
bool use_carry;
MemoryContext oldcontext;
/* stuff done only on the first call of the function */
if (SRF_IS_FIRSTCALL())
{
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
/* switch to memory context appropriate for multiple function calls */
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* total number of tuples to be returned */
funcctx->max_calls = PG_GETARG_UINT32(0);
/* allocate memory for user context */
fctx = (normal_rand_fctx *) palloc(sizeof(normal_rand_fctx));
/*
* Use fctx to keep track of upper and lower bounds from call to call.
* It will also be used to carry over the spare value we get from the
* Box-Muller algorithm so that we only actually calculate a new value
* every other call.
*/
fctx->mean = PG_GETARG_FLOAT8(1);
fctx->stddev = PG_GETARG_FLOAT8(2);
fctx->carry_val = 0;
fctx->use_carry = false;
funcctx->user_fctx = fctx;
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
fctx = (normal_rand_fctx*) funcctx->user_fctx;
mean = fctx->mean;
stddev = fctx->stddev;
carry_val = fctx->carry_val;
use_carry = fctx->use_carry;
if (call_cntr < max_calls) /* do when there is more left to send */
{
float8 result;
if (use_carry)
{
/* reset use_carry and use second value obtained on last pass */
fctx->use_carry = false;
result = carry_val;
}
else
{
float8 normval_1;
float8 normval_2;
/* Get the next two normal values */
get_normal_pair(&normval_1, &normval_2);
/* use the first */
result = mean + (stddev * normval_1);
/* and save the second */
fctx->carry_val = mean + (stddev * normval_2);
fctx->use_carry = true;
}
/* send the result */
SRF_RETURN_NEXT(funcctx, Float8GetDatum(result));
}
else
/* do when there is no more left */
SRF_RETURN_DONE(funcctx);
}
/*****************
* script *
*****************/
CREATE OR REPLACE FUNCTION normal_rand(int4, float8, float8)
RETURNS setof float8
AS '$libdir/tablefunc','normal_rand'
LANGUAGE C VOLATILE STRICT;
In the log file, it indicated that the server stop with an exception
(0xC0000005). In the "ntstatus.h", it indicated that this exception 0xC0000005
means ACCESS VIOLATION. I don't know what causes this exception. It's also a
problem with some missing " extern 'C' " ? Or is it a null pointer problem ?
Tom Lane wrote :
> On the whole I'd recommend using plain C for backend functions if you
> possibly can.
Unfortunately, I must use cpp because I must translate an temporal extension
(wrote on cpp with templates, ...) from Oracle to PostgreSQL.
--
Ben Ali Rachid