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