Attached new patch
More explicit error messages based on type.
On 2023/10/16 18:15, Quan Zongliang wrote:
Implement TODO item:
PL/pgSQL
Incomplete item Allow handling of %TYPE arrays, e.g. tab.col%TYPE[]
As a first step, deal only with [], such as
xxx.yyy%TYPE[]
xxx%TYPE[]
It can be extended to support multi-dimensional and complex syntax in
the future.
--
Quan Zongliang
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 6a09bfdd67..c6377dcf2c 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -23,6 +23,7 @@
#include "parser/scanner.h"
#include "parser/scansup.h"
#include "utils/builtins.h"
+#include "utils/syscache.h"
#include "plpgsql.h"
@@ -76,6 +77,7 @@ static PLpgSQL_expr *read_sql_expression2(int
until, int until2,
int *endtoken);
static PLpgSQL_expr *read_sql_stmt(void);
static PLpgSQL_type *read_datatype(int tok);
+static PLpgSQL_type *read_datatype_array(PLpgSQL_type *elem_typ);
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,
@@ -2783,6 +2785,74 @@ read_sql_construct(int until,
return expr;
}
+static PLpgSQL_type *
+read_datatype_array(PLpgSQL_type *elem_typ)
+{
+ int tok;
+ HeapTuple type_tup = NULL;
+ Form_pg_type type_frm;
+
+ Assert(elem_typ);
+ if (!OidIsValid(elem_typ->typoid))
+ return elem_typ;
+
+ tok = yylex();
+ /* Next token is not square bracket. */
+ if (tok != '[')
+ {
+ plpgsql_push_back_token(tok);
+
+ return elem_typ;
+ }
+
+ tok = yylex();
+ /* For now, deal only with []. */
+ if (tok != ']')
+ {
+ plpgsql_push_back_token(tok);
+ plpgsql_push_back_token('[');
+
+ return elem_typ;
+ }
+
+ type_tup = SearchSysCache1(TYPEOID,
+
ObjectIdGetDatum(elem_typ->typoid));
+
+ /* should not happen. */
+ if (!HeapTupleIsValid(type_tup))
+ elog(ERROR, "cache lookup failed for type %u",
elem_typ->typoid);
+
+ type_frm = (Form_pg_type) GETSTRUCT(type_tup);
+
+ if (OidIsValid(type_frm->typarray))
+ {
+ Oid arrtyp_oid = type_frm->typarray;
+
+ ReleaseSysCache(type_tup);
+
+ return plpgsql_build_datatype(arrtyp_oid,
+ elem_typ->atttypmod,
+ elem_typ->collation,
+ NULL);
+ }
+
+ if (type_frm->typcategory != TYPCATEGORY_ARRAY)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s[]\" does not exist",
+ NameStr(type_frm->typname)),
+ parser_errposition(yylloc)));
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("do not support multi-dimensional arrays
in PL/pgSQL"),
+ parser_errposition(yylloc)));
+
+ ReleaseSysCache(type_tup);
+
+ return elem_typ;
+}
+
static PLpgSQL_type *
read_datatype(int tok)
{
@@ -2818,7 +2888,9 @@ read_datatype(int tok)
{
result = plpgsql_parse_wordtype(dtname);
if (result)
- return result;
+ {
+ return read_datatype_array(result);
+ }
}
else if (tok_is_keyword(tok, &yylval,
K_ROWTYPE, "rowtype"))
@@ -2842,7 +2914,9 @@ read_datatype(int tok)
{
result = plpgsql_parse_wordtype(dtname);
if (result)
- return result;
+ {
+ return read_datatype_array(result);
+ }
}
else if (tok_is_keyword(tok, &yylval,
K_ROWTYPE, "rowtype"))
@@ -2866,7 +2940,9 @@ read_datatype(int tok)
{
result = plpgsql_parse_cwordtype(dtnames);
if (result)
- return result;
+ {
+ return read_datatype_array(result);
+ }
}
else if (tok_is_keyword(tok, &yylval,
K_ROWTYPE, "rowtype"))
diff --git a/src/test/regress/expected/plpgsql.out
b/src/test/regress/expected/plpgsql.out
index 272f5d2111..0e9497faaf 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5814,6 +5814,53 @@ SELECT * FROM list_partitioned_table() AS t;
2
(2 rows)
+CREATE OR REPLACE FUNCTION array_partitioned_table()
+RETURNS SETOF partitioned_table.a%TYPE AS $$
+DECLARE
+ i int;
+ row partitioned_table%ROWTYPE;
+ a_val partitioned_table.a%TYPE[];
+ b_val partitioned_table.a%TYPE;
+ c_val b_val%TYPE[];
+BEGIN
+ i := 1;
+ FOR row IN SELECT * FROM partitioned_table ORDER BY a LOOP
+ a_val[i] := row.a;
+ c_val[i] := a_val[i];
+ i = i + 1;
+ END LOOP;
+ RETURN QUERY SELECT unnest(c_val);
+END; $$ LANGUAGE plpgsql;
+NOTICE: type reference partitioned_table.a%TYPE converted to integer
+SELECT * FROM array_partitioned_table() AS t;
+ t
+---
+ 1
+ 2
+(2 rows)
+
+CREATE OR REPLACE FUNCTION type_array_err1()
+RETURNS void AS $$
+DECLARE
+ i int[];
+ j i%TYPE[];
+BEGIN
+ RETURN;
+END; $$ LANGUAGE plpgsql;
+ERROR: do not support multi-dimensional arrays in PL/pgSQL
+LINE 5: j i%TYPE[];
+ ^
+CREATE OR REPLACE FUNCTION type_array_err2()
+RETURNS void AS $$
+DECLARE
+ i pg_node_tree;
+ j i%TYPE[];
+BEGIN
+ RETURN;
+END; $$ LANGUAGE plpgsql;
+ERROR: type "pg_node_tree[]" does not exist
+LINE 5: j i%TYPE[];
+ ^
--
-- Check argument name is used instead of $n in error message
--
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 924d524094..dfa491684a 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4748,6 +4748,44 @@ END; $$ LANGUAGE plpgsql;
SELECT * FROM list_partitioned_table() AS t;
+CREATE OR REPLACE FUNCTION array_partitioned_table()
+RETURNS SETOF partitioned_table.a%TYPE AS $$
+DECLARE
+ i int;
+ row partitioned_table%ROWTYPE;
+ a_val partitioned_table.a%TYPE[];
+ b_val partitioned_table.a%TYPE;
+ c_val b_val%TYPE[];
+BEGIN
+ i := 1;
+ FOR row IN SELECT * FROM partitioned_table ORDER BY a LOOP
+ a_val[i] := row.a;
+ c_val[i] := a_val[i];
+ i = i + 1;
+ END LOOP;
+ RETURN QUERY SELECT unnest(c_val);
+END; $$ LANGUAGE plpgsql;
+
+SELECT * FROM array_partitioned_table() AS t;
+
+CREATE OR REPLACE FUNCTION type_array_err1()
+RETURNS void AS $$
+DECLARE
+ i int[];
+ j i%TYPE[];
+BEGIN
+ RETURN;
+END; $$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION type_array_err2()
+RETURNS void AS $$
+DECLARE
+ i pg_node_tree;
+ j i%TYPE[];
+BEGIN
+ RETURN;
+END; $$ LANGUAGE plpgsql;
+
--
-- Check argument name is used instead of $n in error message
--