diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 5977534a62..0eedf4a55d 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 ... ;
+
+ Arrays of Copying Types and Row Types
+
+
+name variable%TYPE[];
+name table_name%ROWTYPE[];
+
+
+
+ Arrays of Copying Types and Row Types is defined by appending square brackets
+ ([]) to the %TYPE or %ROWTYPE.
+ Its definition is similar to PostgreSQL's arrays described in .
+ For example:
+
+user_id users.user_id%TYPE[];
+user_id users%ROWTYPE[];
+
+ 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.
+
+
+
+ An alternative syntax, which conforms to the SQL standard by using the keyword
+ ARRAY, can be used for one-dimensional or multi-dimensional
+ arrays too:
+
+user_id users.user_id%TYPE ARRAY;
+user_id users%ROWTYPE ARRAY[4][3];
+
+ As before, however, PostgreSQL does not enforce the size restriction in any case.
+
+
+
Record Types
@@ -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.
+
+
+ The RECORD cannot be used for declaration of variable
+ of an array type. "Copying Types" as shown in
+ are not supported too.
+
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..b207014682 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5825,3 +5825,73 @@ 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[]
+-- 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..0db50bc6fe 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4756,3 +4756,67 @@ 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;
+$$;
+
+-- 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;