Re: pg_get_expr locking
От | Tom Lane |
---|---|
Тема | Re: pg_get_expr locking |
Дата | |
Msg-id | 1240055.1707423659@sss.pgh.pa.us обсуждение исходный текст |
Ответ на | Re: pg_get_expr locking (Tom Lane <tgl@sss.pgh.pa.us>) |
Список | pgsql-hackers |
I wrote: > Peter Eisentraut <peter@eisentraut.org> writes: >> I think the situation is that one test (domain) runs pg_get_expr as part >> of an information_schema view, while at the same time another test >> (alter_table) drops a table that the pg_get_expr is just processing. > The test case that's failing is, IIUC, > +SELECT * FROM information_schema.domain_constraints > + WHERE domain_name IN ('con', 'dom', 'pos_int', 'things') > + ORDER BY constraint_name; Oh, scratch that: there are two confusingly lookalike queries in the patch. The one that is failing is SELECT * FROM information_schema.check_constraints WHERE (constraint_schema, constraint_name) IN (SELECT constraint_schema, constraint_name FROM information_schema.domain_constraints WHERE domain_name IN ('con', 'dom', 'pos_int', 'things')) ORDER BY constraint_name; and we have trouble because the evaluation of pg_get_expr in check_constraints is done before the semijoin that would restrict it to just the desired objects. After looking at the code I'm less worried about the permissions-checking angle than I was before, because I see that pg_get_expr already takes a transient AccessShareLock on the rel, down inside set_relation_column_names. This is not ideal from a permissions standpoint perhaps, but it's probably OK considering we've done that for a long time. We just need to hold that lock a little while longer. I propose the attached as a reasonably localized fix. We could imagine a more aggressive refactoring that would allow passing down the Relation instead of re-opening it in set_relation_column_names, but I doubt it's worth the trouble. regards, tom lane diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index b625f471a8..644966721f 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -352,8 +352,7 @@ static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags, bool attrsOnly, bool missing_ok); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags, bool missing_ok); -static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, - int prettyFlags); +static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags); static int print_function_arguments(StringInfo buf, HeapTuple proctup, bool print_table_args, bool print_defaults); static void print_function_rettype(StringInfo buf, HeapTuple proctup); @@ -2615,6 +2622,11 @@ decompile_column_index_array(Datum column_index_array, Oid relId, * partial indexes, column default expressions, etc. We also support * Var-free expressions, for which the OID can be InvalidOid. * + * If the OID is nonzero but not actually valid, don't throw an error, + * just return NULL. This is a bit questionable, but it's what we've + * done historically, and it can help avoid unwanted failures when + * examining catalog entries for just-deleted relations. + * * We expect this function to work, or throw a reasonably clean error, * for any node tree that can appear in a catalog pg_node_tree column. * Query trees, such as those appearing in pg_rewrite.ev_action, are @@ -2627,29 +2639,16 @@ pg_get_expr(PG_FUNCTION_ARGS) { text *expr = PG_GETARG_TEXT_PP(0); Oid relid = PG_GETARG_OID(1); + text *result; int prettyFlags; - char *relname; prettyFlags = PRETTYFLAG_INDENT; - if (OidIsValid(relid)) - { - /* Get the name for the relation */ - relname = get_rel_name(relid); - - /* - * If the OID isn't actually valid, don't throw an error, just return - * NULL. This is a bit questionable, but it's what we've done - * historically, and it can help avoid unwanted failures when - * examining catalog entries for just-deleted relations. - */ - if (relname == NULL) - PG_RETURN_NULL(); - } + result = pg_get_expr_worker(expr, relid, prettyFlags); + if (result) + PG_RETURN_TEXT_P(result); else - relname = NULL; - - PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags)); + PG_RETURN_NULL(); } Datum @@ -2658,33 +2657,27 @@ pg_get_expr_ext(PG_FUNCTION_ARGS) text *expr = PG_GETARG_TEXT_PP(0); Oid relid = PG_GETARG_OID(1); bool pretty = PG_GETARG_BOOL(2); + text *result; int prettyFlags; - char *relname; prettyFlags = GET_PRETTY_FLAGS(pretty); - if (OidIsValid(relid)) - { - /* Get the name for the relation */ - relname = get_rel_name(relid); - /* See notes above */ - if (relname == NULL) - PG_RETURN_NULL(); - } + result = pg_get_expr_worker(expr, relid, prettyFlags); + if (result) + PG_RETURN_TEXT_P(result); else - relname = NULL; - - PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags)); + PG_RETURN_NULL(); } static text * -pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags) +pg_get_expr_worker(text *expr, Oid relid, int prettyFlags) { Node *node; Node *tst; Relids relids; List *context; char *exprstr; + Relation rel = NULL; char *str; /* Convert input pg_node_tree (really TEXT) object to C string */ @@ -2729,9 +2722,19 @@ pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags) errmsg("expression contains variables"))); } - /* Prepare deparse context if needed */ + /* + * Prepare deparse context if needed. If we are deparsing with a relid, + * we need to transiently open and lock the rel, to make sure it won't go + * away underneath us. (set_relation_column_names would lock it anyway, + * so this isn't really introducing any new behavior.) + */ if (OidIsValid(relid)) - context = deparse_context_for(relname, relid); + { + rel = try_relation_open(relid, AccessShareLock); + if (rel == NULL) + return NULL; + context = deparse_context_for(RelationGetRelationName(rel), relid); + } else context = NIL; @@ -2739,6 +2742,9 @@ pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags) str = deparse_expression_pretty(node, context, false, false, prettyFlags, 0); + if (rel != NULL) + relation_close(rel, AccessShareLock); + return string_to_text(str); }
В списке pgsql-hackers по дате отправления: