On Sun, 7 Aug 2011 14:57:36 +0200 Wojciech Muła
<wojciech_m...@poczta.onet.pl> wrote:

> Hi all, does anybody work on this TODO item?
> http://wiki.postgresql.org/wiki/Todo#PL.2FpgSQL
> 
> I didn't find any related posting/bug report.

Hi, I've prepared simple patch, please review.

Since exact array defintion isn't needed anywhere, code
detects only if %TYPE is followed by tokens matching
regexp ('[' ICONST ']')+. This information is used during
type construction.

w.
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 92b54dd..efdc833 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -76,6 +76,8 @@ static	PLpgSQL_expr	*read_sql_expression2(int until, int until2,
 											  int *endtoken);
 static	PLpgSQL_expr	*read_sql_stmt(const char *sqlstart);
 static	PLpgSQL_type	*read_datatype(int tok);
+static	int				 read_array_dims(int tok);
+static	int				 read_single_array_dim(int tok);
 static	PLpgSQL_stmt	*make_execsql_stmt(int firsttoken, int location);
 static	PLpgSQL_stmt_fetch *read_fetch_direction(void);
 static	void			 complete_direction(PLpgSQL_stmt_fetch *fetch,
@@ -2503,7 +2505,13 @@ read_datatype(int tok)
 			if (tok_is_keyword(tok, &yylval,
 							   K_TYPE, "type"))
 			{
-				result = plpgsql_parse_wordtype(dtname);
+				/*
+				 * Now try to determine if there are any array indicators
+				 * [] or [ICONST] follow type definition, and then use
+				 * this information during type construction.
+				 */
+				const int arrndim = read_array_dims(tok);
+				result = plpgsql_parse_wordtype(dtname, arrndim);
 				if (result)
 					return result;
 			}
@@ -2527,7 +2535,8 @@ read_datatype(int tok)
 			if (tok_is_keyword(tok, &yylval,
 							   K_TYPE, "type"))
 			{
-				result = plpgsql_parse_cwordtype(dtnames);
+				const int arrndim = read_array_dims(tok);
+				result = plpgsql_parse_cwordtype(dtnames, arrndim);
 				if (result)
 					return result;
 			}
@@ -2582,6 +2591,67 @@ read_datatype(int tok)
 	return result;
 }
 
+/*
+ * Parse all items of array declaration: ('[' ICONST? ']')*
+ */
+static int
+read_array_dims(int tok) {
+	int n = 0;
+	int k;
+
+	while (true) {
+		tok = yylex();
+		k = read_single_array_dim(tok);
+		switch (k) {
+			case 1: /* ok */
+				n += 1;
+				break;
+
+			case 0: /* probably wrong syntax, but don't panic here */
+				plpgsql_push_back_token(tok);
+				return 0;
+
+			case -1: /* end of array def */
+				plpgsql_push_back_token(tok);
+				return n;
+
+			default:
+				Assert(false);
+		}
+	}
+}
+
+/* Parse single item of array declaration: '[' ']' or '[' ICONST ']' */
+static int
+read_single_array_dim(int tok) {
+	int tok2;
+	if (tok == '[') {
+		tok = yylex();
+		if (tok == ']') {
+			/* variant [] */
+			return 1;
+		}
+		else {
+			/* variant [ICONST] */
+			if (tok == ICONST) {
+				/* valid only if ICONST > 0 */
+				if (yylval.ival <= 0)
+					return 0;
+
+				tok2 = yylex();
+				if (tok2 == ']')
+					return 1;
+				else
+					plpgsql_push_back_token(tok2);
+			}
+			return 0;
+		}
+	}
+	else
+		return -1;
+}
+
+
 static PLpgSQL_stmt *
 make_execsql_stmt(int firsttoken, int location)
 {
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index d22fa68..826eabe 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -1588,6 +1588,35 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
 
 
 /* ----------
+ * build_arraydatatype	Try to construct array type from a type
+ *
+ * Function is used internally by plpgsql_parse_wordtype and
+ * plpgsql_parse_cwordtype to handle syntax word%TYPE[].
+ * 
+ * Returns datatype struct, or NULL if no match found for word.
+ * ----------
+ */
+static PLpgSQL_type *
+build_arraydatatype(Oid typoid, int32 typmod, Oid collation) {
+	Oid elem_typoid;
+
+	if (type_is_array(typoid))
+		/* copy array type */
+		return plpgsql_build_datatype(typoid, typmod, collation);
+	else
+	{
+		/* get array type for given scalar type */
+		elem_typoid = get_array_type(typoid);
+		if (elem_typoid != InvalidOid) 
+			return plpgsql_build_datatype(elem_typoid, typmod, collation);
+		else
+			/* typoid can't be element type of an array */
+			return NULL;
+	}
+}
+
+
+/* ----------
  * plpgsql_parse_wordtype	The scanner found word%TYPE. word can be
  *				a variable name or a basetype.
  *
@@ -1595,7 +1624,7 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
  * ----------
  */
 PLpgSQL_type *
-plpgsql_parse_wordtype(char *ident)
+plpgsql_parse_wordtype(char *ident, int arrndim)
 {
 	PLpgSQL_type *dtype;
 	PLpgSQL_nsitem *nse;
@@ -1613,7 +1642,12 @@ plpgsql_parse_wordtype(char *ident)
 		switch (nse->itemtype)
 		{
 			case PLPGSQL_NSTYPE_VAR:
-				return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+				dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+				if (arrndim <= 0)
+					return dtype;
+				else
+					return build_arraydatatype(dtype->typoid,
+								-1, plpgsql_curr_compile->fn_input_collation);
 
 				/* XXX perhaps allow REC/ROW here? */
 
@@ -1638,7 +1672,11 @@ plpgsql_parse_wordtype(char *ident)
 			return NULL;
 		}
 
-		dtype = build_datatype(typeTup, -1,
+		if (arrndim <= 0)
+			dtype = build_datatype(typeTup, -1,
+							   plpgsql_curr_compile->fn_input_collation);
+		else
+			dtype = build_arraydatatype(HeapTupleGetOid(typeTup), -1,
 							   plpgsql_curr_compile->fn_input_collation);
 
 		ReleaseSysCache(typeTup);
@@ -1658,7 +1696,7 @@ plpgsql_parse_wordtype(char *ident)
  * ----------
  */
 PLpgSQL_type *
-plpgsql_parse_cwordtype(List *idents)
+plpgsql_parse_cwordtype(List *idents, int arrndim)
 {
 	PLpgSQL_type *dtype = NULL;
 	PLpgSQL_nsitem *nse;
@@ -1690,6 +1728,9 @@ plpgsql_parse_cwordtype(List *idents)
 		if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
 		{
 			dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+			if (arrndim > 0)
+				dtype = build_arraydatatype(dtype->typoid, -1,
+							plpgsql_curr_compile->fn_input_collation);
 			goto done;
 		}
 
@@ -1749,9 +1790,14 @@ plpgsql_parse_cwordtype(List *idents)
 	 * return it
 	 */
 	MemoryContextSwitchTo(oldCxt);
-	dtype = build_datatype(typetup,
-						   attrStruct->atttypmod,
-						   attrStruct->attcollation);
+	if (arrndim <= 0)
+		dtype = build_datatype(typetup,
+							   attrStruct->atttypmod,
+							   attrStruct->attcollation);
+	else
+		dtype = build_arraydatatype(HeapTupleGetOid(typetup),
+									 attrStruct->atttypmod,
+									 attrStruct->attcollation);
 	MemoryContextSwitchTo(compile_tmp_cxt);
 
 done:
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index c543f1c..f2ea428 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -871,8 +871,8 @@ extern bool plpgsql_parse_dblword(char *word1, char *word2,
 					  PLwdatum *wdatum, PLcword *cword);
 extern bool plpgsql_parse_tripword(char *word1, char *word2, char *word3,
 					   PLwdatum *wdatum, PLcword *cword);
-extern PLpgSQL_type *plpgsql_parse_wordtype(char *ident);
-extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents);
+extern PLpgSQL_type *plpgsql_parse_wordtype(char *ident, int arrndim);
+extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents, int arrndim);
 extern PLpgSQL_type *plpgsql_parse_wordrowtype(char *ident);
 extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
 extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
begin;

create type composite AS (
	foo	integer,
	bar	varchar(256)
);

create or replace function foo() returns void aS $$
	declare
		-- base types
		a	boolean;
		b	integer;
		c	numeric(8,2);
		d	char;
		e	varchar(12);
		f	text;
		g	composite;

		-- arrays
		aa	boolean[];
		ba	integer[];
		ca	numeric(8,2)[];
		da	char[];
		ea	varchar(12)[];
		fa	text[];
		ga	composite[];
		
		-- copy scalar types
		type_a	a%TYPE[5];
		type_b	b%TYPE[3][3];
		type_c	c%TYPE[];
		type_d	d%TYPE[][][];
		type_e	e%TYPE[][7];
		type_f	f%TYPE[];
		type_g1	composite.foo%TYPE[12][5][][][];
		type_g2	composite.bar%TYPE[][][];

		-- copy array types
		typearr_a	aa%TYPE[5];
		typearr_b	ba%TYPE[3][3];
		typearr_c	ca%TYPE[];
		typearr_d	da%TYPE[][1][];
		typearr_e	ea%TYPE[][7];
		typearr_f	fa%TYPE[][];
		typearr_g	ga%TYPE[];
	begin
		--- shot types
		raise notice '%', pg_typeof(type_a);
		raise notice '%', pg_typeof(type_b);
		raise notice '%', pg_typeof(type_c);
		raise notice '%', pg_typeof(type_d);
		raise notice '%', pg_typeof(type_e);
		raise notice '%', pg_typeof(type_f);
		raise notice '%', pg_typeof(type_g1);
		raise notice '%', pg_typeof(type_g2);

		--- 
		raise notice '%', pg_typeof(typearr_a);
		raise notice '%', pg_typeof(typearr_b);
		raise notice '%', pg_typeof(typearr_c);
		raise notice '%', pg_typeof(typearr_d);
		raise notice '%', pg_typeof(typearr_e);
		raise notice '%', pg_typeof(typearr_f);
		raise notice '%', pg_typeof(typearr_g);
	end;
$$ language plpgsql;

select foo();

rollback;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to