Postgres 8.3 - C function taking and returning arrays - Mailing list pgsql-novice
From | s anwar |
---|---|
Subject | Postgres 8.3 - C function taking and returning arrays |
Date | |
Msg-id | 3e3c86f90802281153o15e70724u425cdd0173bb2373@mail.gmail.com Whole thread Raw |
Responses |
Re: Postgres 8.3 - C function taking and returning arrays
|
List | pgsql-novice |
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);
}
pgsql-novice by date: