plperl_array.diff

application/octet-stream

Filename: plperl_array.diff
Type: application/octet-stream
Part: 0
Message: arrays as input parameters in plperl

Patch

Format: unified
File+
src/pl/plperl/expected/plperl_array.out 54 0
src/pl/plperl/GNUmakefile 1 1
src/pl/plperl/plperl.c 94 0
src/pl/plperl/sql/plperl_array.sql 27 0
diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile
index a3c3495..5729459 100644
--- a/src/pl/plperl/GNUmakefile
+++ b/src/pl/plperl/GNUmakefile
@@ -39,7 +39,7 @@ OBJS = plperl.o spi_internal.o SPI.o
 SHLIB_LINK = $(perl_embed_ldflags)
 
 REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-language=plperl
-REGRESS = plperl plperl_trigger plperl_shared plperl_elog
+REGRESS = plperl plperl_trigger plperl_shared plperl_elog plperl_array
 # where to find psql for running the tests
 PSQLDIR = $(bindir)
 
diff --git a/src/pl/plperl/expected/plperl_array.out b/src/pl/plperl/expected/plperl_array.out
new file mode 100644
index 0000000..ee37a5f
--- /dev/null
+++ b/src/pl/plperl/expected/plperl_array.out
@@ -0,0 +1,54 @@
+CREATE OR REPLACE FUNCTION input_array(INTEGER[]) RETURNS INTEGER AS $$
+	my $arref = shift;
+	my $result = 0;
+
+	foreach (@$arref) {
+		$result += $_;
+	}
+	return $result;
+$$ LANGUAGE plperl;
+select input_array('{1,2,NULL}');
+ input_array 
+-------------
+           3
+(1 row)
+
+select input_array('{}');
+ input_array 
+-------------
+           0
+(1 row)
+
+select input_array('{{1,2,3}, {4,5,6}}');
+ input_array 
+-------------
+          21
+(1 row)
+
+CREATE OR REPLACE FUNCTION plperl_concat(TEXT[]) RETURNS TEXT AS $$
+	my $arref = shift;
+	my $result = "";
+	
+	foreach (@$arref) {
+		$result .= $_;
+	}
+	return $result;
+$$ LANGUAGE plperl;
+select plperl_concat('{"hello"," ","world!"}');
+ plperl_concat 
+---------------
+ hello world!
+(1 row)
+
+select plperl_concat('{"NULL","NULL","NULL''"}');
+ plperl_concat 
+---------------
+ NULLNULLNULL'
+(1 row)
+
+select plperl_concat('{{NULL,NULL,NULL}}');
+ plperl_concat 
+---------------
+ 
+(1 row)
+
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 4ed4f59..832eb21 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -64,6 +64,7 @@ typedef struct plperl_proc_desc
 	int			nargs;
 	FmgrInfo	arg_out_func[FUNC_MAX_ARGS];
 	bool		arg_is_rowtype[FUNC_MAX_ARGS];
+	bool		arg_is_array[FUNC_MAX_ARGS];
 	SV		   *reference;
 } plperl_proc_desc;
 
@@ -162,6 +163,7 @@ static SV **hv_store_string(HV *hv, const char *key, SV *val);
 static SV **hv_fetch_string(HV *hv, const char *key);
 static SV  *plperl_create_sub(char *proname, char *s, bool trusted);
 static SV  *plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo);
+static SV  *plperl_ref_from_pg_array(Datum arg);
 static void plperl_compile_callback(void *arg);
 static void plperl_exec_callback(void *arg);
 
@@ -575,6 +577,7 @@ plperl_safe_init(void)
 			desc.reference = func;
 			desc.nargs = 1;
 			desc.arg_is_rowtype[0] = false;
+			desc.arg_is_array[0] = false;
 			fmgr_info(F_TEXTOUT, &(desc.arg_out_func[0]));
 
 			fcinfo.arg[0] = CStringGetTextDatum("a");
@@ -1119,6 +1122,13 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo)
 			XPUSHs(sv_2mortal(hashref));
 			ReleaseTupleDesc(tupdesc);
 		}
