From d9ade916b6c0ec758189b1cd702cd3ecc0ceb23c Mon Sep 17 00:00:00 2001 From: jian he Date: Tue, 7 Jul 2026 11:40:17 +0800 Subject: [PATCH v12 1/4] error safe for casting text to other types per pg_cast select castsource::regtype, casttarget::regtype, castfunc, castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc and pc.castfunc > 0 and castsource::regtype = 'text'::regtype order by castsource::regtype; castsource | casttarget | castfunc | castcontext | castmethod | prosrc | proname ------------+------------+----------+-------------+------------+---------------+---------- text | regclass | 1079 | i | f | text_regclass | regclass text | "char" | 944 | a | f | text_char | char text | name | 407 | i | f | text_name | name text | xml | 2896 | e | f | texttoxml | xml (4 rows) Already error safe: text_name, text_char, texttoxml. Author: jian he Reviewed-by: Andrew Dunstan Reviewed-by: Amul Sul Reviewed-by: Corey Huinker Reviewed-by: Henson Choi Discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com Commitfest: https://commitfest.postgresql.org/patch/5941 --- src/backend/catalog/namespace.c | 46 ++++++++++++++++++++++++++------- src/backend/utils/adt/regproc.c | 17 +++++++++--- src/backend/utils/adt/varlena.c | 10 +++++-- src/include/catalog/namespace.h | 6 +++++ src/include/utils/varlena.h | 1 + 5 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 56b87d878e8..0f63c826e80 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -442,6 +442,16 @@ Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg) +{ + return RangeVarGetRelidExtendedSafe(relation, lockmode, flags, + callback, callback_arg, + NULL); +} + +Oid +RangeVarGetRelidExtendedSafe(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, + RangeVarGetRelidCallback callback, void *callback_arg, + Node *escontext) { uint64 inval_count; Oid relId; @@ -458,7 +468,7 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, if (relation->catalogname) { if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0) - ereport(ERROR, + ereturn(escontext, InvalidOid, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cross-database references are not implemented: \"%s.%s.%s\"", relation->catalogname, relation->schemaname, @@ -515,7 +525,7 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, * return InvalidOid. */ if (namespaceId != myTempNamespace) - ereport(ERROR, + ereturn(escontext, InvalidOid, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("temporary tables cannot specify a schema name"))); } @@ -595,14 +605,20 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, { int elevel = (flags & RVR_SKIP_LOCKED) ? DEBUG1 : ERROR; - if (relation->schemaname) + if (escontext == NULL) ereport(elevel, (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + relation->schemaname ? errmsg("could not obtain lock on relation \"%s.%s\"", - relation->schemaname, relation->relname))); + relation->schemaname, relation->relname) : + errmsg("could not obtain lock on relation \"%s\"", + relation->relname))); else - ereport(elevel, + ereturn(escontext, InvalidOid, (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + relation->schemaname ? + errmsg("could not obtain lock on relation \"%s.%s\"", + relation->schemaname, relation->relname) : errmsg("could not obtain lock on relation \"%s\"", relation->relname))); @@ -628,14 +644,20 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, { int elevel = missing_ok ? DEBUG1 : ERROR; - if (relation->schemaname) + if (escontext == NULL) ereport(elevel, (errcode(ERRCODE_UNDEFINED_TABLE), + relation->schemaname ? errmsg("relation \"%s.%s\" does not exist", - relation->schemaname, relation->relname))); + relation->schemaname, relation->relname) : + errmsg("relation \"%s\" does not exist", + relation->relname))); else - ereport(elevel, + ereturn(escontext, InvalidOid, (errcode(ERRCODE_UNDEFINED_TABLE), + relation->schemaname ? + errmsg("relation \"%s.%s\" does not exist", + relation->schemaname, relation->relname) : errmsg("relation \"%s\" does not exist", relation->relname))); } @@ -3624,6 +3646,12 @@ get_namespace_oid(const char *nspname, bool missing_ok) */ RangeVar * makeRangeVarFromNameList(const List *names) +{ + return makeRangeVarFromNameListSafe(names, NULL); +} + +RangeVar * +makeRangeVarFromNameListSafe(const List *names, Node *escontext) { RangeVar *rel = makeRangeVar(NULL, NULL, -1); @@ -3642,7 +3670,7 @@ makeRangeVarFromNameList(const List *names) rel->relname = strVal(lthird(names)); break; default: - ereport(ERROR, + ereturn(escontext, NULL, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("improper relation name (too many dotted names): %s", NameListToString(names)))); diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 64f293f4e98..1f533c34516 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -1901,12 +1901,23 @@ text_regclass(PG_FUNCTION_ARGS) text *relname = PG_GETARG_TEXT_PP(0); Oid result; RangeVar *rv; + List *namelist; - rv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + namelist = textToQualifiedNameListSafe(relname, fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + PG_RETURN_NULL(); + + rv = makeRangeVarFromNameListSafe(namelist, fcinfo->context); + if (SOFT_ERROR_OCCURRED(fcinfo->context)) + PG_RETURN_NULL(); /* We might not even have permissions on this relation; don't lock it. */ - result = RangeVarGetRelid(rv, NoLock, false); - + result = RangeVarGetRelidExtendedSafe(rv, + NoLock, + fcinfo->context ? RVR_MISSING_OK : 0, + NULL, + NULL, + fcinfo->context); PG_RETURN_OID(result); } diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 0c6d3ba4d22..c3263ad994c 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -2720,6 +2720,12 @@ name_text(PG_FUNCTION_ARGS) */ List * textToQualifiedNameList(text *textval) +{ + return textToQualifiedNameListSafe(textval, NULL); +} + +List * +textToQualifiedNameListSafe(text *textval, Node *escontext) { char *rawname; List *result = NIL; @@ -2731,12 +2737,12 @@ textToQualifiedNameList(text *textval) rawname = text_to_cstring(textval); if (!SplitIdentifierString(rawname, '.', &namelist)) - ereport(ERROR, + ereturn(escontext, NIL, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid name syntax"))); if (namelist == NIL) - ereport(ERROR, + ereturn(escontext, NIL, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid name syntax"))); diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index 9453a3e4932..7f3141ba27d 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -103,6 +103,11 @@ extern Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg); +extern Oid RangeVarGetRelidExtendedSafe(const RangeVar *relation, + LOCKMODE lockmode, uint32 flags, + RangeVarGetRelidCallback callback, + void *callback_arg, + Node *escontext); extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation); extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, @@ -168,6 +173,7 @@ extern Oid LookupCreationNamespace(const char *nspname); extern void CheckSetNamespace(Oid oldNspOid, Oid nspOid); extern Oid QualifiedNameGetCreationNamespace(const List *names, char **objname_p); extern RangeVar *makeRangeVarFromNameList(const List *names); +extern RangeVar *makeRangeVarFromNameListSafe(const List *names, Node *escontext); extern char *NameListToString(const List *names); extern char *NameListToQuotedString(const List *names); diff --git a/src/include/utils/varlena.h b/src/include/utils/varlena.h index fe8d8a58952..c4d4292eb1c 100644 --- a/src/include/utils/varlena.h +++ b/src/include/utils/varlena.h @@ -27,6 +27,7 @@ extern int varstr_levenshtein_less_equal(const char *source, int slen, int ins_c, int del_c, int sub_c, int max_d, bool trusted); extern List *textToQualifiedNameList(text *textval); +extern List *textToQualifiedNameListSafe(text *textval, Node *escontext); extern char *scan_quoted_identifier(char **endp, char **nextp); extern char *scan_identifier(char **endp, char **nextp, char separator, bool downcase_unquoted); -- 2.34.1