Thread: Postgres 8.3 - C function taking and returning arrays
I have written a PostgreSQL 8.3beta2 server side function named array_times_scalar (source included below). It works, but I haven't found sufficient examples to be certain that I am not leaking memory. I was wondering if someone can either point me to examples or take a look at the code below.
I am, however, getting incorrect values if I select a particular array index out of the result of this function. That is, "select (array_times_scalar('{344,52,25}'::smallint[],0.001::double precision))[1];" is not the same as the first element of "select array_times_scalar('{344,52,25}'::smallint[],0.001::double precision);". I was hoping if someone could shed a light on why that may be.
As a side-note: I've written similar function in plpgsql, which produces correct results when I extract any element of the returned array. However, I need the speed of a C-function since this function may be run on millions of records at a time.
Thanks.
create or replace function array_times_scalar(smallint[], double precision) returns real[]
as '/var/lib/pgsql/test_func',
I am, however, getting incorrect values if I select a particular array index out of the result of this function. That is, "select (array_times_scalar('{344,52,25}'::smallint[],0.001::double precision))[1];" is not the same as the first element of "select array_times_scalar('{344,52,25}'::smallint[],0.001::double precision);". I was hoping if someone could shed a light on why that may be.
As a side-note: I've written similar function in plpgsql, which produces correct results when I extract any element of the returned array. However, I need the speed of a C-function since this function may be run on millions of records at a time.
Thanks.
create or replace function array_times_scalar(smallint[], double precision) returns real[]
as '/var/lib/pgsql/test_func',
'array_times_scalar' language C immutable strict;
Datum array_times_scalar(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(array_times_scalar);
/**
** Returns the input array with every element multiplied
** by the specified scale.
**/
Datum
array_times_scalar(PG_FUNCTION_ARGS){
ArrayType *input;
Datum *i_data;
bool *nulls;
float8 scale = 0;
ArrayType *result;
Datum *result_data;
int ndims, *dims, *lbs;
Oid i_eltype, s_eltype, o_eltype;
int16 i_typlen, o_typlen;
bool i_typbyval, o_typbyval;
char i_typalign, o_typalign;
int i, n;
/* return null on null input */
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)){
PG_RETURN_NULL();
}
/* get input args */
input = PG_GETARG_ARRAYTYPE_P(0);
/* get input array element type */
i_eltype = ARR_ELEMTYPE(input);
get_typlenbyvalalign(i_eltype, &i_typlen, &i_typbyval, &i_typalign);
/* validate input data type */
switch(i_eltype){
case INT2OID:
case INT4OID:
case FLOAT4OID:
case FLOAT8OID:
break;
default:
elog(ERROR, "Invalid input data type");
break;
}
/* get scale data type */
s_eltype = get_fn_expr_argtype(fcinfo->flinfo, 1);
/* validate the scale data type */
switch(s_eltype){
case INT2OID: scale = PG_GETARG_INT16(1); break;
case INT4OID: scale = PG_GETARG_INT32(1); break;
case FLOAT4OID: scale = PG_GETARG_FLOAT4(1); break;
case FLOAT8OID: scale = PG_GETARG_FLOAT8(1); break;
default:
elog(ERROR, "Invalid scale type");
break;
}
/* get output array element type */
if (i_eltype == FLOAT8OID || s_eltype == FLOAT8OID){
o_eltype = FLOAT8OID;
}
else if (i_eltype == FLOAT4OID || s_eltype == FLOAT4OID){
o_eltype = FLOAT4OID;
}
else {
o_eltype = INT4OID;
}
get_typlenbyvalalign(o_eltype, &o_typlen, &o_typbyval, &o_typalign);
/* get various pieces of data from the input array */
ndims = ARR_NDIM(input);
dims = ARR_DIMS(input);
lbs = ARR_LBOUND(input);
/* get src data */
deconstruct_array(input, i_eltype, i_typlen, i_typbyval, i_typalign, &i_data, &nulls, &n);
/* construct result array */
result_data = (Datum *)palloc(n * sizeof(Datum));
/* apply scale */
for(i=0; i<n; i++){
if (nulls[i]){
result_data[i] = PointerGetDatum(NULL);
}
else {
double v = 0;
switch(i_eltype){
case INT2OID: v = DatumGetInt16(i_data[i]); break;
case INT4OID: v = DatumGetInt32(i_data[i]); break;
case FLOAT4OID: v = DatumGetFloat4(i_data[i]); break;
case FLOAT8OID: v = DatumGetFloat8(i_data[i]); break;
}
v *= scale;
switch(o_eltype){
case INT4OID: result_data[i] = Int32GetDatum((int32)v); break;
case FLOAT4OID: result_data[i] = Float4GetDatum((float4)v); break;
case FLOAT8OID: result_data[i] = Float8GetDatum((float8)v); break;
}
}
}
result = construct_md_array((void *)result_data, nulls, ndims, dims, lbs, o_eltype, o_typlen, o_typbyval, o_typalign);
pfree(i_data);
pfree(result_data);
pfree(nulls);
PG_RETURN_ARRAYTYPE_P(result);
}
Datum array_times_scalar(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(array_times_scalar);
/**
** Returns the input array with every element multiplied
** by the specified scale.
**/
Datum
array_times_scalar(PG_FUNCTION_ARGS){
ArrayType *input;
Datum *i_data;
bool *nulls;
float8 scale = 0;
ArrayType *result;
Datum *result_data;
int ndims, *dims, *lbs;
Oid i_eltype, s_eltype, o_eltype;
int16 i_typlen, o_typlen;
bool i_typbyval, o_typbyval;
char i_typalign, o_typalign;
int i, n;
/* return null on null input */
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)){
PG_RETURN_NULL();
}
/* get input args */
input = PG_GETARG_ARRAYTYPE_P(0);
/* get input array element type */
i_eltype = ARR_ELEMTYPE(input);
get_typlenbyvalalign(i_eltype, &i_typlen, &i_typbyval, &i_typalign);
/* validate input data type */
switch(i_eltype){
case INT2OID:
case INT4OID:
case FLOAT4OID:
case FLOAT8OID:
break;
default:
elog(ERROR, "Invalid input data type");
break;
}
/* get scale data type */
s_eltype = get_fn_expr_argtype(fcinfo->flinfo, 1);
/* validate the scale data type */
switch(s_eltype){
case INT2OID: scale = PG_GETARG_INT16(1); break;
case INT4OID: scale = PG_GETARG_INT32(1); break;
case FLOAT4OID: scale = PG_GETARG_FLOAT4(1); break;
case FLOAT8OID: scale = PG_GETARG_FLOAT8(1); break;
default:
elog(ERROR, "Invalid scale type");
break;
}
/* get output array element type */
if (i_eltype == FLOAT8OID || s_eltype == FLOAT8OID){
o_eltype = FLOAT8OID;
}
else if (i_eltype == FLOAT4OID || s_eltype == FLOAT4OID){
o_eltype = FLOAT4OID;
}
else {
o_eltype = INT4OID;
}
get_typlenbyvalalign(o_eltype, &o_typlen, &o_typbyval, &o_typalign);
/* get various pieces of data from the input array */
ndims = ARR_NDIM(input);
dims = ARR_DIMS(input);
lbs = ARR_LBOUND(input);
/* get src data */
deconstruct_array(input, i_eltype, i_typlen, i_typbyval, i_typalign, &i_data, &nulls, &n);
/* construct result array */
result_data = (Datum *)palloc(n * sizeof(Datum));
/* apply scale */
for(i=0; i<n; i++){
if (nulls[i]){
result_data[i] = PointerGetDatum(NULL);
}
else {
double v = 0;
switch(i_eltype){
case INT2OID: v = DatumGetInt16(i_data[i]); break;
case INT4OID: v = DatumGetInt32(i_data[i]); break;
case FLOAT4OID: v = DatumGetFloat4(i_data[i]); break;
case FLOAT8OID: v = DatumGetFloat8(i_data[i]); break;
}
v *= scale;
switch(o_eltype){
case INT4OID: result_data[i] = Int32GetDatum((int32)v); break;
case FLOAT4OID: result_data[i] = Float4GetDatum((float4)v); break;
case FLOAT8OID: result_data[i] = Float8GetDatum((float8)v); break;
}
}
}
result = construct_md_array((void *)result_data, nulls, ndims, dims, lbs, o_eltype, o_typlen, o_typbyval, o_typalign);
pfree(i_data);
pfree(result_data);
pfree(nulls);
PG_RETURN_ARRAYTYPE_P(result);
}
"s anwar" <sanwar@gmail.com> writes: > I have written a PostgreSQL 8.3beta2 server side function named > array_times_scalar (source included below). It works, but I haven't found > sufficient examples to be certain that I am not leaking memory. I was > wondering if someone can either point me to examples or take a look at the > code below. As a rule of thumb, SQL-callable C functions don't need to be particularly concerned about "leaking" memory allocations made with palloc(). Such functions are normally called with CurrentMemoryContext set to a short-lived context that will be reset soon (usually at the end of the processing cycle for the current tuple). So anything you palloc will go away pretty soon anyway. There's even an argument to be made that explicit pfree's are a bad idea, because they eat more cycles than would be needed to let the memory be reclaimed by the next context reset. There are some exceptions, in particular functions that are used as b-tree index support functions need to be more careful. But for the most part I'd say don't worry unless you have direct evidence that you have a problem. > I am, however, getting incorrect values if I select a particular array index > out of the result of this function. You declared the function as "returns real[]" but the function code seems to think that it can return some randomly chosen type different from array of FLOAT4OID. I think this disconnect is the problem... regards, tom lane