pá 24. 11. 2023 v 2:12 odesílatel Quan Zongliang <quanzongli...@yeah.net>
napsal:

>
>
> On 2023/11/24 03:39, Pavel Stehule wrote:
>
> >
> > I modified the documentation a little bit - we don't need to extra
> > propose SQL array syntax, I think.
> > I rewrote regress tests - we don't need to test unsupported
> > functionality (related to RECORD).
> >
> > - all tests passed
> >
> I wrote two examples of errors:
>    user_id users.user_id%ROWTYPE[];
>    user_id users.user_id%ROWTYPE ARRAY[4][3];
>

there were more issues in this part - the name "user_id" is a bad name for
a composite variable.  I renamed it.
+ I wrote a test related to usage type without array support.

Now, I think so this simple patch is ready for committers

Regards

Pavel



> Fixed.
>
> > Regards
> >
> > Pavel
> >
> >
> >      >
> >      > Regards
> >      >
> >      > Pavel
> >
From 8fc6d02a8cea6cc897e4290ad7724b494e330ef8 Mon Sep 17 00:00:00 2001
From: "ok...@github.com" <pavel.steh...@gmail.com>
Date: Thu, 23 Nov 2023 18:39:27 +0100
Subject: [PATCH] support of syntax %type[] and %rowtype[]

---
 doc/src/sgml/plpgsql.sgml             | 40 ++++++++++++++
 src/pl/plpgsql/src/pl_comp.c          | 23 ++++++++
 src/pl/plpgsql/src/pl_gram.y          | 60 +++++++++++++++-----
 src/pl/plpgsql/src/plpgsql.h          |  1 +
 src/test/regress/expected/plpgsql.out | 80 +++++++++++++++++++++++++++
 src/test/regress/sql/plpgsql.sql      | 73 ++++++++++++++++++++++++
 6 files changed, 264 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 5977534a62..aa848c034e 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -766,6 +766,40 @@ SELECT merge_fields(t.*) FROM table1 t WHERE ... ;
    </para>
   </sect2>
 
+  <sect2 id="plpgsql-declaration-typearrays">
+   <title>Arrays of Copying Types and Row Types</title>
+
+<synopsis>
+<replaceable>name</replaceable> variable%TYPE[];
+<replaceable>name</replaceable> table_name%ROWTYPE[];
+</synopsis>
+
+   <para>
+    Arrays of Copying Types and Row Types is defined by appending square brackets
+    (<literal>[]</literal>) to the <literal>%TYPE</literal> or <literal>%ROWTYPE</literal>.
+    Its definition is similar to PostgreSQL's arrays described in <xref linkend="arrays"/>.
+    For example:
+<programlisting>
+user_id users.user_id%TYPE[];
+users_row users%ROWTYPE[];
+</programlisting>
+    The syntax allows the exact size of arrays to be specified. However, the current
+    implementation ignores any supplied array size limits, i.e., the behavior is the
+    same as for arrays of unspecified length.
+   </para>
+
+   <para>
+    An alternative syntax, which conforms to the SQL standard by using the keyword
+    <literal>ARRAY</literal>, can be used for one-dimensional or multi-dimensional
+    arrays too:
+<programlisting>
+user_id users.user_id%TYPE ARRAY;
+users_row users%ROWTYPE ARRAY[4][3];
+</programlisting>
+    As before, however, PostgreSQL does not enforce the size restriction in any case.
+   </para>
+  </sect2>
+
   <sect2 id="plpgsql-declaration-records">
    <title>Record Types</title>
 
@@ -794,6 +828,12 @@ SELECT merge_fields(t.*) FROM table1 t WHERE ... ;
     calling query is parsed, whereas a record variable can change its row
     structure on-the-fly.
    </para>
+
+   <para>
+    The <literal>RECORD</literal> cannot be used for declaration of variable
+    of an array type. "Copying Types" as shown in <xref linkend="plpgsql-declaration-type"/>
+    are not supported too.
+   </para>
   </sect2>
 
   <sect2 id="plpgsql-declaration-collation">
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index a341cde2c1..a9cb15df6d 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -2095,6 +2095,29 @@ plpgsql_build_datatype(Oid typeOid, int32 typmod,
 	return typ;
 }
 
+/*
+ * Returns an array for type specified as argument.
+ */
+PLpgSQL_type *
+plpgsql_datatype_arrayof(PLpgSQL_type *dtype)
+{
+	Oid			array_typeid;
+
+	if (dtype->typisarray)
+		return dtype;
+
+	array_typeid = get_array_type(dtype->typoid);
+
+	if (!OidIsValid(array_typeid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("could not find array type for data type \"%s\"",
+						format_type_be(dtype->typoid))));
+
+	return plpgsql_build_datatype(array_typeid, dtype->atttypmod,
+								  dtype->collation, NULL);
+}
+
 /*
  * Utility subroutine to make a PLpgSQL_type struct given a pg_type entry
  * and additional details (see comments for plpgsql_build_datatype).
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 6a09bfdd67..aa9103cf0e 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -2789,7 +2789,7 @@ read_datatype(int tok)
 	StringInfoData ds;
 	char	   *type_name;
 	int			startlocation;
-	PLpgSQL_type *result;
+	PLpgSQL_type *result = NULL;
 	int			parenlevel = 0;
 
 	/* Should only be called while parsing DECLARE sections */
@@ -2817,15 +2817,11 @@ read_datatype(int tok)
 							   K_TYPE, "type"))
 			{
 				result = plpgsql_parse_wordtype(dtname);
-				if (result)
-					return result;
 			}
 			else if (tok_is_keyword(tok, &yylval,
 									K_ROWTYPE, "rowtype"))
 			{
 				result = plpgsql_parse_wordrowtype(dtname);
-				if (result)
-					return result;
 			}
 		}
 	}
@@ -2841,15 +2837,11 @@ read_datatype(int tok)
 							   K_TYPE, "type"))
 			{
 				result = plpgsql_parse_wordtype(dtname);
-				if (result)
-					return result;
 			}
 			else if (tok_is_keyword(tok, &yylval,
 									K_ROWTYPE, "rowtype"))
 			{
 				result = plpgsql_parse_wordrowtype(dtname);
-				if (result)
-					return result;
 			}
 		}
 	}
@@ -2865,19 +2857,61 @@ read_datatype(int tok)
 							   K_TYPE, "type"))
 			{
 				result = plpgsql_parse_cwordtype(dtnames);
-				if (result)
-					return result;
 			}
 			else if (tok_is_keyword(tok, &yylval,
 									K_ROWTYPE, "rowtype"))
 			{
 				result = plpgsql_parse_cwordrowtype(dtnames);
-				if (result)
-					return result;
 			}
 		}
 	}
 
+	/*
+	 * After %TYPE or %ROWTYPE syntax (the result type is known
+	 * already), we should to check syntax of an array declaration.
+	 * Supported syntax is same like SQL parser. Although array's
+	 * dimensions and dimension's sizes can be specified, they are
+	 * ignored.
+	 */
+	if (result)
+	{
+		bool		be_array = false;
+
+		tok = yylex();
+
+		/* Supported syntax: [ ARRAY ] [ '[' [ iconst ] ']' [ ... ] ] */
+		if (tok_is_keyword(tok, &yylval,
+						   K_ARRAY, "array"))
+		{
+			be_array = true;
+			tok = yylex();
+		}
+
+		if (tok == '[')
+		{
+			be_array = true;
+
+			while (tok == '[')
+			{
+				tok = yylex();
+				if (tok == ICONST)
+					tok = yylex();
+
+				if (tok != ']')
+					yyerror("syntax error, expected \"]\"");
+
+				tok = yylex();
+			}
+		}
+
+		plpgsql_push_back_token(tok);
+
+		if (be_array)
+			result = plpgsql_datatype_arrayof(result);
+
+		return result;
+	}
+
 	while (tok != ';')
 	{
 		if (tok == 0)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 9f0a912115..9da5e5b225 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1249,6 +1249,7 @@ extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
 extern PGDLLEXPORT PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
 														Oid collation,
 														TypeName *origtypname);
+extern PLpgSQL_type *plpgsql_datatype_arrayof(PLpgSQL_type *dtype);
 extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
 												PLpgSQL_type *dtype,
 												bool add2namespace);
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 272f5d2111..3d7e800956 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5825,3 +5825,83 @@ END; $$ LANGUAGE plpgsql;
 ERROR:  "x" is not a scalar variable
 LINE 3:   GET DIAGNOSTICS x = ROW_COUNT;
                           ^
