Re: [HACKERS] [PL/pgSQL] %TYPE and array declaration - patch

2011-08-20 Thread Wojciech Muła
On Sun, 7 Aug 2011 14:57:36 +0200 Wojciech Muła
 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 al

[HACKERS] [PL/pgSQL] %TYPE and array declaration

2011-08-07 Thread Wojciech Muła
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.

w.

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