mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
Make DROP IF EXISTS more consistently not fail
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
This commit is contained in:
@ -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);
|
||||
|
Reference in New Issue
Block a user