From b152c6cd0de1827ba58756e24e18110cf902182a Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvherre@alvh.no-ip.org> Date: Thu, 23 Jan 2014 14:40:29 -0300 Subject: [PATCH] Make DROP IF EXISTS more consistently not fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some cases were still reporting errors and aborting, instead of a NOTICE that the object was being skipped. This makes it more difficult to cleanly handle pg_dump --clean, so change that to instead skip missing objects properly. Per bug #7873 reported by Dave Rolsky; apparently this affects a large number of users. Authors: Pavel Stehule and Dean Rasheed. Some tweaks by Ãlvaro Herrera --- src/backend/catalog/namespace.c | 45 ++-- src/backend/catalog/objectaddress.c | 46 +++- src/backend/commands/dropcmds.c | 258 ++++++++++++++++--- src/backend/commands/functioncmds.c | 4 +- src/backend/commands/opclasscmds.c | 26 +- src/backend/commands/tablecmds.c | 26 +- src/backend/commands/typecmds.c | 2 +- src/backend/parser/parse_func.c | 32 +-- src/backend/parser/parse_oper.c | 4 +- src/backend/parser/parse_type.c | 88 +++++-- src/backend/utils/adt/regproc.c | 6 +- src/include/catalog/namespace.h | 3 +- src/include/parser/parse_type.h | 4 +- src/pl/plpgsql/src/pl_comp.c | 2 +- src/test/regress/expected/drop_if_exists.out | 79 +++++- src/test/regress/expected/event_trigger.out | 2 +- src/test/regress/sql/drop_if_exists.sql | 41 +++ 17 files changed, 523 insertions(+), 145 deletions(-) diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 08b26b462a7..6c2a5d2af5a 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -906,10 +906,15 @@ TypeIsVisible(Oid typid) * with oid = 0 that represents a set of such conflicting candidates. * The caller might end up discarding such an entry anyway, but if it selects * such an entry it should react as though the call were ambiguous. + * + * If missing_ok is true, an empty list (NULL) is returned if the name was + * schema- qualified with a schema that does not exist. Likewise if no + * candidate is found for other reasons. */ FuncCandidateList FuncnameGetCandidates(List *names, int nargs, List *argnames, - bool expand_variadic, bool expand_defaults) + bool expand_variadic, bool expand_defaults, + bool missing_ok) { FuncCandidateList resultList = NULL; bool any_special = false; @@ -928,7 +933,9 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames, if (schemaname) { /* use exact schema given */ - namespaceId = LookupExplicitNamespace(schemaname, false); + namespaceId = LookupExplicitNamespace(schemaname, missing_ok); + if (!OidIsValid(namespaceId)) + return NULL; } else { @@ -1414,7 +1421,7 @@ FunctionIsVisible(Oid funcid) visible = false; clist = FuncnameGetCandidates(list_make1(makeString(proname)), - nargs, NIL, false, false); + nargs, NIL, false, false, false); for (; clist; clist = clist->next) { @@ -1443,7 +1450,8 @@ FunctionIsVisible(Oid funcid) * a postfix op. * * If the operator name is not schema-qualified, it is sought in the current - * namespace search path. + * namespace search path. If the name is schema-qualified and the given + * schema does not exist, InvalidOid is returned. */ Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright) @@ -1460,21 +1468,26 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright) { /* search only in exact schema given */ Oid namespaceId; - HeapTuple opertup; - namespaceId = LookupExplicitNamespace(schemaname, false); - opertup = SearchSysCache4(OPERNAMENSP, - CStringGetDatum(opername), - ObjectIdGetDatum(oprleft), - ObjectIdGetDatum(oprright), - ObjectIdGetDatum(namespaceId)); - if (HeapTupleIsValid(opertup)) + namespaceId = LookupExplicitNamespace(schemaname, true); + if (OidIsValid(namespaceId)) { - Oid result = HeapTupleGetOid(opertup); + HeapTuple opertup; + + opertup = SearchSysCache4(OPERNAMENSP, + CStringGetDatum(opername), + ObjectIdGetDatum(oprleft), + ObjectIdGetDatum(oprright), + ObjectIdGetDatum(namespaceId)); + if (HeapTupleIsValid(opertup)) + { + Oid result = HeapTupleGetOid(opertup); - ReleaseSysCache(opertup); - return result; + ReleaseSysCache(opertup); + return result; + } } + return InvalidOid; } @@ -1729,7 +1742,7 @@ OperatorIsVisible(Oid oprid) * If it is in the path, it might still not be visible; it could be * hidden by another operator of the same name and arguments earlier * in the path. So we must do a slow check to see if this is the same - * operator that would be found by OpernameGetOprId. + * operator that would be found by OpernameGetOprid. */ char *oprname = NameStr(oprform->oprname); diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index a6ec6aa04bd..ea812238be0 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -450,7 +450,7 @@ static void getRelationIdentity(StringInfo buffer, Oid relid); * sub-object is looked up, the parent object will be locked instead. * * If the object is a relation or a child object of a relation (e.g. an - * attribute or contraint), the relation is also opened and *relp receives + * attribute or constraint), the relation is also opened and *relp receives * the open relcache entry pointer; otherwise, *relp is set to NULL. This * is a bit grotty but it makes life simpler, since the caller will * typically need the relcache entry too. Caller must close the relcache @@ -458,9 +458,20 @@ static void getRelationIdentity(StringInfo buffer, Oid relid); * if the target object is the relation itself or an attribute, but for other * child objects, only AccessShareLock is acquired on the relation. * + * If the object is not found, an error is thrown, unless missing_ok is + * true. In this case, no lock is acquired, relp is set to NULL, and the + * returned address has objectId set to InvalidOid. + * * We don't currently provide a function to release the locks acquired here; * typically, the lock must be held until commit to guard against a concurrent * drop operation. + * + * Note: If the object is not found, we don't give any indication of the + * reason. (It might have been a missing schema if the name was qualified, or + * an inexistant type name in case of a cast, function or operator; etc). + * Currently there is only one caller that might be interested in such info, so + * we don't spend much effort here. If more callers start to care, it might be + * better to add some support for that in this function. */ ObjectAddress get_object_address(ObjectType objtype, List *objname, List *objargs, @@ -580,9 +591,11 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, { TypeName *sourcetype = (TypeName *) linitial(objname); TypeName *targettype = (TypeName *) linitial(objargs); - Oid sourcetypeid = typenameTypeId(NULL, sourcetype); - Oid targettypeid = typenameTypeId(NULL, targettype); + Oid sourcetypeid; + Oid targettypeid; + sourcetypeid = LookupTypeNameOid(NULL, sourcetype, missing_ok); + targettypeid = LookupTypeNameOid(NULL, targettype, missing_ok); address.classId = CastRelationId; address.objectId = get_cast_oid(sourcetypeid, targettypeid, missing_ok); @@ -942,26 +955,31 @@ get_object_address_relobject(ObjectType objtype, List *objname, /* Extract relation name and open relation. */ relname = list_truncate(list_copy(objname), nnames - 1); - relation = heap_openrv(makeRangeVarFromNameList(relname), - AccessShareLock); - reloid = RelationGetRelid(relation); + relation = heap_openrv_extended(makeRangeVarFromNameList(relname), + AccessShareLock, + missing_ok); + + reloid = relation ? RelationGetRelid(relation) : InvalidOid; switch (objtype) { case OBJECT_RULE: address.classId = RewriteRelationId; - address.objectId = get_rewrite_oid(reloid, depname, missing_ok); + address.objectId = relation ? + get_rewrite_oid(reloid, depname, missing_ok) : InvalidOid; address.objectSubId = 0; break; case OBJECT_TRIGGER: address.classId = TriggerRelationId; - address.objectId = get_trigger_oid(reloid, depname, missing_ok); + address.objectId = relation ? + get_trigger_oid(reloid, depname, missing_ok) : InvalidOid; address.objectSubId = 0; break; case OBJECT_CONSTRAINT: address.classId = ConstraintRelationId; - address.objectId = - get_relation_constraint_oid(reloid, depname, missing_ok); + address.objectId = relation ? + get_relation_constraint_oid(reloid, depname, missing_ok) : + InvalidOid; address.objectSubId = 0; break; default: @@ -975,7 +993,9 @@ get_object_address_relobject(ObjectType objtype, List *objname, /* Avoid relcache leak when object not found. */ if (!OidIsValid(address.objectId)) { - heap_close(relation, AccessShareLock); + if (relation != NULL) + heap_close(relation, AccessShareLock); + relation = NULL; /* department of accident prevention */ return address; } @@ -1008,6 +1028,7 @@ get_object_address_attribute(ObjectType objtype, List *objname, errmsg("column name must be qualified"))); attname = strVal(lfirst(list_tail(objname))); relname = list_truncate(list_copy(objname), list_length(objname) - 1); + /* XXX no missing_ok support here */ relation = relation_openrv(makeRangeVarFromNameList(relname), lockmode); reloid = RelationGetRelid(relation); @@ -1053,7 +1074,7 @@ get_object_address_type(ObjectType objtype, address.objectId = InvalidOid; address.objectSubId = 0; - tup = LookupTypeName(NULL, typename, NULL); + tup = LookupTypeName(NULL, typename, NULL, missing_ok); if (!HeapTupleIsValid(tup)) { if (!missing_ok) @@ -1090,6 +1111,7 @@ get_object_address_opcf(ObjectType objtype, ObjectAddress address; Assert(list_length(objargs) == 1); + /* XXX no missing_ok support here */ amoid = get_am_oid(strVal(linitial(objargs)), false); switch (objtype) diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c index b25317ebad4..e64ad8027e2 100644 --- a/src/backend/commands/dropcmds.c +++ b/src/backend/commands/dropcmds.c @@ -12,7 +12,6 @@ * *------------------------------------------------------------------------- */ - #include "postgres.h" #include "access/heapam.h" @@ -29,8 +28,16 @@ #include "utils/builtins.h" #include "utils/syscache.h" + static void does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs); +static bool owningrel_does_not_exist_skipping(List *objname, + const char **msg, char **name); +static bool schema_does_not_exist_skipping(List *objname, + const char **msg, char **name); +static bool type_in_list_does_not_exist_skipping(List *typenames, + const char **msg, char **name); + /* * Drop one or more objects. @@ -73,9 +80,14 @@ RemoveObjects(DropStmt *stmt) AccessExclusiveLock, stmt->missing_ok); - /* Issue NOTICE if supplied object was not found. */ + /* + * Issue NOTICE if supplied object was not found. Note this is only + * relevant in the missing_ok case, because otherwise + * get_object_address would have thrown an error. + */ if (!OidIsValid(address.objectId)) { + Assert(stmt->missing_ok); does_not_exist_skipping(stmt->removeType, objname, objargs); continue; } @@ -125,9 +137,121 @@ RemoveObjects(DropStmt *stmt) } /* + * owningrel_does_not_exist_skipping + * Subroutine for RemoveObjects + * + * After determining that a specification for a rule or trigger returns that + * the specified object does not exist, test whether its owning relation, and + * its schema, exist or not; if they do, return false --- the trigger or rule + * itself is missing instead. If the owning relation or its schema do not + * exist, fill the error message format string and name, and return true. + */ +static bool +owningrel_does_not_exist_skipping(List *objname, const char **msg, char **name) +{ + List *parent_objname; + RangeVar *parent_rel; + + parent_objname = list_truncate(list_copy(objname), + list_length(objname) - 1); + + if (schema_does_not_exist_skipping(parent_objname, msg, name)) + return true; + + parent_rel = makeRangeVarFromNameList(parent_objname); + + if (!OidIsValid(RangeVarGetRelid(parent_rel, NoLock, true))) + { + *msg = gettext_noop("relation \"%s\" does not exist, skipping"); + *name = NameListToString(parent_objname); + + return true; + } + + return false; +} + +/* + * schema_does_not_exist_skipping + * Subroutine for RemoveObjects + * + * After determining that a specification for a schema-qualifiable object + * refers to an object that does not exist, test whether the specified schema + * exists or not. If no schema was specified, or if the schema does exist, + * return false -- the object itself is missing instead. If the specified + * schema does not exist, fill the error message format string and the + * specified schema name, and return true. + */ +static bool +schema_does_not_exist_skipping(List *objname, const char **msg, char **name) +{ + RangeVar *rel; + + rel = makeRangeVarFromNameList(objname); + + if (rel->schemaname != NULL && + !OidIsValid(LookupNamespaceNoError(rel->schemaname))) + { + *msg = gettext_noop("schema \"%s\" does not exist, skipping"); + *name = rel->schemaname; + + return true; + } + + return false; +} + +/* + * type_in_list_does_not_exist_skipping + * Subroutine for RemoveObjects + * + * After determining that a specification for a function, cast, aggregate or + * operator returns that the specified object does not exist, test whether the + * involved datatypes, and their schemas, exist or not; if they do, return + * false --- the original object itself is missing instead. If the datatypes + * or schemas do not exist, fill the error message format string and the + * missing name, and return true. + * + * First parameter is a list of TypeNames. + */ +static bool +type_in_list_does_not_exist_skipping(List *typenames, const char **msg, + char **name) +{ + ListCell *l; + + foreach(l, typenames) + { + TypeName *typeName = (TypeName *) lfirst(l); + + if (typeName != NULL) + { + Assert(IsA(typeName, TypeName)); + + if (!OidIsValid(LookupTypeNameOid(NULL, typeName, true))) + { + /* type doesn't exist, try to find why */ + if (schema_does_not_exist_skipping(typeName->names, msg, name)) + return true; + + *msg = gettext_noop("type \"%s\" does not exist, skipping"); + *name = TypeNameToString(typeName); + + return true; + } + } + } + + return false; +} + +/* + * does_not_exist_skipping + * Subroutine for RemoveObjects + * * Generate a NOTICE stating that the named object was not found, and is * being skipped. This is only relevant when "IF EXISTS" is used; otherwise, - * get_object_address() will throw an ERROR. + * get_object_address() in RemoveObjects would have thrown an ERROR. */ static void does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) @@ -140,81 +264,125 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) { case OBJECT_TYPE: case OBJECT_DOMAIN: - msg = gettext_noop("type \"%s\" does not exist, skipping"); - name = TypeNameToString(makeTypeNameFromNameList(objname)); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("type \"%s\" does not exist, skipping"); + name = TypeNameToString(makeTypeNameFromNameList(objname)); + } break; case OBJECT_COLLATION: - msg = gettext_noop("collation \"%s\" does not exist, skipping"); - name = NameListToString(objname); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("collation \"%s\" does not exist, skipping"); + name = NameListToString(objname); + } break; case OBJECT_CONVERSION: - msg = gettext_noop("conversion \"%s\" does not exist, skipping"); - name = NameListToString(objname); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("conversion \"%s\" does not exist, skipping"); + name = NameListToString(objname); + } break; case OBJECT_SCHEMA: msg = gettext_noop("schema \"%s\" does not exist, skipping"); name = NameListToString(objname); break; case OBJECT_TSPARSER: - msg = gettext_noop("text search parser \"%s\" does not exist, skipping"); - name = NameListToString(objname); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("text search parser \"%s\" does not exist, skipping"); + name = NameListToString(objname); + } break; case OBJECT_TSDICTIONARY: - msg = gettext_noop("text search dictionary \"%s\" does not exist, skipping"); - name = NameListToString(objname); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("text search dictionary \"%s\" does not exist, skipping"); + name = NameListToString(objname); + } break; case OBJECT_TSTEMPLATE: - msg = gettext_noop("text search template \"%s\" does not exist, skipping"); - name = NameListToString(objname); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("text search template \"%s\" does not exist, skipping"); + name = NameListToString(objname); + } break; case OBJECT_TSCONFIGURATION: - msg = gettext_noop("text search configuration \"%s\" does not exist, skipping"); - name = NameListToString(objname); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("text search configuration \"%s\" does not exist, skipping"); + name = NameListToString(objname); + } break; case OBJECT_EXTENSION: msg = gettext_noop("extension \"%s\" does not exist, skipping"); name = NameListToString(objname); break; case OBJECT_FUNCTION: - msg = gettext_noop("function %s(%s) does not exist, skipping"); - name = NameListToString(objname); - args = TypeNameListToString(objargs); + if (!schema_does_not_exist_skipping(objname, &msg, &name) && + !type_in_list_does_not_exist_skipping(objargs, &msg, &name)) + { + msg = gettext_noop("function %s(%s) does not exist, skipping"); + name = NameListToString(objname); + args = TypeNameListToString(objargs); + } break; case OBJECT_AGGREGATE: - msg = gettext_noop("aggregate %s(%s) does not exist, skipping"); - name = NameListToString(objname); - args = TypeNameListToString(objargs); + if (!schema_does_not_exist_skipping(objname, &msg, &name) && + !type_in_list_does_not_exist_skipping(objargs, &msg, &name)) + { + msg = gettext_noop("aggregate %s(%s) does not exist, skipping"); + name = NameListToString(objname); + args = TypeNameListToString(objargs); + } break; case OBJECT_OPERATOR: - msg = gettext_noop("operator %s does not exist, skipping"); - name = NameListToString(objname); + if (!schema_does_not_exist_skipping(objname, &msg, &name) && + !type_in_list_does_not_exist_skipping(objargs, &msg, &name)) + { + msg = gettext_noop("operator %s does not exist, skipping"); + name = NameListToString(objname); + } break; case OBJECT_LANGUAGE: msg = gettext_noop("language \"%s\" does not exist, skipping"); name = NameListToString(objname); break; case OBJECT_CAST: - msg = gettext_noop("cast from type %s to type %s does not exist, skipping"); - name = format_type_be(typenameTypeId(NULL, - (TypeName *) linitial(objname))); - args = format_type_be(typenameTypeId(NULL, - (TypeName *) linitial(objargs))); + { + if (!type_in_list_does_not_exist_skipping(objname, &msg, &name) && + !type_in_list_does_not_exist_skipping(objargs, &msg, &name)) + { + /* XXX quote or no quote? */ + msg = gettext_noop("cast from type %s to type %s does not exist, skipping"); + name = TypeNameToString((TypeName *) linitial(objname)); + args = TypeNameToString((TypeName *) linitial(objargs)); + } + } break; case OBJECT_TRIGGER: - msg = gettext_noop("trigger \"%s\" for table \"%s\" does not exist, skipping"); - name = strVal(llast(objname)); - args = NameListToString(list_truncate(list_copy(objname), + if (!owningrel_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("trigger \"%s\" for relation \"%s\" does not exist, skipping"); + name = strVal(llast(objname)); + args = NameListToString(list_truncate(list_copy(objname), list_length(objname) - 1)); + } break; case OBJECT_EVENT_TRIGGER: msg = gettext_noop("event trigger \"%s\" does not exist, skipping"); name = NameListToString(objname); break; case OBJECT_RULE: - msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping"); - name = strVal(llast(objname)); - args = NameListToString(list_truncate(list_copy(objname), + if (!owningrel_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping"); + name = strVal(llast(objname)); + args = NameListToString(list_truncate(list_copy(objname), list_length(objname) - 1)); + } break; case OBJECT_FDW: msg = gettext_noop("foreign-data wrapper \"%s\" does not exist, skipping"); @@ -225,14 +393,20 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) name = NameListToString(objname); break; case OBJECT_OPCLASS: - msg = gettext_noop("operator class \"%s\" does not exist for access method \"%s\", skipping"); - name = NameListToString(objname); - args = strVal(linitial(objargs)); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("operator class \"%s\" does not exist for access method \"%s\", skipping"); + name = NameListToString(objname); + args = strVal(linitial(objargs)); + } break; case OBJECT_OPFAMILY: - msg = gettext_noop("operator family \"%s\" does not exist for access method \"%s\", skipping"); - name = NameListToString(objname); - args = strVal(linitial(objargs)); + if (!schema_does_not_exist_skipping(objname, &msg, &name)) + { + msg = gettext_noop("operator family \"%s\" does not exist for access method \"%s\", skipping"); + name = NameListToString(objname); + args = strVal(linitial(objargs)); + } break; default: elog(ERROR, "unexpected object type (%d)", (int) objtype); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 7763feccf97..350cb76ab8c 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -86,7 +86,7 @@ compute_return_type(TypeName *returnType, Oid languageOid, Type typtup; AclResult aclresult; - typtup = LookupTypeName(NULL, returnType, NULL); + typtup = LookupTypeName(NULL, returnType, NULL, false); if (typtup) { @@ -224,7 +224,7 @@ interpret_function_parameter_list(List *parameters, Type typtup; AclResult aclresult; - typtup = LookupTypeName(NULL, t, NULL); + typtup = LookupTypeName(NULL, t, NULL, false); if (typtup) { if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index c910c3964c6..5d7b37c674a 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -103,11 +103,14 @@ OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok) /* Look in specific schema only */ Oid namespaceId; - namespaceId = LookupExplicitNamespace(schemaname, false); - htup = SearchSysCache3(OPFAMILYAMNAMENSP, - ObjectIdGetDatum(amID), - PointerGetDatum(opfname), - ObjectIdGetDatum(namespaceId)); + namespaceId = LookupExplicitNamespace(schemaname, missing_ok); + if (!OidIsValid(namespaceId)) + htup = NULL; + else + htup = SearchSysCache3(OPFAMILYAMNAMENSP, + ObjectIdGetDatum(amID), + PointerGetDatum(opfname), + ObjectIdGetDatum(namespaceId)); } else { @@ -179,11 +182,14 @@ OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok) /* Look in specific schema only */ Oid namespaceId; - namespaceId = LookupExplicitNamespace(schemaname, false); - htup = SearchSysCache3(CLAAMNAMENSP, - ObjectIdGetDatum(amID), - PointerGetDatum(opcname), - ObjectIdGetDatum(namespaceId)); + namespaceId = LookupExplicitNamespace(schemaname, missing_ok); + if (!OidIsValid(namespaceId)) + htup = NULL; + else + htup = SearchSysCache3(CLAAMNAMENSP, + ObjectIdGetDatum(amID), + PointerGetDatum(opcname), + ObjectIdGetDatum(namespaceId)); } else { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 26a4613c474..08b037e501f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -690,10 +690,28 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) * non-existent relation */ static void -DropErrorMsgNonExistent(const char *relname, char rightkind, bool missing_ok) +DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok) { const struct dropmsgstrings *rentry; + if (rel->schemaname != NULL && + !OidIsValid(LookupNamespaceNoError(rel->schemaname))) + { + if (!missing_ok) + { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", rel->schemaname))); + } + else + { + ereport(NOTICE, + (errmsg("schema \"%s\" does not exist, skipping", + rel->schemaname))); + } + return; + } + for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++) { if (rentry->kind == rightkind) @@ -702,11 +720,11 @@ DropErrorMsgNonExistent(const char *relname, char rightkind, bool missing_ok) { ereport(ERROR, (errcode(rentry->nonexistent_code), - errmsg(rentry->nonexistent_msg, relname))); + errmsg(rentry->nonexistent_msg, rel->relname))); } else { - ereport(NOTICE, (errmsg(rentry->skipping_msg, relname))); + ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname))); break; } } @@ -845,7 +863,7 @@ RemoveRelations(DropStmt *drop) /* Not there? */ if (!OidIsValid(relOid)) { - DropErrorMsgNonExistent(rel->relname, relkind, drop->missing_ok); + DropErrorMsgNonExistent(rel, relkind, drop->missing_ok); continue; } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index d85c16c7270..c1ee69b3233 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -3246,7 +3246,7 @@ AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype) typename = makeTypeNameFromNameList(names); /* Use LookupTypeName here so that shell types can be processed */ - tup = LookupTypeName(NULL, typename, NULL); + tup = LookupTypeName(NULL, typename, NULL, false); if (tup == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 32179aacb55..63be2a44f16 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -1259,7 +1259,8 @@ func_get_detail(List *funcname, /* Get list of possible candidates from namespace search */ raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames, - expand_variadic, expand_defaults); + expand_variadic, expand_defaults, + false); /* * Quickly check if there is an exact match to the input datatypes (there @@ -1714,7 +1715,7 @@ FuncNameAsType(List *funcname) Oid result; Type typtup; - typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL); + typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, false); if (typtup == NULL) return InvalidOid; @@ -1873,7 +1874,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) { FuncCandidateList clist; - clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false); + clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, noError); while (clist) { @@ -1892,27 +1893,6 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) return InvalidOid; } -/* - * LookupTypeNameOid - * Convenience routine to look up a type, silently accepting shell types - */ -static Oid -LookupTypeNameOid(const TypeName *typename) -{ - Oid result; - Type typtup; - - typtup = LookupTypeName(NULL, typename, NULL); - if (typtup == NULL) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("type \"%s\" does not exist", - TypeNameToString(typename)))); - result = typeTypeId(typtup); - ReleaseSysCache(typtup); - return result; -} - /* * LookupFuncNameTypeNames * Like LookupFuncName, but the argument types are specified by a @@ -1940,7 +1920,7 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError) { TypeName *t = (TypeName *) lfirst(args_item); - argoids[i] = LookupTypeNameOid(t); + argoids[i] = LookupTypeNameOid(NULL, t, noError); args_item = lnext(args_item); } @@ -1980,7 +1960,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) { TypeName *t = (TypeName *) lfirst(lc); - argoids[i] = LookupTypeNameOid(t); + argoids[i] = LookupTypeNameOid(NULL, t, noError); i++; } diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 387f7fd0b36..99dbd30d758 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -148,12 +148,12 @@ LookupOperNameTypeNames(ParseState *pstate, List *opername, if (oprleft == NULL) leftoid = InvalidOid; else - leftoid = typenameTypeId(pstate, oprleft); + leftoid = LookupTypeNameOid(pstate, oprleft, noError); if (oprright == NULL) rightoid = InvalidOid; else - rightoid = typenameTypeId(pstate, oprright); + rightoid = LookupTypeNameOid(pstate, oprright, noError); return LookupOperName(pstate, opername, leftoid, rightoid, noError, location); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 635aa1d3631..b329dfb1684 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -56,7 +56,7 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, */ Type LookupTypeName(ParseState *pstate, const TypeName *typeName, - int32 *typmod_p) + int32 *typmod_p, bool missing_ok) { Oid typoid; HeapTuple tup; @@ -116,24 +116,32 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, * concurrent DDL. But taking a lock would carry a performance * penalty and would also require a permissions check. */ - relid = RangeVarGetRelid(rel, NoLock, false); + relid = RangeVarGetRelid(rel, NoLock, missing_ok); attnum = get_attnum(relid, field); if (attnum == InvalidAttrNumber) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - field, rel->relname), - parser_errposition(pstate, typeName->location))); - typoid = get_atttype(relid, attnum); + { + if (missing_ok) + typoid = InvalidOid; + else + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + field, rel->relname), + parser_errposition(pstate, typeName->location))); + } + else + { + typoid = get_atttype(relid, attnum); - /* this construct should never have an array indicator */ - Assert(typeName->arrayBounds == NIL); + /* this construct should never have an array indicator */ + Assert(typeName->arrayBounds == NIL); - /* emit nuisance notice (intentionally not errposition'd) */ - ereport(NOTICE, - (errmsg("type reference %s converted to %s", - TypeNameToString(typeName), - format_type_be(typoid)))); + /* emit nuisance notice (intentionally not errposition'd) */ + ereport(NOTICE, + (errmsg("type reference %s converted to %s", + TypeNameToString(typeName), + format_type_be(typoid)))); + } } else { @@ -149,10 +157,13 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, /* Look in specific schema only */ Oid namespaceId; - namespaceId = LookupExplicitNamespace(schemaname, false); - typoid = GetSysCacheOid2(TYPENAMENSP, - PointerGetDatum(typname), - ObjectIdGetDatum(namespaceId)); + namespaceId = LookupExplicitNamespace(schemaname, missing_ok); + if (OidIsValid(namespaceId)) + typoid = GetSysCacheOid2(TYPENAMENSP, + PointerGetDatum(typname), + ObjectIdGetDatum(namespaceId)); + else + typoid = InvalidOid; } else { @@ -184,6 +195,43 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, return (Type) tup; } +/* + * LookupTypeNameOid + * Given a TypeName object, lookup the pg_type syscache entry of the type. + * Returns InvalidOid if no such type can be found. If the type is found, + * return its Oid. + * + * NB: direct callers of this function need to be aware that the type OID + * returned may correspond to a shell type. Most code should go through + * typenameTypeId instead. + * + * pstate is only used for error location info, and may be NULL. + */ +Oid +LookupTypeNameOid(ParseState *pstate, const TypeName *typeName, bool missing_ok) +{ + Oid typoid; + Type tup; + + tup = LookupTypeName(pstate, typeName, NULL, missing_ok); + if (tup == NULL) + { + if (!missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" does not exist", + TypeNameToString(typeName)), + parser_errposition(pstate, typeName->location))); + + return InvalidOid; + } + + typoid = HeapTupleGetOid(tup); + ReleaseSysCache(tup); + + return typoid; +} + /* * typenameType - given a TypeName, return a Type structure and typmod * @@ -196,7 +244,7 @@ typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p) { Type tup; - tup = LookupTypeName(pstate, typeName, typmod_p); + tup = LookupTypeName(pstate, typeName, typmod_p, false); if (tup == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 5023d163a50..5d73562a4fc 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -135,7 +135,7 @@ regprocin(PG_FUNCTION_ARGS) * pg_proc entries in the current search path. */ names = stringToQualifiedNameList(pro_name_or_oid); - clist = FuncnameGetCandidates(names, -1, NIL, false, false); + clist = FuncnameGetCandidates(names, -1, NIL, false, false, false); if (clist == NULL) ereport(ERROR, @@ -192,7 +192,7 @@ regprocout(PG_FUNCTION_ARGS) * qualify it. */ clist = FuncnameGetCandidates(list_make1(makeString(proname)), - -1, NIL, false, false); + -1, NIL, false, false, false); if (clist != NULL && clist->next == NULL && clist->oid == proid) nspname = NULL; @@ -279,7 +279,7 @@ regprocedurein(PG_FUNCTION_ARGS) */ parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes); - clist = FuncnameGetCandidates(names, nargs, NIL, false, false); + clist = FuncnameGetCandidates(names, nargs, NIL, false, false, false); for (; clist; clist = clist->next) { diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index 810ffa3be17..b30e5e8d025 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -71,7 +71,8 @@ extern bool TypeIsVisible(Oid typid); extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs, List *argnames, bool expand_variadic, - bool expand_defaults); + bool expand_defaults, + bool missing_ok); extern bool FunctionIsVisible(Oid funcid); extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright); diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h index 9208bad26d7..ab73148dcaa 100644 --- a/src/include/parser/parse_type.h +++ b/src/include/parser/parse_type.h @@ -20,7 +20,9 @@ typedef HeapTuple Type; extern Type LookupTypeName(ParseState *pstate, const TypeName *typeName, - int32 *typmod_p); + int32 *typmod_p, bool missing_ok); +extern Oid LookupTypeNameOid(ParseState *pstate, const TypeName *typeName, + bool missing_ok); extern Type typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p); extern Oid typenameTypeId(ParseState *pstate, const TypeName *typeName); diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 05cd1710584..5afc2e50044 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -1665,7 +1665,7 @@ plpgsql_parse_wordtype(char *ident) * Word wasn't found in the namespace stack. Try to find a data type with * that name, but ignore shell types and complex types. */ - typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL); + typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL, false); if (typeTup) { Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out index 85994016e15..6910b627c15 100644 --- a/src/test/regress/expected/drop_if_exists.out +++ b/src/test/regress/expected/drop_if_exists.out @@ -169,11 +169,15 @@ NOTICE: cast from type text to type text does not exist, skipping DROP TRIGGER test_trigger_exists ON test_exists; ERROR: trigger "test_trigger_exists" for table "test_exists" does not exist DROP TRIGGER IF EXISTS test_trigger_exists ON test_exists; -NOTICE: trigger "test_trigger_exists" for table "test_exists" does not exist, skipping +NOTICE: trigger "test_trigger_exists" for relation "test_exists" does not exist, skipping DROP TRIGGER test_trigger_exists ON no_such_table; ERROR: relation "no_such_table" does not exist DROP TRIGGER IF EXISTS test_trigger_exists ON no_such_table; -ERROR: relation "no_such_table" does not exist +NOTICE: relation "no_such_table" does not exist, skipping +DROP TRIGGER test_trigger_exists ON no_such_schema.no_such_table; +ERROR: schema "no_such_schema" does not exist +DROP TRIGGER IF EXISTS test_trigger_exists ON no_such_schema.no_such_table; +NOTICE: schema "no_such_schema" does not exist, skipping CREATE TRIGGER test_trigger_exists BEFORE UPDATE ON test_exists FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); @@ -186,7 +190,11 @@ NOTICE: rule "test_rule_exists" for relation "test_exists" does not exist, skip DROP RULE test_rule_exists ON no_such_table; ERROR: relation "no_such_table" does not exist DROP RULE IF EXISTS test_rule_exists ON no_such_table; -ERROR: relation "no_such_table" does not exist +NOTICE: relation "no_such_table" does not exist, skipping +DROP RULE test_rule_exists ON no_such_schema.no_such_table; +ERROR: schema "no_such_schema" does not exist +DROP RULE IF EXISTS test_rule_exists ON no_such_schema.no_such_table; +NOTICE: schema "no_such_schema" does not exist, skipping CREATE RULE test_rule_exists AS ON INSERT TO test_exists DO INSTEAD INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); @@ -223,3 +231,68 @@ ERROR: access method "no_such_am" does not exist DROP TABLE IF EXISTS test_exists; DROP TABLE test_exists; ERROR: table "test_exists" does not exist +-- be tolerant with missing schemas, types, etc +DROP AGGREGATE IF EXISTS no_such_schema.foo(int); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP AGGREGATE IF EXISTS foo(no_such_type); +NOTICE: type "no_such_type" does not exist, skipping +DROP AGGREGATE IF EXISTS foo(no_such_schema.no_such_type); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP CAST IF EXISTS (INTEGER AS no_such_type2); +NOTICE: type "no_such_type2" does not exist, skipping +DROP CAST IF EXISTS (no_such_type1 AS INTEGER); +NOTICE: type "no_such_type1" does not exist, skipping +DROP CAST IF EXISTS (INTEGER AS no_such_schema.bar); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP CAST IF EXISTS (no_such_schema.foo AS INTEGER); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP COLLATION IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP CONVERSION IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP DOMAIN IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP FOREIGN TABLE IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP FUNCTION IF EXISTS no_such_schema.foo(); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP FUNCTION IF EXISTS foo(no_such_type); +NOTICE: type "no_such_type" does not exist, skipping +DROP FUNCTION IF EXISTS foo(no_such_schema.no_such_type); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP INDEX IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP MATERIALIZED VIEW IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP OPERATOR IF EXISTS no_such_schema.+ (int, int); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP OPERATOR IF EXISTS + (no_such_type, no_such_type); +NOTICE: type "no_such_type" does not exist, skipping +DROP OPERATOR IF EXISTS + (no_such_schema.no_such_type, no_such_schema.no_such_type); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP OPERATOR IF EXISTS # (NONE, no_such_schema.no_such_type); +NOTICE: schema "no_such_schema" does not exist, skipping +DROP OPERATOR CLASS IF EXISTS no_such_schema.widget_ops USING btree; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP OPERATOR FAMILY IF EXISTS no_such_schema.float_ops USING btree; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP RULE IF EXISTS foo ON no_such_schema.bar; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP SEQUENCE IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP TABLE IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP TEXT SEARCH CONFIGURATION IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP TEXT SEARCH DICTIONARY IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP TEXT SEARCH PARSER IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP TEXT SEARCH TEMPLATE IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP TRIGGER IF EXISTS foo ON no_such_schema.bar; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP TYPE IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping +DROP VIEW IF EXISTS no_such_schema.foo; +NOTICE: schema "no_such_schema" does not exist, skipping diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out index 656d47f9966..d4723b223d7 100644 --- a/src/test/regress/expected/event_trigger.out +++ b/src/test/regress/expected/event_trigger.out @@ -280,7 +280,7 @@ SELECT * FROM dropped_objects WHERE schema IS NULL OR schema <> 'pg_toast'; (23 rows) DROP OWNED BY regression_bob; -NOTICE: table "audit_tbls_schema_one_table_two" does not exist, skipping +NOTICE: schema "audit_tbls" does not exist, skipping CONTEXT: SQL statement "DROP TABLE IF EXISTS audit_tbls.audit_tbls_schema_one_table_two" PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement SELECT * FROM dropped_objects WHERE type = 'schema'; diff --git a/src/test/regress/sql/drop_if_exists.sql b/src/test/regress/sql/drop_if_exists.sql index 633056619aa..03547ccae7a 100644 --- a/src/test/regress/sql/drop_if_exists.sql +++ b/src/test/regress/sql/drop_if_exists.sql @@ -182,6 +182,9 @@ DROP TRIGGER IF EXISTS test_trigger_exists ON test_exists; DROP TRIGGER test_trigger_exists ON no_such_table; DROP TRIGGER IF EXISTS test_trigger_exists ON no_such_table; +DROP TRIGGER test_trigger_exists ON no_such_schema.no_such_table; +DROP TRIGGER IF EXISTS test_trigger_exists ON no_such_schema.no_such_table; + CREATE TRIGGER test_trigger_exists BEFORE UPDATE ON test_exists FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); @@ -194,6 +197,9 @@ DROP RULE IF EXISTS test_rule_exists ON test_exists; DROP RULE test_rule_exists ON no_such_table; DROP RULE IF EXISTS test_rule_exists ON no_such_table; +DROP RULE test_rule_exists ON no_such_schema.no_such_table; +DROP RULE IF EXISTS test_rule_exists ON no_such_schema.no_such_table; + CREATE RULE test_rule_exists AS ON INSERT TO test_exists DO INSTEAD INSERT INTO test_exists VALUES (NEW.a, NEW.b || NEW.a::text); @@ -226,3 +232,38 @@ DROP OPERATOR FAMILY IF EXISTS test_operator_family USING no_such_am; DROP TABLE IF EXISTS test_exists; DROP TABLE test_exists; + +-- be tolerant with missing schemas, types, etc + +DROP AGGREGATE IF EXISTS no_such_schema.foo(int); +DROP AGGREGATE IF EXISTS foo(no_such_type); +DROP AGGREGATE IF EXISTS foo(no_such_schema.no_such_type); +DROP CAST IF EXISTS (INTEGER AS no_such_type2); +DROP CAST IF EXISTS (no_such_type1 AS INTEGER); +DROP CAST IF EXISTS (INTEGER AS no_such_schema.bar); +DROP CAST IF EXISTS (no_such_schema.foo AS INTEGER); +DROP COLLATION IF EXISTS no_such_schema.foo; +DROP CONVERSION IF EXISTS no_such_schema.foo; +DROP DOMAIN IF EXISTS no_such_schema.foo; +DROP FOREIGN TABLE IF EXISTS no_such_schema.foo; +DROP FUNCTION IF EXISTS no_such_schema.foo(); +DROP FUNCTION IF EXISTS foo(no_such_type); +DROP FUNCTION IF EXISTS foo(no_such_schema.no_such_type); +DROP INDEX IF EXISTS no_such_schema.foo; +DROP MATERIALIZED VIEW IF EXISTS no_such_schema.foo; +DROP OPERATOR IF EXISTS no_such_schema.+ (int, int); +DROP OPERATOR IF EXISTS + (no_such_type, no_such_type); +DROP OPERATOR IF EXISTS + (no_such_schema.no_such_type, no_such_schema.no_such_type); +DROP OPERATOR IF EXISTS # (NONE, no_such_schema.no_such_type); +DROP OPERATOR CLASS IF EXISTS no_such_schema.widget_ops USING btree; +DROP OPERATOR FAMILY IF EXISTS no_such_schema.float_ops USING btree; +DROP RULE IF EXISTS foo ON no_such_schema.bar; +DROP SEQUENCE IF EXISTS no_such_schema.foo; +DROP TABLE IF EXISTS no_such_schema.foo; +DROP TEXT SEARCH CONFIGURATION IF EXISTS no_such_schema.foo; +DROP TEXT SEARCH DICTIONARY IF EXISTS no_such_schema.foo; +DROP TEXT SEARCH PARSER IF EXISTS no_such_schema.foo; +DROP TEXT SEARCH TEMPLATE IF EXISTS no_such_schema.foo; +DROP TRIGGER IF EXISTS foo ON no_such_schema.bar; +DROP TYPE IF EXISTS no_such_schema.foo; +DROP VIEW IF EXISTS no_such_schema.foo; -- GitLab