+		else if (desc->arg_is_array[i])
+		{
+			SV			*arrayref;
+			/* Get perl array reference from the postgresql array */
+			arrayref = plperl_ref_from_pg_array(fcinfo->arg[i]);
+			XPUSHs(sv_2mortal(arrayref));
+		}
 		else
 		{
 			char	   *tmp;
@@ -1709,6 +1719,12 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
 					perm_fmgr_info(typeStruct->typoutput,
 								   &(prodesc->arg_out_func[i]));
 				}
+				
+				/* Set flag for array attributes */
+				if (typeStruct->typlen == -1 && typeStruct->typelem)
+					prodesc->arg_is_array[i] = true;
+				else
+					prodesc->arg_is_array[i] = false;
 
 				ReleaseSysCache(typeTup);
 			}
@@ -2736,3 +2752,81 @@ plperl_compile_callback(void *arg)
 	if (procname)
 		errcontext("compilation of PL/Perl function \"%s\"", procname);
 }
+
+/* 
+ * Convert PostgreSQL array to a perl array reference.
+ * The code is heavily based on array_out.
+ */
+static SV  *
+plperl_ref_from_pg_array(Datum arg)
+{
+	ArrayType	*ar = DatumGetArrayTypeP(arg);
+	Oid 		elementtype = ARR_ELEMTYPE(ar);
+	AV			*result;
+	int16		typlen;
+	bool		typbyval;
+	char		typalign,
+				typdelim;
+	Oid			typioparam;
+	Oid			typoutputfunc;
+	FmgrInfo	proc;
+	int			i,
+				nitems,
+				ndims,
+				*dims,
+				*lb;
+	bits8	   *bitmap;
+	int			bitmask;
+	char	   *data;
+			
+				
+	/* get element type information, including output coversion function */
+	get_type_io_data(elementtype, IOFunc_output, 
+					 &typlen, &typbyval, &typalign, 
+					 &typdelim, &typioparam, &typoutputfunc);
+	
+	perm_fmgr_info(typoutputfunc, &proc);
+	
+	ndims = ARR_NDIM(ar);
+	dims = ARR_DIMS(ar);
+	lb = ARR_LBOUND(ar);
+	nitems = ArrayGetNItems(ndims, dims);
+	
+	bitmap = ARR_NULLBITMAP(ar);
+	bitmask = 1;
+	data = ARR_DATA_PTR(ar);
+	
+	result = newAV();
+	
+	for (i = 0; i < nitems; i++ )
+	{
+		/* write undef  on NULL elements*/
+		if (bitmap && (*bitmap & bitmask) == 0)
+			av_push(result, newSV(0));	
+		else
+		{
+			Datum	itemvalue;
+			char   *val;
+			
+			itemvalue = fetch_att(data, typbyval, typlen);
+			val = OutputFunctionCall(&proc, itemvalue);
+			av_push(result, newSVstring(val));
+			/* get to the next array element */
+			data = att_addlength_pointer(data, typlen, data);
+			data = (char *) att_align_nominal(data, typalign);
+		}
+		
+		/* advance bitmap pointer if any */ 
+		if (bitmap)
+		{
+			bitmask <<= 1;
+			if (bitmask == 0x100)
+			{
+				bitmap++;
+				bitmask = 1;
+			}
+		}	
+		
+	}
+	return newRV_noinc((SV *) result);
+}
diff --git a/src/pl/plperl/sql/plperl_array.sql b/src/pl/plperl/sql/plperl_array.sql
new file mode 100644
index 0000000..b84eed3
--- /dev/null
+++ b/src/pl/plperl/sql/plperl_array.sql
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION input_array(INTEGER[]) RETURNS INTEGER AS $$
+	my $arref = shift;
+	my $result = 0;
+
+	foreach (@$arref) {
+		$result += $_;
+	}
+	return $result;
+$$ LANGUAGE plperl;
+
+select input_array('{1,2,NULL}');
+select input_array('{}');
+select input_array('{{1,2,3}, {4,5,6}}');
+
+CREATE OR REPLACE FUNCTION plperl_concat(TEXT[]) RETURNS TEXT AS $$
+	my $arref = shift;
+	my $result = "";
+	
+	foreach (@$arref) {
+		$result .= $_;
+	}
+	return $result;
+$$ LANGUAGE plperl;
+
+select plperl_concat('{"hello"," ","world!"}');
+select plperl_concat('{"NULL","NULL","NULL''"}');
+select plperl_concat('{{NULL,NULL,NULL}}');