+--
+-- test of %type[] and %rowtype[] syntax
+--
+-- check supported syntax
+do $$
+declare
+  v int;
+  v1 v%type;
+  v2 v%type[];
+  v3 v%type[1];
+  v4 v%type[][];
+  v5 v%type[1][3];
+  v6 v%type array;
+  v7 v%type array[];
+  v8 v%type array[1];
+  v9 v%type array[1][1];
+  v10 pg_catalog.pg_class%rowtype[];
+begin
+  raise notice '%', pg_typeof(v1);
+  raise notice '%', pg_typeof(v2);
+  raise notice '%', pg_typeof(v3);
+  raise notice '%', pg_typeof(v4);
+  raise notice '%', pg_typeof(v5);
+  raise notice '%', pg_typeof(v6);
+  raise notice '%', pg_typeof(v7);
+  raise notice '%', pg_typeof(v8);
+  raise notice '%', pg_typeof(v9);
+  raise notice '%', pg_typeof(v10);
+end;
+$$;
+NOTICE:  integer
+NOTICE:  integer[]
+NOTICE:  integer[]
+NOTICE:  integer[]
+NOTICE:  integer[]
+NOTICE:  integer[]
+NOTICE:  integer[]
+NOTICE:  integer[]
+NOTICE:  integer[]
+NOTICE:  pg_class[]
+-- some types doesn't support arrays
+do $$
+declare
+  v pg_node_tree;
+  v1 v%type[];
+begin
+end;
+$$;
+ERROR:  could not find array type for data type "pg_node_tree"
+CONTEXT:  compilation of PL/pgSQL function "inline_code_block" near line 4
+-- check functionality
+do $$
+declare
+  v1 int;
+  v2 varchar;
+  a1 v1%type[];
+  a2 v2%type[];
+begin
+  v1 := 10;
+  v2 := 'Hi';
+  a1 := array[v1,v1];
+  a2 := array[v2,v2];
+  raise notice '% %', a1, a2;
+end;
+$$;
+NOTICE:  {10,10} {Hi,Hi}
+create table plpgsql_test_table(a int, b varchar);
+insert into plpgsql_test_table values(1, 'first'), (2, 'second');
+do $$
+declare tg plpgsql_test_table%rowtype[];
+begin
+  tg := array(select plpgsql_test_table from plpgsql_test_table);
+  raise notice '%', tg;
+  tg := array(select row(a,b) from plpgsql_test_table);
+  raise notice '%', tg;
+end;
+$$;
+NOTICE:  {"(1,first)","(2,second)"}
+NOTICE:  {"(1,first)","(2,second)"}
+drop table plpgsql_test_table;
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 924d524094..b76ad588b3 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4756,3 +4756,76 @@ BEGIN
   GET DIAGNOSTICS x = ROW_COUNT;
   RETURN;
 END; $$ LANGUAGE plpgsql;
+
+--
+-- test of %type[] and %rowtype[] syntax
+--
+
+-- check supported syntax
+do $$
+declare
+  v int;
+  v1 v%type;
+  v2 v%type[];
+  v3 v%type[1];
+  v4 v%type[][];
+  v5 v%type[1][3];
+  v6 v%type array;
+  v7 v%type array[];
+  v8 v%type array[1];
+  v9 v%type array[1][1];
+  v10 pg_catalog.pg_class%rowtype[];
+begin
+  raise notice '%', pg_typeof(v1);
+  raise notice '%', pg_typeof(v2);
+  raise notice '%', pg_typeof(v3);
+  raise notice '%', pg_typeof(v4);
+  raise notice '%', pg_typeof(v5);
+  raise notice '%', pg_typeof(v6);
+  raise notice '%', pg_typeof(v7);
+  raise notice '%', pg_typeof(v8);
+  raise notice '%', pg_typeof(v9);
+  raise notice '%', pg_typeof(v10);
+end;
+$$;
+
+-- some types doesn't support arrays
+do $$
+declare
+  v pg_node_tree;
+  v1 v%type[];
+begin
+end;
+$$;
+
+-- check functionality
+do $$
+declare
+  v1 int;
+  v2 varchar;
+  a1 v1%type[];
+  a2 v2%type[];
+begin
+  v1 := 10;
+  v2 := 'Hi';
+  a1 := array[v1,v1];
+  a2 := array[v2,v2];
+  raise notice '% %', a1, a2;
+end;
+$$;
+
+create table plpgsql_test_table(a int, b varchar);
+
+insert into plpgsql_test_table values(1, 'first'), (2, 'second');
+
+do $$
+declare tg plpgsql_test_table%rowtype[];
+begin
+  tg := array(select plpgsql_test_table from plpgsql_test_table);
+  raise notice '%', tg;
+  tg := array(select row(a,b) from plpgsql_test_table);
+  raise notice '%', tg;
+end;
+$$;
+
+drop table plpgsql_test_table;
-- 
2.42.0

Reply via email to