> Tatsuo Ishii <is...@postgresql.org> writes: >> Can I make sure that we want to keep the current behavior: > >> test=# SELECT 'pg_klass'::regclass; >> ERROR: relation "pg_klass" does not exist > > Yeah, I think the consensus is to not change the behavior of the input > functions, just add some new ones.
Ok, here is the conceptual patch to implement "toregclass" (only for now). If my direction is ok, I'll come up with complete patches to implement more "to*" functions. Any advice will be appreciated. Here is a sample session: test=# select toregclass('foo'); toregclass ------------ - (1 row) test=# select toregclass('pg_class'); toregclass ------------ pg_class (1 row) test=# select toregclass('pg_class')::oid; toregclass ------------ 1259 (1 row) test=# select toregclass('foo')::oid; toregclass ------------ 0 (1 row) Implementation notes: To implement toregclass, which does not throw errors when invalid argument is given, src/backend/utils/adt/regproc.c is modified. I added two static functions: static Datum regclass_gut(char *class_name_or_oid, bool raiseError); static List *stringToQualifiedNameList_gut(const char *string, bool raiseError); regclass_gut is called from regclassin and toregclass and do the most job before regclassin did. "raiseError" flag controls whether an error is raised or not when an invalid argument (for example non existent relation) is given. For this purpose, regclass_gut wraps the call to oidin using a PG_TRY block. Secondly, when called as bootstap and raiseError is true, returns InvalidOid instead of raising an error "relation XXX does not exist". However, I doubt there's no customer who calls regclass_gut with raiseError is false in the bootstrap. Thirdly, stringToQualifiedNameList_gut is added to replace stringToQualifiedNameList. The reason why I don't use PG_TRY block is, I need to free some memory allocated inside the function in an error condition. Finially I modified the call to RangeVarGetRelid to switch "missing_ok" flag to reflect raiseError argument. One thing I need to further is modifying makeRangeVarFromNameList. If strange schema qualified name like "a.b.c.d.e.f" is given, still an error raises. So, any advice will be appreciated. Best regards, -- Tatsuo Ishii SRA OSS, Inc. Japan English: http://www.sraoss.co.jp/index_en.php Japanese: http://www.sraoss.co.jp
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index c24a2c1..d3532d7 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -45,6 +45,8 @@ static char *format_operator_internal(Oid operator_oid, bool force_qualify); static char *format_procedure_internal(Oid procedure_oid, bool force_qualify); static void parseNameAndArgTypes(const char *string, bool allowNone, List **names, int *nargs, Oid *argtypes); +static Datum regclass_gut(char *class_name_or_oid, bool raiseError); +static List *stringToQualifiedNameList_gut(const char *string, bool raiseError); /***************************************************************************** @@ -804,21 +806,55 @@ Datum regclassin(PG_FUNCTION_ARGS) { char *class_name_or_oid = PG_GETARG_CSTRING(0); + Oid result; + + result = regclass_gut(class_name_or_oid, true); + PG_RETURN_OID(result); +} + +Datum +toregclass(PG_FUNCTION_ARGS) +{ + char *class_name_or_oid = PG_GETARG_CSTRING(0); + Oid result; + + result = regclass_gut(class_name_or_oid, false); + PG_RETURN_OID(result); +} + +/* + * Gut of regclassin and toregclass. + * If raiseError is false, returns InvalidOid upon error. + */ +static Datum regclass_gut(char *class_name_or_oid, bool raiseError) +{ Oid result = InvalidOid; List *names; /* '-' ? */ if (strcmp(class_name_or_oid, "-") == 0) - PG_RETURN_OID(InvalidOid); + return result; /* Numeric OID? */ if (class_name_or_oid[0] >= '0' && class_name_or_oid[0] <= '9' && strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid)) { - result = DatumGetObjectId(DirectFunctionCall1(oidin, - CStringGetDatum(class_name_or_oid))); - PG_RETURN_OID(result); + PG_TRY(); + { + result = DatumGetObjectId(DirectFunctionCall1(oidin, + CStringGetDatum(class_name_or_oid))); + } + PG_CATCH(); + { + if (raiseError) + PG_RE_THROW(); + else + return InvalidOid; + } + PG_END_TRY(); + + return result; } /* Else it's a name, possibly schema-qualified */ @@ -848,28 +884,36 @@ regclassin(PG_FUNCTION_ARGS) if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = HeapTupleGetOid(tuple); else - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" does not exist", class_name_or_oid))); + if (raiseError) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("relation \"%s\" does not exist", class_name_or_oid))); + else + return InvalidOid; /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); - PG_RETURN_OID(result); + return result; } /* * Normal case: parse the name into components and see if it matches any * pg_class entries in the current search path. */ - names = stringToQualifiedNameList(class_name_or_oid); + names = stringToQualifiedNameList_gut(class_name_or_oid, false); + if (names == NIL) + return InvalidOid; /* We might not even have permissions on this relation; don't lock it. */ - result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, false); + if (raiseError) + result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, false); + else + result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, true); - PG_RETURN_OID(result); + return result; } /* @@ -1352,6 +1396,12 @@ text_regclass(PG_FUNCTION_ARGS) List * stringToQualifiedNameList(const char *string) { + return stringToQualifiedNameList_gut(string, true); +} + +static List * +stringToQualifiedNameList_gut(const char *string, bool raiseError) +{ char *rawname; List *result = NIL; List *namelist; @@ -1361,14 +1411,30 @@ stringToQualifiedNameList(const char *string) rawname = pstrdup(string); if (!SplitIdentifierString(rawname, '.', &namelist)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_NAME), - errmsg("invalid name syntax"))); + { + if (raiseError) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("invalid name syntax"))); + else + { + pfree(rawname); + return result; + } + } if (namelist == NIL) - ereport(ERROR, - (errcode(ERRCODE_INVALID_NAME), - errmsg("invalid name syntax"))); + { + if (raiseError) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("invalid name syntax"))); + else + { + pfree(rawname); + return result; + } + } foreach(l, namelist) { diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 0117500..3f00b05 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3304,6 +3304,7 @@ DATA(insert OID = 2218 ( regclassin PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2 DESCR("I/O"); DATA(insert OID = 2219 ( regclassout PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "2205" _null_ _null_ _null_ _null_ regclassout _null_ _null_ _null_ )); DESCR("I/O"); +DATA(insert OID = 3179 ( toregclass PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2205 "2275" _null_ _null_ _null_ _null_ toregclass _null_ _null_ _null_ )); DATA(insert OID = 2220 ( regtypein PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2206 "2275" _null_ _null_ _null_ _null_ regtypein _null_ _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 2221 ( regtypeout PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "2206" _null_ _null_ _null_ _null_ regtypeout _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 1bfd145..1b57a7b 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -600,6 +600,7 @@ extern Datum regclassin(PG_FUNCTION_ARGS); extern Datum regclassout(PG_FUNCTION_ARGS); extern Datum regclassrecv(PG_FUNCTION_ARGS); extern Datum regclasssend(PG_FUNCTION_ARGS); +extern Datum toregclass(PG_FUNCTION_ARGS); extern Datum regtypein(PG_FUNCTION_ARGS); extern Datum regtypeout(PG_FUNCTION_ARGS); extern Datum regtyperecv(PG_FUNCTION_ARGS);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers