public inbox for [email protected]help / color / mirror / Atom feed
[PATCH v5 2/2] Add a --outdated option to reindexdb 4+ messages / 4 participants [nested] [flat]
* [PATCH v5 2/2] Add a --outdated option to reindexdb @ 2021-02-24 17:33 Julien Rouhaud <[email protected]> 0 siblings, 0 replies; 4+ messages in thread From: Julien Rouhaud @ 2021-02-24 17:33 UTC (permalink / raw) This uses the new OUTDATED option for REINDEX. If user asks for multiple job, the list of tables to process will be sorted by the total size of underlying indexes that have outdated dependency. Author: Julien Rouhaud <[email protected]> Reviewed-by: Discussion: https://postgr.es/m/20201203093143.GA64934%40nol --- src/bin/scripts/reindexdb.c | 143 ++++++++++++++++++++++++----- src/bin/scripts/t/090_reindexdb.pl | 34 ++++++- 2 files changed, 151 insertions(+), 26 deletions(-) diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index cf28176243..369d164e65 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -35,20 +35,23 @@ typedef enum ReindexType static SimpleStringList *get_parallel_object_list(PGconn *conn, ReindexType type, SimpleStringList *user_list, + bool outdated, bool echo); static void reindex_one_database(const ConnParams *cparams, ReindexType type, SimpleStringList *user_list, const char *progname, bool echo, bool verbose, bool concurrently, - int concurrentCons, const char *tablespace); + int concurrentCons, const char *tablespace, + bool outdated); static void reindex_all_databases(ConnParams *cparams, const char *progname, bool echo, bool quiet, bool verbose, bool concurrently, - int concurrentCons, const char *tablespace); + int concurrentCons, const char *tablespace, + bool outdated); static void run_reindex_command(PGconn *conn, ReindexType type, const char *name, bool echo, bool verbose, bool concurrently, bool async, - const char *tablespace); + const char *tablespace, bool outdated); static void help(const char *progname); @@ -74,6 +77,7 @@ main(int argc, char *argv[]) {"concurrently", no_argument, NULL, 1}, {"maintenance-db", required_argument, NULL, 2}, {"tablespace", required_argument, NULL, 3}, + {"outdated", no_argument, NULL, 4}, {NULL, 0, NULL, 0} }; @@ -95,6 +99,7 @@ main(int argc, char *argv[]) bool quiet = false; bool verbose = false; bool concurrently = false; + bool outdated = false; SimpleStringList indexes = {NULL, NULL}; SimpleStringList tables = {NULL, NULL}; SimpleStringList schemas = {NULL, NULL}; @@ -170,6 +175,9 @@ main(int argc, char *argv[]) case 3: tablespace = pg_strdup(optarg); break; + case 4: + outdated = true; + break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); @@ -234,7 +242,8 @@ main(int argc, char *argv[]) cparams.dbname = maintenance_db; reindex_all_databases(&cparams, progname, echo, quiet, verbose, - concurrently, concurrentCons, tablespace); + concurrently, concurrentCons, tablespace, + outdated); } else if (syscatalog) { @@ -253,12 +262,17 @@ main(int argc, char *argv[]) pg_log_error("cannot reindex specific index(es) and system catalogs at the same time"); exit(1); } - if (concurrentCons > 1) { pg_log_error("cannot use multiple jobs to reindex system catalogs"); exit(1); } + if (outdated) + { + pg_log_error("cannot filter indexes having outdated dependencies " + "and reindex system catalogs at the same time"); + exit(1); + } if (dbname == NULL) { @@ -274,7 +288,7 @@ main(int argc, char *argv[]) reindex_one_database(&cparams, REINDEX_SYSTEM, NULL, progname, echo, verbose, - concurrently, 1, tablespace); + concurrently, 1, tablespace, outdated); } else { @@ -304,17 +318,20 @@ main(int argc, char *argv[]) if (schemas.head != NULL) reindex_one_database(&cparams, REINDEX_SCHEMA, &schemas, progname, echo, verbose, - concurrently, concurrentCons, tablespace); + concurrently, concurrentCons, tablespace, + outdated); if (indexes.head != NULL) reindex_one_database(&cparams, REINDEX_INDEX, &indexes, progname, echo, verbose, - concurrently, 1, tablespace); + concurrently, 1, tablespace, + outdated); if (tables.head != NULL) reindex_one_database(&cparams, REINDEX_TABLE, &tables, progname, echo, verbose, - concurrently, concurrentCons, tablespace); + concurrently, concurrentCons, tablespace, + outdated); /* * reindex database only if neither index nor table nor schema is @@ -323,7 +340,8 @@ main(int argc, char *argv[]) if (indexes.head == NULL && tables.head == NULL && schemas.head == NULL) reindex_one_database(&cparams, REINDEX_DATABASE, NULL, progname, echo, verbose, - concurrently, concurrentCons, tablespace); + concurrently, concurrentCons, tablespace, + outdated); } exit(0); @@ -334,7 +352,7 @@ reindex_one_database(const ConnParams *cparams, ReindexType type, SimpleStringList *user_list, const char *progname, bool echo, bool verbose, bool concurrently, int concurrentCons, - const char *tablespace) + const char *tablespace, bool outdated) { PGconn *conn; SimpleStringListCell *cell; @@ -363,6 +381,14 @@ reindex_one_database(const ConnParams *cparams, ReindexType type, exit(1); } + if (outdated && PQserverVersion(conn) < 140000) + { + PQfinish(conn); + pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s", + "outdated", "14"); + exit(1); + } + if (!parallel) { switch (process_type) @@ -399,14 +425,24 @@ reindex_one_database(const ConnParams *cparams, ReindexType type, */ if (concurrently) pg_log_warning("cannot reindex system catalogs concurrently, skipping all"); + else if (outdated) + { + /* + * The only supported kind of object that can be outdated + * is collation. No system catalog has any index that can + * depend on an outdated collation, so skip system + * catalogs. + */ + } else run_reindex_command(conn, REINDEX_SYSTEM, PQdb(conn), echo, verbose, concurrently, false, - tablespace); + tablespace, outdated); /* Build a list of relations from the database */ process_list = get_parallel_object_list(conn, process_type, - user_list, echo); + user_list, outdated, + echo); process_type = REINDEX_TABLE; /* Bail out if nothing to process */ @@ -419,7 +455,8 @@ reindex_one_database(const ConnParams *cparams, ReindexType type, /* Build a list of relations from all the schemas */ process_list = get_parallel_object_list(conn, process_type, - user_list, echo); + user_list, outdated, + echo); process_type = REINDEX_TABLE; /* Bail out if nothing to process */ @@ -484,7 +521,8 @@ reindex_one_database(const ConnParams *cparams, ReindexType type, ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL); run_reindex_command(free_slot->connection, process_type, objname, - echo, verbose, concurrently, true, tablespace); + echo, verbose, concurrently, true, tablespace, + outdated); cell = cell->next; } while (cell != NULL); @@ -509,7 +547,7 @@ finish: static void run_reindex_command(PGconn *conn, ReindexType type, const char *name, bool echo, bool verbose, bool concurrently, bool async, - const char *tablespace) + const char *tablespace, bool outdated) { const char *paren = "("; const char *comma = ", "; @@ -536,6 +574,12 @@ run_reindex_command(PGconn *conn, ReindexType type, const char *name, sep = comma; } + if (outdated) + { + appendPQExpBuffer(&sql, "%sOUTDATED", sep); + sep = comma; + } + if (sep != paren) appendPQExpBufferStr(&sql, ") "); @@ -641,7 +685,7 @@ run_reindex_command(PGconn *conn, ReindexType type, const char *name, */ static SimpleStringList * get_parallel_object_list(PGconn *conn, ReindexType type, - SimpleStringList *user_list, bool echo) + SimpleStringList *user_list, bool outdated, bool echo) { PQExpBufferData catalog_query; PQExpBufferData buf; @@ -660,16 +704,41 @@ get_parallel_object_list(PGconn *conn, ReindexType type, { case REINDEX_DATABASE: Assert(user_list == NULL); + appendPQExpBufferStr(&catalog_query, "SELECT c.relname, ns.nspname\n" " FROM pg_catalog.pg_class c\n" " JOIN pg_catalog.pg_namespace ns" - " ON c.relnamespace = ns.oid\n" + " ON c.relnamespace = ns.oid\n"); + + if (outdated) + { + appendPQExpBufferStr(&catalog_query, + " JOIN pg_catalog.pg_index i" + " ON c.oid = i.indrelid\n" + " JOIN pg_catalog.pg_class ci" + " ON i.indexrelid = ci.oid\n"); + } + + appendPQExpBufferStr(&catalog_query, " WHERE ns.nspname != 'pg_catalog'\n" " AND c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " - CppAsString2(RELKIND_MATVIEW) ")\n" - " ORDER BY c.relpages DESC;"); + CppAsString2(RELKIND_MATVIEW) ")\n"); + + if (outdated) + { + appendPQExpBufferStr(&catalog_query, + " GROUP BY c.relname, ns.nspname\n" + " ORDER BY sum(ci.relpages)" + " FILTER (WHERE pg_catalog.pg_index_has_outdated_dependency(ci.oid)) DESC;"); + } + else + { + appendPQExpBufferStr(&catalog_query, + " ORDER BY c.relpages DESC;"); + } + break; case REINDEX_SCHEMA: @@ -687,7 +756,18 @@ get_parallel_object_list(PGconn *conn, ReindexType type, "SELECT c.relname, ns.nspname\n" " FROM pg_catalog.pg_class c\n" " JOIN pg_catalog.pg_namespace ns" - " ON c.relnamespace = ns.oid\n" + " ON c.relnamespace = ns.oid\n"); + + if (outdated) + { + appendPQExpBufferStr(&catalog_query, + " JOIN pg_catalog.pg_index i" + " ON c.oid = i.indrelid\n" + " JOIN pg_catalog.pg_class ci" + " ON i.indexrelid = ci.oid\n"); + } + + appendPQExpBufferStr(&catalog_query, " WHERE c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " CppAsString2(RELKIND_MATVIEW) ")\n" @@ -705,8 +785,20 @@ get_parallel_object_list(PGconn *conn, ReindexType type, appendStringLiteralConn(&catalog_query, nspname, conn); } - appendPQExpBufferStr(&catalog_query, ")\n" - " ORDER BY c.relpages DESC;"); + appendPQExpBufferStr(&catalog_query, ")\n"); + + if (outdated) + { + appendPQExpBufferStr(&catalog_query, + " GROUP BY c.relname, ns.nspname\n" + " ORDER BY sum(ci.relpages)" + " FILTER (WHERE pg_catalog.pg_index_has_outdated_dependency(ci.oid)) DESC;"); + } + else + { + appendPQExpBufferStr(&catalog_query, + " ORDER BY c.relpages DESC;"); + } } break; @@ -754,7 +846,7 @@ static void reindex_all_databases(ConnParams *cparams, const char *progname, bool echo, bool quiet, bool verbose, bool concurrently, int concurrentCons, - const char *tablespace) + const char *tablespace, bool outdated) { PGconn *conn; PGresult *result; @@ -778,7 +870,7 @@ reindex_all_databases(ConnParams *cparams, reindex_one_database(cparams, REINDEX_DATABASE, NULL, progname, echo, verbose, concurrently, - concurrentCons, tablespace); + concurrentCons, tablespace, outdated); } PQclear(result); @@ -797,6 +889,7 @@ help(const char *progname) printf(_(" -e, --echo show the commands being sent to the server\n")); printf(_(" -i, --index=INDEX recreate specific index(es) only\n")); printf(_(" -j, --jobs=NUM use this many concurrent connections to reindex\n")); + printf(_(" --outdated only process indexes having outdated depencies\n")); printf(_(" -q, --quiet don't write any messages\n")); printf(_(" -s, --system reindex system catalogs\n")); printf(_(" -S, --schema=SCHEMA reindex specific schema(s) only\n")); diff --git a/src/bin/scripts/t/090_reindexdb.pl b/src/bin/scripts/t/090_reindexdb.pl index 159b637230..b60cac6081 100644 --- a/src/bin/scripts/t/090_reindexdb.pl +++ b/src/bin/scripts/t/090_reindexdb.pl @@ -3,7 +3,7 @@ use warnings; use PostgresNode; use TestLib; -use Test::More tests => 58; +use Test::More tests => 70; program_help_ok('reindexdb'); program_version_ok('reindexdb'); @@ -174,6 +174,9 @@ $node->command_fails( $node->command_fails( [ 'reindexdb', '-j', '2', '-i', 'i1', 'postgres' ], 'parallel reindexdb cannot process indexes'); +$node->command_fails( + [ 'reindexdb', '-s', '--outdated' ], + 'cannot reindex system catalog and filter indexes having outdated dependencies'); $node->issues_sql_like( [ 'reindexdb', '-j', '2', 'postgres' ], qr/statement:\ REINDEX SYSTEM postgres; @@ -196,3 +199,32 @@ $node->command_checks_all( qr/^reindexdb: warning: cannot reindex system catalogs concurrently, skipping all/s ], 'parallel reindexdb for system with --concurrently skips catalogs'); + +# Temporarily downgrade client-min-message to get the no-op report +$ENV{PGOPTIONS} = '--client-min-messages=NOTICE'; +$node->command_checks_all( + [ 'reindexdb', '--outdated', '-v', '-t', 's1.t1', 'postgres' ], + 0, + [qr/^$/], + [qr/table "t1" has no indexes to reindex/], + 'verbose reindexdb for outdated dependencies on a specific table reports no-op tables'); + +$node->command_checks_all( + [ 'reindexdb', '--outdated', '-v', '-d', 'postgres' ], + 0, + [qr/^$/], + [qr/^$/], + 'verbose reindexdb for outdated dependencies database wide silently ignore all tables'); +$node->command_checks_all( + [ 'reindexdb', '--outdated', '-v', '-j', '2', '-d', 'postgres' ], + 0, + [qr/^$/], + [qr/table "t1" has no indexes to reindex/], + 'parallel verbose reindexdb for outdated dependencies database wide reports no-op tables'); + +# Switch back to WARNING client-min-message +$ENV{PGOPTIONS} = '--client-min-messages=WARNING'; +$node->issues_sql_like( + [ 'reindexdb', '--outdated', '-t', 's1.t1', 'postgres' ], + qr/.*statement: REINDEX \(OUTDATED\) TABLE s1\.t1;/, + 'reindexdb for outdated dependencies specify the OUTDATED keyword'); -- 2.30.1 --oao5se3im4osqowp-- ^ permalink raw reply [nested|flat] 4+ messages in thread
* Better error messages for %TYPE and %ROWTYPE in plpgsql @ 2024-02-26 20:02 Tom Lane <[email protected]> 2024-02-26 20:10 ` Re: Better error messages for %TYPE and %ROWTYPE in plpgsql Pavel Stehule <[email protected]> 2024-02-27 00:40 ` Re: Better error messages for %TYPE and %ROWTYPE in plpgsql Andy Fan <[email protected]> 0 siblings, 2 replies; 4+ messages in thread From: Tom Lane @ 2024-02-26 20:02 UTC (permalink / raw) To: [email protected] Per recent discussion[1], plpgsql returns fairly unhelpful "syntax error" messages when a %TYPE or %ROWTYPE construct references a nonexistent object. Here's a quick little finger exercise to try to improve that. The basic point is that plpgsql_parse_wordtype and friends are designed to return NULL rather than failing (at least when it's easy to do so), but that leaves the caller without enough info to deliver a good error message. There is only one caller, and it has no use at all for this behavior, so let's just change those functions to throw appropriate errors. Amusingly, plpgsql_parse_wordrowtype was already behaving that way, and plpgsql_parse_cwordrowtype did so in more cases than not, so we didn't even have a consistent "return NULL" story. Along the way I got rid of plpgsql_parse_cwordtype's restriction on what relkinds can be referenced. I don't really see the point of that --- as long as the relation has the desired column, the column's type is surely well-defined. regards, tom lane [1] https://www.postgresql.org/message-id/flat/88b574f4-cc08-46c5-826b-020849e5a356%40gelassene-pferde.b... Attachments: [text/x-diff] v1-improve-percent-type-errors.patch (9.3K, ../../[email protected]/2-v1-improve-percent-type-errors.patch) download | inline diff: diff --git a/src/pl/plpgsql/src/expected/plpgsql_misc.out b/src/pl/plpgsql/src/expected/plpgsql_misc.out index faddba2511..a6511df08e 100644 --- a/src/pl/plpgsql/src/expected/plpgsql_misc.out +++ b/src/pl/plpgsql/src/expected/plpgsql_misc.out @@ -29,3 +29,39 @@ CREATE OR REPLACE PROCEDURE public.test2(IN x integer) BEGIN ATOMIC SELECT (x + 2); END +-- Test %TYPE and %ROWTYPE error cases +create table misc_table(f1 int); +do $$ declare x foo%type; begin end $$; +ERROR: variable "foo" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x notice%type; begin end $$; -- covers unreserved-keyword case +ERROR: variable "notice" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x foo.bar%type; begin end $$; +ERROR: relation "foo" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x foo.bar.baz%type; begin end $$; +ERROR: schema "foo" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x public.foo.bar%type; begin end $$; +ERROR: relation "public.foo" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x public.misc_table.zed%type; begin end $$; +ERROR: column "zed" of relation "misc_table" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x foo%rowtype; begin end $$; +ERROR: relation "foo" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x notice%rowtype; begin end $$; -- covers unreserved-keyword case +ERROR: relation "notice" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x foo.bar%rowtype; begin end $$; +ERROR: schema "foo" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x foo.bar.baz%rowtype; begin end $$; +ERROR: cross-database references are not implemented: "foo.bar.baz" +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x public.foo%rowtype; begin end $$; +ERROR: relation "public.foo" does not exist +CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1 +do $$ declare x public.misc_table%rowtype; begin end $$; diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index f18e8e3c64..f1bce708d6 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -1599,7 +1599,7 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3, * plpgsql_parse_wordtype The scanner found word%TYPE. word should be * a pre-existing variable name. * - * Returns datatype struct, or NULL if no match found for word. + * Returns datatype struct. Throws error if no match found for word. * ---------- */ PLpgSQL_type * @@ -1623,15 +1623,15 @@ plpgsql_parse_wordtype(char *ident) case PLPGSQL_NSTYPE_REC: return ((PLpgSQL_rec *) (plpgsql_Datums[nse->itemno]))->datatype; default: - return NULL; + break; } } - /* - * Nothing found - up to now it's a word without any special meaning for - * us. - */ - return NULL; + /* No match, complain */ + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("variable \"%s\" does not exist", ident))); + return NULL; /* keep compiler quiet */ } @@ -1639,7 +1639,8 @@ plpgsql_parse_wordtype(char *ident) * plpgsql_parse_cwordtype Same lookup for compositeword%TYPE * * Here, we allow either a block-qualified variable name, or a reference - * to a column of some table. + * to a column of some table. (If we must throw error, we assume that the + * latter case was intended.) * ---------- */ PLpgSQL_type * @@ -1648,12 +1649,11 @@ plpgsql_parse_cwordtype(List *idents) PLpgSQL_type *dtype = NULL; PLpgSQL_nsitem *nse; int nnames; - const char *fldname; + RangeVar *relvar = NULL; + const char *fldname = NULL; Oid classOid; - HeapTuple classtup = NULL; HeapTuple attrtup = NULL; HeapTuple typetup = NULL; - Form_pg_class classStruct; Form_pg_attribute attrStruct; MemoryContext oldCxt; @@ -1688,57 +1688,39 @@ plpgsql_parse_cwordtype(List *idents) /* * First word could also be a table name */ - classOid = RelnameGetRelid(strVal(linitial(idents))); - if (!OidIsValid(classOid)) - goto done; + relvar = makeRangeVar(NULL, + strVal(linitial(idents)), + -1); fldname = strVal(lsecond(idents)); } - else if (list_length(idents) == 3) + else { - RangeVar *relvar; - /* * We could check for a block-qualified reference to a field of a * record variable, but %TYPE is documented as applying to variables, * not fields of variables. Things would get rather ambiguous if we * allowed either interpretation. */ - relvar = makeRangeVar(strVal(linitial(idents)), - strVal(lsecond(idents)), - -1); - /* Can't lock relation - we might not have privileges. */ - classOid = RangeVarGetRelid(relvar, NoLock, true); - if (!OidIsValid(classOid)) - goto done; - fldname = strVal(lthird(idents)); - } - else - goto done; + List *rvnames; - classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(classOid)); - if (!HeapTupleIsValid(classtup)) - goto done; - classStruct = (Form_pg_class) GETSTRUCT(classtup); + Assert(list_length(idents) > 2); + rvnames = list_delete_last(list_copy(idents)); + relvar = makeRangeVarFromNameList(rvnames); + fldname = strVal(llast(idents)); + } - /* - * It must be a relation, sequence, view, materialized view, composite - * type, or foreign table - */ - if (classStruct->relkind != RELKIND_RELATION && - classStruct->relkind != RELKIND_SEQUENCE && - classStruct->relkind != RELKIND_VIEW && - classStruct->relkind != RELKIND_MATVIEW && - classStruct->relkind != RELKIND_COMPOSITE_TYPE && - classStruct->relkind != RELKIND_FOREIGN_TABLE && - classStruct->relkind != RELKIND_PARTITIONED_TABLE) - goto done; + /* Look up relation name. Can't lock it - we might not have privileges. */ + classOid = RangeVarGetRelid(relvar, NoLock, false); /* * Fetch the named table field and its type */ attrtup = SearchSysCacheAttName(classOid, fldname); if (!HeapTupleIsValid(attrtup)) - goto done; + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + fldname, relvar->relname))); attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); typetup = SearchSysCache1(TYPEOID, @@ -1759,8 +1741,6 @@ plpgsql_parse_cwordtype(List *idents) MemoryContextSwitchTo(plpgsql_compile_tmp_cxt); done: - if (HeapTupleIsValid(classtup)) - ReleaseSysCache(classtup); if (HeapTupleIsValid(attrtup)) ReleaseSysCache(attrtup); if (HeapTupleIsValid(typetup)) @@ -1824,16 +1804,12 @@ plpgsql_parse_cwordrowtype(List *idents) * As above, this is a relation lookup but could be a type lookup if we * weren't being backwards-compatible about error wording. */ - if (list_length(idents) != 2) - return NULL; /* Avoid memory leaks in long-term function context */ oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt); /* Look up relation name. Can't lock it - we might not have privileges. */ - relvar = makeRangeVar(strVal(linitial(idents)), - strVal(lsecond(idents)), - -1); + relvar = makeRangeVarFromNameList(idents); classOid = RangeVarGetRelid(relvar, NoLock, false); /* Some relkinds lack type OIDs */ @@ -1842,7 +1818,7 @@ plpgsql_parse_cwordrowtype(List *idents) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("relation \"%s\" does not have a composite type", - strVal(lsecond(idents))))); + relvar->relname))); MemoryContextSwitchTo(oldCxt); diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y index e630498e82..bef33d58a2 100644 --- a/src/pl/plpgsql/src/pl_gram.y +++ b/src/pl/plpgsql/src/pl_gram.y @@ -2810,10 +2810,7 @@ read_datatype(int tok) /* * If we have a simple or composite identifier, check for %TYPE and - * %ROWTYPE constructs. (Note that if plpgsql_parse_wordtype et al fail - * to recognize the identifier, we'll fall through and pass the whole - * string to parse_datatype, which will assuredly give an unhelpful - * "syntax error". Should we try to give a more specific error?) + * %ROWTYPE constructs. */ if (tok == T_WORD) { diff --git a/src/pl/plpgsql/src/sql/plpgsql_misc.sql b/src/pl/plpgsql/src/sql/plpgsql_misc.sql index 71a8fc232e..d3a7f703a7 100644 --- a/src/pl/plpgsql/src/sql/plpgsql_misc.sql +++ b/src/pl/plpgsql/src/sql/plpgsql_misc.sql @@ -20,3 +20,20 @@ $$; \sf test1 \sf test2 + +-- Test %TYPE and %ROWTYPE error cases +create table misc_table(f1 int); + +do $$ declare x foo%type; begin end $$; +do $$ declare x notice%type; begin end $$; -- covers unreserved-keyword case +do $$ declare x foo.bar%type; begin end $$; +do $$ declare x foo.bar.baz%type; begin end $$; +do $$ declare x public.foo.bar%type; begin end $$; +do $$ declare x public.misc_table.zed%type; begin end $$; + +do $$ declare x foo%rowtype; begin end $$; +do $$ declare x notice%rowtype; begin end $$; -- covers unreserved-keyword case +do $$ declare x foo.bar%rowtype; begin end $$; +do $$ declare x foo.bar.baz%rowtype; begin end $$; +do $$ declare x public.foo%rowtype; begin end $$; +do $$ declare x public.misc_table%rowtype; begin end $$; ^ permalink raw reply [nested|flat] 4+ messages in thread
* Re: Better error messages for %TYPE and %ROWTYPE in plpgsql 2024-02-26 20:02 Better error messages for %TYPE and %ROWTYPE in plpgsql Tom Lane <[email protected]> @ 2024-02-26 20:10 ` Pavel Stehule <[email protected]> 1 sibling, 0 replies; 4+ messages in thread From: Pavel Stehule @ 2024-02-26 20:10 UTC (permalink / raw) To: Tom Lane <[email protected]>; +Cc: [email protected] po 26. 2. 2024 v 21:02 odesÃlatel Tom Lane <[email protected]> napsal: > Per recent discussion[1], plpgsql returns fairly unhelpful "syntax > error" messages when a %TYPE or %ROWTYPE construct references a > nonexistent object. Here's a quick little finger exercise to try > to improve that. > > The basic point is that plpgsql_parse_wordtype and friends are > designed to return NULL rather than failing (at least when it's > easy to do so), but that leaves the caller without enough info > to deliver a good error message. There is only one caller, > and it has no use at all for this behavior, so let's just > change those functions to throw appropriate errors. Amusingly, > plpgsql_parse_wordrowtype was already behaving that way, and > plpgsql_parse_cwordrowtype did so in more cases than not, > so we didn't even have a consistent "return NULL" story. > > Along the way I got rid of plpgsql_parse_cwordtype's restriction > on what relkinds can be referenced. I don't really see the > point of that --- as long as the relation has the desired > column, the column's type is surely well-defined. > +1 Pavel > regards, tom lane > > [1] > https://www.postgresql.org/message-id/flat/88b574f4-cc08-46c5-826b-020849e5a356%40gelassene-pferde.b... > > ^ permalink raw reply [nested|flat] 4+ messages in thread
* Re: Better error messages for %TYPE and %ROWTYPE in plpgsql 2024-02-26 20:02 Better error messages for %TYPE and %ROWTYPE in plpgsql Tom Lane <[email protected]> @ 2024-02-27 00:40 ` Andy Fan <[email protected]> 1 sibling, 0 replies; 4+ messages in thread From: Andy Fan @ 2024-02-27 00:40 UTC (permalink / raw) To: Tom Lane <[email protected]>; +Cc: [email protected] > Per recent discussion[1], plpgsql returns fairly unhelpful "syntax > error" messages when a %TYPE or %ROWTYPE construct references a > nonexistent object. Here's a quick little finger exercise to try > to improve that. Looks this modify the error message, I want to know how ould we treat error-message-compatible issue during minor / major upgrade. I'm not sure if my question is too inconceivable, I ask this because one of my patch [1] has blocked on this kind of issue [only] for 2 months and actaully the error-message-compatible requirement was metioned by me at the first and resolve it by adding a odd parameter. Then the odd parameter blocked the whole process. [1] https://www.postgresql.org/message-id/[email protected] -- Best Regards Andy Fan ^ permalink raw reply [nested|flat] 4+ messages in thread
end of thread, other threads:[~2024-02-27 00:40 UTC | newest] Thread overview: 4+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2021-02-24 17:33 [PATCH v5 2/2] Add a --outdated option to reindexdb Julien Rouhaud <[email protected]> 2024-02-26 20:02 Better error messages for %TYPE and %ROWTYPE in plpgsql Tom Lane <[email protected]> 2024-02-26 20:10 ` Re: Better error messages for %TYPE and %ROWTYPE in plpgsql Pavel Stehule <[email protected]> 2024-02-27 00:40 ` Re: Better error messages for %TYPE and %ROWTYPE in plpgsql Andy Fan <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox