Permalink
Browse files

Fixed bug: integer argument with value = 0 was converted to NA in R. Now

appropriately passes the 0 value as-is.

Added support for 3D PostgreSQL arrays as input arguments to PL/R functions.

Added support for returning 3D arrays. Specifically, if the R return value
is a 3D array, and the PL/R function is declared to return an array type,
the returned array will also be 3D.

Updated documentation for 3D array support.
  • Loading branch information...
1 parent 850536f commit ee99c4f83de78bc58caa491a2d65dfb3e16fcfcc postgres committed Mar 5, 2003
Showing with 205 additions and 72 deletions.
  1. +14 −6 doc/plr.sgml
  2. +58 −0 expected/plr.out
  3. +106 −66 pg_conversion.c
  4. +27 −0 sql/plr.sql
View
@@ -278,10 +278,11 @@ select round(sd('{1.23,1.31,1.42,1.27}'::_float8)::numeric,8);
the input arguments converted to a corresponding R form.
See <xref linkend="plr-args-table">. Scalar PostgreSQL
values become single element R vectors. One-dimensional
- PostgreSQL arrays are converted to multi-element
- R vectors, and two-dimensional PostgreSQL arrays
- are mapped to R matrixes, but greater than two-dimensional arrays are not
- supported. Composite-types are transformed into R data.frames.
+ PostgreSQL arrays are converted to multi-element R vectors, two-dimensional
+ PostgreSQL arrays are mapped to R matrixes, and three-dimensional
+ PostgreSQL arrays are converted to three-dimensional R arrays. Greater
+ than three-dimensional arrays are not supported. Composite-types are
+ transformed into R data.frames.
</para>
<table id="plr-args-table">
@@ -375,9 +376,9 @@ select round(sd('{1.23,1.31,1.42,1.27}'::_float8)::numeric,8);
<row>
<entry><type>array</type></entry>
- <entry><type>1D array</type>, <type>greater than 2D array</type>, <type>vector</type></entry>
+ <entry><type>1D array</type>, <type>greater than 3D array</type>, <type>vector</type></entry>
<entry>1D array</entry>
- <entry>array(1:8,c(2,2,2)) in R returns {1,2,3,4,5,6,7,8}</entry>
+ <entry>array(1:8,c(2,2,2,2)) in R returns {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8}</entry>
</row>
<row>
@@ -388,6 +389,13 @@ select round(sd('{1.23,1.31,1.42,1.27}'::_float8)::numeric,8);
</row>
<row>
+ <entry><type>array</type></entry>
+ <entry><type>3D array</type></entry>
+ <entry>3D array</entry>
+ <entry>array(1:8,c(2,2,2)) in R returns {{{1,5},{3,7}},{{2,6},{4,8}}}</entry>
+ </row>
+
+ <row>
<entry><type>composite</type></entry>
<entry><type>1D array</type>, <type>greater than 2D array</type>, <type>vector</type></entry>
<entry>first row, 1 column</entry>
View
@@ -549,3 +549,61 @@ select * from test_in_m_tup('{{1,3,5},{2,4,6}}') as t(f1 int, f2 int, f3 int);
2 | 4 | 6
(2 rows)
+--
+-- test 3D array argument
+--
+create or replace function arr3d(_int4,int4,int4,int4) returns int4 as '
+if (arg2 < 1 || arg3 < 1 || arg4 < 1)
+ return(NA)
+if (arg2 > dim(arg1)[1] || arg3 > dim(arg1)[2] || arg4 > dim(arg1)[3])
+ return(NA)
+return(arg1[arg2,arg3,arg4])
+' language 'plr' WITH (isstrict);
+select arr3d('{{{111,112},{121,122},{131,132}},{{211,212},{221,222},{231,232}}}',2,3,1) as "231";
+ 231
+-----
+ 231
+(1 row)
+
+-- for sake of comparison, see what normal pgsql array operations produces
+select f1[2][3][1] as "231" from (select '{{{111,112},{121,122},{131,132}},{{211,212},{221,222},{231,232}}}'::int4[] as f1) as t;
+ 231
+-----
+ 231
+(1 row)
+
+-- out-of-bounds, returns null
+select arr3d('{{{111,112},{121,122},{131,132}},{{211,212},{221,222},{231,232}}}',1,4,1) is null as "NULL";
+ NULL
+------
+ t
+(1 row)
+
+select f1[1][4][1] is null as "NULL" from (select '{{{111,112},{121,122},{131,132}},{{211,212},{221,222},{231,232}}}'::int4[] as f1) as t;
+ NULL
+------
+ t
+(1 row)
+
+select arr3d('{{{111,112},{121,122},{131,132}},{{211,212},{221,222},{231,232}}}',0,1,1) is null as "NULL";
+ NULL
+------
+ t
+(1 row)
+
+select f1[0][1][1] is null as "NULL" from (select '{{{111,112},{121,122},{131,132}},{{211,212},{221,222},{231,232}}}'::int4[] as f1) as t;
+ NULL
+------
+ t
+(1 row)
+
+--
+-- test 3D array return value
+--
+create or replace function arr3d(_int4) returns int4[] as 'return(arg1)' language 'plr' WITH (isstrict);
+select arr3d('{{{111,112},{121,122},{131,132}},{{211,212},{221,222},{231,232}}}');
+ arr3d
+-------------------------------------------------------------------
+ {{{111,112},{121,122},{131,132}},{{211,212},{221,222},{231,232}}}
+(1 row)
+
View
@@ -41,7 +41,7 @@ static SEXP get_r_vector(Oid typtype, int numels);
static Datum get_tuplestore(SEXP rval, plr_proc_desc *prodesc, FunctionCallInfo fcinfo, bool *isnull);
static Datum get_array_datum(SEXP rval, plr_proc_desc *prodesc, bool *isnull);
static Datum get_frame_array_datum(SEXP rval, plr_proc_desc *prodesc, bool *isnull);
-static Datum get_matrix_array_datum(SEXP rval, plr_proc_desc *prodesc, bool *isnull);
+static Datum get_md_array_datum(SEXP rval, int ndims, plr_proc_desc *prodesc, bool *isnull);
static Datum get_generic_array_datum(SEXP rval, plr_proc_desc *prodesc, bool *isnull);
static Tuplestorestate *get_frame_tuplestore(SEXP rval,
plr_proc_desc *prodesc,
@@ -68,16 +68,6 @@ pg_scalar_get_r(Datum dvalue, Oid arg_typid, FmgrInfo arg_out_func)
SEXP result;
char *value;
- if (dvalue == (Datum) 0)
- {
- /* fast track for null arguments */
- PROTECT(result = NEW_CHARACTER(1));
- SET_STRING_ELT(result, 0, NA_STRING);
- UNPROTECT(1);
-
- return result;
- }
-
value = DatumGetCString(FunctionCall3(&arg_out_func,
dvalue,
(Datum) 0,
@@ -117,10 +107,11 @@ pg_array_get_r(Datum dvalue, FmgrInfo out_func, int typlen, bool typbyval, char
char *value;
ArrayType *v = (ArrayType *) dvalue;
Oid element_type;
- int i, j,
+ int i, j, k,
nitems,
- nr = 0,
- nc = 0,
+ nr = 1,
+ nc = 1,
+ nz = 1,
ndim,
*dim;
char *p;
@@ -141,16 +132,19 @@ pg_array_get_r(Datum dvalue, FmgrInfo out_func, int typlen, bool typbyval, char
return result;
}
- if (ndim < 2)
- {
+ if (ndim == 1)
nr = nitems;
- nc = 1;
- }
else if (ndim == 2)
{
nr = dim[0];
nc = dim[1];
}
+ else if (ndim == 3)
+ {
+ nr = dim[0];
+ nc = dim[1];
+ nz = dim[2];
+ }
else
elog(ERROR, "plr: 3 (or more) dimension arrays are not yet supported as function arguments");
@@ -163,33 +157,37 @@ pg_array_get_r(Datum dvalue, FmgrInfo out_func, int typlen, bool typbyval, char
{
for (j = 0; j < nc; j++)
{
- Datum itemvalue;
- int idx = (j * nr) + i;
-
- itemvalue = fetch_att(p, typbyval, typlen);
- value = DatumGetCString(FunctionCall3(&out_func,
- itemvalue,
- (Datum) 0,
- Int32GetDatum(-1)));
- p = att_addlength(p, typlen, PointerGetDatum(p));
- p = (char *) att_align(p, typalign);
-
- if (value != NULL)
- pg_get_one_r(value, element_type, &result, idx);
- else
- SET_STRING_ELT(result, idx, NA_STRING);
+ for (k = 0; k < nz; k++)
+ {
+ Datum itemvalue;
+ int idx = (k * nr * nc) + (j * nr) + i;
+
+ itemvalue = fetch_att(p, typbyval, typlen);
+ value = DatumGetCString(FunctionCall3(&out_func,
+ itemvalue,
+ (Datum) 0,
+ Int32GetDatum(-1)));
+ p = att_addlength(p, typlen, PointerGetDatum(p));
+ p = (char *) att_align(p, typalign);
+
+ if (value != NULL)
+ pg_get_one_r(value, element_type, &result, idx);
+ else
+ SET_STRING_ELT(result, idx, NA_STRING);
+ }
}
}
UNPROTECT(1);
- if (ndim == 2)
+ if (ndim > 1)
{
SEXP matrix_dims;
/* attach dimensions */
- PROTECT(matrix_dims = allocVector(INTSXP, 2));
- INTEGER_DATA(matrix_dims)[0] = dim[0];
- INTEGER_DATA(matrix_dims)[1] = dim[1];
+ PROTECT(matrix_dims = allocVector(INTSXP, ndim));
+ for (i = 0; i < ndim; i++)
+ INTEGER_DATA(matrix_dims)[i] = dim[i];
+
setAttrib(result, R_DimSymbol, matrix_dims);
UNPROTECT(1);
}
@@ -557,12 +555,25 @@ get_scalar_datum(SEXP rval, FmgrInfo result_in_func, Oid result_elem, bool *isnu
static Datum
get_array_datum(SEXP rval, plr_proc_desc *prodesc, bool *isnull)
{
+ SEXP rdims;
+ int ndims;
+
+ /* two supported special cases */
if (isFrame(rval))
return get_frame_array_datum(rval, prodesc, isnull);
else if (isMatrix(rval))
- return get_matrix_array_datum(rval, prodesc, isnull);
- else
- return get_generic_array_datum(rval, prodesc, isnull);
+ return get_md_array_datum(rval, 2 /* matrix is 2D */, prodesc, isnull);
+
+ PROTECT(rdims = getAttrib(rval, R_DimSymbol));
+ ndims = length(rdims);
+ UNPROTECT(1);
+
+ /* 2D and 3D arrays are specifically supported too */
+ if (ndims == 2 || ndims == 3)
+ return get_md_array_datum(rval, ndims, prodesc, isnull);
+
+ /* everything else */
+ return get_generic_array_datum(rval, prodesc, isnull);
}
static Datum
@@ -657,60 +668,89 @@ get_frame_array_datum(SEXP rval, plr_proc_desc *prodesc, bool *isnull)
}
static Datum
-get_matrix_array_datum(SEXP rval, plr_proc_desc *prodesc, bool *isnull)
+get_md_array_datum(SEXP rval, int ndims, plr_proc_desc *prodesc, bool *isnull)
{
Datum dvalue;
SEXP obj;
+ SEXP rdims;
char *value;
Oid result_elem = prodesc->result_elem;
FmgrInfo in_func = prodesc->result_elem_in_func;
int typlen = prodesc->result_elem_typlen;
bool typbyval = prodesc->result_elem_typbyval;
char typalign = prodesc->result_elem_typalign;
- int i;
+ int i, j, k;
Datum *dvalues = NULL;
ArrayType *array;
-
- int nr = nrows(rval);
- int nc = ncols(rval);
- int ndims = 2;
+ int nitems;
+ int nr = 1;
+ int nc = 1;
+ int nz = 1;
int ndatabytes;
int nbytes;
ArrayType *tmparray;
int dims[ndims];
int lbs[ndims];
int idx;
+ int cntr = 0;
- dims[0] = nr;
- dims[1] = nc;
- lbs[0] = 1;
- lbs[1] = 1;
+ PROTECT(rdims = getAttrib(rval, R_DimSymbol));
+ for(i = 0; i < ndims; i++)
+ {
+ dims[i] = INTEGER(rdims)[i];
+ lbs[i] = 1;
+
+ switch (i)
+ {
+ case 0:
+ nr = dims[i];
+ break;
+ case 1:
+ nc = dims[i];
+ break;
+ case 2:
+ nz = dims[i];
+ break;
+ default:
+ /* anything higher is currently unsupported */
+ elog(ERROR, "plr: returning arrays of greater than 3 " \
+ "dimensions is currently not supported");
+ }
- dvalues = (Datum *) palloc(nr * nc * sizeof(Datum));
+ }
+ UNPROTECT(1);
+
+ nitems = nr * nc * nz;
+ dvalues = (Datum *) palloc(nitems * sizeof(Datum));
PROTECT(obj = AS_CHARACTER(rval));
- /* Loop is needed here as result value might be of length > 1 */
- for(i = 0; i < nr * nc; i++)
+ for (i = 0; i < nr; i++)
{
- value = CHAR(STRING_ELT(obj, i));
- idx = ((i % nr) * nc) + (i / nr);
+ for (j = 0; j < nc; j++)
+ {
+ for (k = 0; k < nz; k++)
+ {
+ idx = (k * nr * nc) + (j * nr) + i;
+ value = CHAR(STRING_ELT(obj, idx));
- if (STRING_ELT(obj, i) == NA_STRING || value == NULL)
- elog(ERROR, "plr: cannot return array with NULL elements");
- else
- dvalues[idx] = FunctionCall3(&in_func,
- CStringGetDatum(value),
- (Datum) 0,
- Int32GetDatum(-1));
- }
+ if (STRING_ELT(obj, idx) == NA_STRING || value == NULL)
+ elog(ERROR, "plr: cannot return array with NULL elements");
+ else
+ dvalues[cntr++] = FunctionCall3(&in_func,
+ CStringGetDatum(value),
+ (Datum) 0,
+ Int32GetDatum(-1));
+ }
+ }
+ }
UNPROTECT(1);
/* build up 1d array */
- array = construct_array(dvalues, nr * nc, result_elem, typlen, typbyval, typalign);
+ array = construct_array(dvalues, nitems, result_elem, typlen, typbyval, typalign);
- /* convert it to a 2d array */
+ /* convert it to a {ndims}d array */
ndatabytes = ARR_SIZE(array) - ARR_OVERHEAD(1);
- nbytes = ndatabytes + ARR_OVERHEAD(2);
+ nbytes = ndatabytes + ARR_OVERHEAD(ndims);
tmparray = (ArrayType *) palloc(nbytes);
tmparray->size = nbytes;
Oops, something went wrong.

0 comments on commit ee99c4f

Please sign in to comment.