--
--- Test earth distance functions
+-- Test earthdistance extension
--
+-- In this file we also do some testing of extension create/drop scenarios.
+-- That's really exercising the core database's dependency logic, so ideally
+-- we'd do it in the core regression tests, but we can't for lack of suitable
+-- guaranteed-available extensions. earthdistance is a good test case because
+-- it has a dependency on the cube extension.
+--
+CREATE EXTENSION earthdistance; -- fail, must install cube first
+ERROR: required extension "cube" is not installed
CREATE EXTENSION cube;
CREATE EXTENSION earthdistance;
--
t
(1 row)
+--
+-- Now we are going to test extension create/drop scenarios.
+--
+-- list what's installed
+\dT
+ List of data types
+ Schema | Name | Description
+--------+-------+---------------------------------------------------------------------------------------------
+ public | cube | multi-dimensional cube '(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)'
+ public | earth |
+(2 rows)
+
+\df
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+-------------------+------------------+------------------------------------------+--------
+ public | cube | cube | cube, double precision | normal
+ public | cube | cube | cube, double precision, double precision | normal
+ public | cube | cube | double precision | normal
+ public | cube | cube | double precision, double precision | normal
+ public | cube | cube | double precision[] | normal
+ public | cube | cube | double precision[], double precision[] | normal
+ public | cube_cmp | integer | cube, cube | normal
+ public | cube_contained | boolean | cube, cube | normal
+ public | cube_contains | boolean | cube, cube | normal
+ public | cube_dim | integer | cube | normal
+ public | cube_distance | double precision | cube, cube | normal
+ public | cube_enlarge | cube | cube, double precision, integer | normal
+ public | cube_eq | boolean | cube, cube | normal
+ public | cube_ge | boolean | cube, cube | normal
+ public | cube_gt | boolean | cube, cube | normal
+ public | cube_in | cube | cstring | normal
+ public | cube_inter | cube | cube, cube | normal
+ public | cube_is_point | boolean | cube | normal
+ public | cube_le | boolean | cube, cube | normal
+ public | cube_ll_coord | double precision | cube, integer | normal
+ public | cube_lt | boolean | cube, cube | normal
+ public | cube_ne | boolean | cube, cube | normal
+ public | cube_out | cstring | cube | normal
+ public | cube_overlap | boolean | cube, cube | normal
+ public | cube_size | double precision | cube | normal
+ public | cube_subset | cube | cube, integer[] | normal
+ public | cube_union | cube | cube, cube | normal
+ public | cube_ur_coord | double precision | cube, integer | normal
+ public | earth | double precision | | normal
+ public | earth_box | cube | earth, double precision | normal
+ public | earth_distance | double precision | earth, earth | normal
+ public | g_cube_compress | internal | internal | normal
+ public | g_cube_consistent | boolean | internal, cube, integer, oid, internal | normal
+ public | g_cube_decompress | internal | internal | normal
+ public | g_cube_penalty | internal | internal, internal, internal | normal
+ public | g_cube_picksplit | internal | internal, internal | normal
+ public | g_cube_same | internal | cube, cube, internal | normal
+ public | g_cube_union | cube | internal, internal | normal
+ public | gc_to_sec | double precision | double precision | normal
+ public | geo_distance | double precision | point, point | normal
+ public | latitude | double precision | earth | normal
+ public | ll_to_earth | earth | double precision, double precision | normal
+ public | longitude | double precision | earth | normal
+ public | sec_to_gc | double precision | double precision | normal
+(44 rows)
+
+\do
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+------------------+--------------------------
+ public | && | cube | cube | boolean | overlaps
+ public | < | cube | cube | boolean | lower than
+ public | <= | cube | cube | boolean | lower than or equal to
+ public | <> | cube | cube | boolean | different
+ public | <@ | cube | cube | boolean | contained in
+ public | <@> | point | point | double precision |
+ public | = | cube | cube | boolean | same as
+ public | > | cube | cube | boolean | greater than
+ public | >= | cube | cube | boolean | greater than or equal to
+ public | @ | cube | cube | boolean | contains
+ public | @> | cube | cube | boolean | contains
+ public | ~ | cube | cube | boolean | contained in
+(12 rows)
+
+drop extension cube; -- fail, earthdistance requires it
+ERROR: cannot drop extension cube because other objects depend on it
+DETAIL: extension earthdistance depends on extension cube
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+drop extension earthdistance;
+drop type cube; -- fail, extension cube requires it
+ERROR: cannot drop type cube because extension cube requires it
+HINT: You can drop extension cube instead.
+-- list what's installed
+\dT
+ List of data types
+ Schema | Name | Description
+--------+------+---------------------------------------------------------------------------------------------
+ public | cube | multi-dimensional cube '(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)'
+(1 row)
+
+\df
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+-------------------+------------------+------------------------------------------+--------
+ public | cube | cube | cube, double precision | normal
+ public | cube | cube | cube, double precision, double precision | normal
+ public | cube | cube | double precision | normal
+ public | cube | cube | double precision, double precision | normal
+ public | cube | cube | double precision[] | normal
+ public | cube | cube | double precision[], double precision[] | normal
+ public | cube_cmp | integer | cube, cube | normal
+ public | cube_contained | boolean | cube, cube | normal
+ public | cube_contains | boolean | cube, cube | normal
+ public | cube_dim | integer | cube | normal
+ public | cube_distance | double precision | cube, cube | normal
+ public | cube_enlarge | cube | cube, double precision, integer | normal
+ public | cube_eq | boolean | cube, cube | normal
+ public | cube_ge | boolean | cube, cube | normal
+ public | cube_gt | boolean | cube, cube | normal
+ public | cube_in | cube | cstring | normal
+ public | cube_inter | cube | cube, cube | normal
+ public | cube_is_point | boolean | cube | normal
+ public | cube_le | boolean | cube, cube | normal
+ public | cube_ll_coord | double precision | cube, integer | normal
+ public | cube_lt | boolean | cube, cube | normal
+ public | cube_ne | boolean | cube, cube | normal
+ public | cube_out | cstring | cube | normal
+ public | cube_overlap | boolean | cube, cube | normal
+ public | cube_size | double precision | cube | normal
+ public | cube_subset | cube | cube, integer[] | normal
+ public | cube_union | cube | cube, cube | normal
+ public | cube_ur_coord | double precision | cube, integer | normal
+ public | g_cube_compress | internal | internal | normal
+ public | g_cube_consistent | boolean | internal, cube, integer, oid, internal | normal
+ public | g_cube_decompress | internal | internal | normal
+ public | g_cube_penalty | internal | internal, internal, internal | normal
+ public | g_cube_picksplit | internal | internal, internal | normal
+ public | g_cube_same | internal | cube, cube, internal | normal
+ public | g_cube_union | cube | internal, internal | normal
+(35 rows)
+
+\do
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+--------------------------
+ public | && | cube | cube | boolean | overlaps
+ public | < | cube | cube | boolean | lower than
+ public | <= | cube | cube | boolean | lower than or equal to
+ public | <> | cube | cube | boolean | different
+ public | <@ | cube | cube | boolean | contained in
+ public | = | cube | cube | boolean | same as
+ public | > | cube | cube | boolean | greater than
+ public | >= | cube | cube | boolean | greater than or equal to
+ public | @ | cube | cube | boolean | contains
+ public | @> | cube | cube | boolean | contains
+ public | ~ | cube | cube | boolean | contained in
+(11 rows)
+
+create table foo (f1 cube, f2 int);
+drop extension cube; -- fail, foo.f1 requires it
+ERROR: cannot drop extension cube because other objects depend on it
+DETAIL: table foo column f1 depends on type cube
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+drop table foo;
+drop extension cube;
+-- list what's installed
+\dT
+ List of data types
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\df
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+------+------------------+---------------------+------
+(0 rows)
+
+\do
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+-------------
+(0 rows)
+
+create schema c;
+create extension cube with schema c;
+-- list what's installed
+\dT public.*
+ List of data types
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\df public.*
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+------+------------------+---------------------+------
+(0 rows)
+
+\do public.*
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+-------------
+(0 rows)
+
+\dT c.*
+ List of data types
+ Schema | Name | Description
+--------+--------+---------------------------------------------------------------------------------------------
+ c | c.cube | multi-dimensional cube '(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)'
+(1 row)
+
+\df c.*
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+-------------------+------------------+--------------------------------------------+--------
+ c | cube | c.cube | c.cube, double precision | normal
+ c | cube | c.cube | c.cube, double precision, double precision | normal
+ c | cube | c.cube | double precision | normal
+ c | cube | c.cube | double precision, double precision | normal
+ c | cube | c.cube | double precision[] | normal
+ c | cube | c.cube | double precision[], double precision[] | normal
+ c | cube_cmp | integer | c.cube, c.cube | normal
+ c | cube_contained | boolean | c.cube, c.cube | normal
+ c | cube_contains | boolean | c.cube, c.cube | normal
+ c | cube_dim | integer | c.cube | normal
+ c | cube_distance | double precision | c.cube, c.cube | normal
+ c | cube_enlarge | c.cube | c.cube, double precision, integer | normal
+ c | cube_eq | boolean | c.cube, c.cube | normal
+ c | cube_ge | boolean | c.cube, c.cube | normal
+ c | cube_gt | boolean | c.cube, c.cube | normal
+ c | cube_in | c.cube | cstring | normal
+ c | cube_inter | c.cube | c.cube, c.cube | normal
+ c | cube_is_point | boolean | c.cube | normal
+ c | cube_le | boolean | c.cube, c.cube | normal
+ c | cube_ll_coord | double precision | c.cube, integer | normal
+ c | cube_lt | boolean | c.cube, c.cube | normal
+ c | cube_ne | boolean | c.cube, c.cube | normal
+ c | cube_out | cstring | c.cube | normal
+ c | cube_overlap | boolean | c.cube, c.cube | normal
+ c | cube_size | double precision | c.cube | normal
+ c | cube_subset | c.cube | c.cube, integer[] | normal
+ c | cube_union | c.cube | c.cube, c.cube | normal
+ c | cube_ur_coord | double precision | c.cube, integer | normal
+ c | g_cube_compress | internal | internal | normal
+ c | g_cube_consistent | boolean | internal, c.cube, integer, oid, internal | normal
+ c | g_cube_decompress | internal | internal | normal
+ c | g_cube_penalty | internal | internal, internal, internal | normal
+ c | g_cube_picksplit | internal | internal, internal | normal
+ c | g_cube_same | internal | c.cube, c.cube, internal | normal
+ c | g_cube_union | c.cube | internal, internal | normal
+(35 rows)
+
+\do c.*
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+--------------------------
+ c | && | c.cube | c.cube | boolean | overlaps
+ c | < | c.cube | c.cube | boolean | lower than
+ c | <= | c.cube | c.cube | boolean | lower than or equal to
+ c | <> | c.cube | c.cube | boolean | different
+ c | <@ | c.cube | c.cube | boolean | contained in
+ c | = | c.cube | c.cube | boolean | same as
+ c | > | c.cube | c.cube | boolean | greater than
+ c | >= | c.cube | c.cube | boolean | greater than or equal to
+ c | @ | c.cube | c.cube | boolean | contains
+ c | @> | c.cube | c.cube | boolean | contains
+ c | ~ | c.cube | c.cube | boolean | contained in
+(11 rows)
+
+create table foo (f1 c.cube, f2 int);
+drop extension cube; -- fail, foo.f1 requires it
+ERROR: cannot drop extension cube because other objects depend on it
+DETAIL: table foo column f1 depends on type c.cube
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+drop schema c; -- fail, cube requires it
+ERROR: cannot drop schema c because other objects depend on it
+DETAIL: extension cube depends on schema c
+table foo column f1 depends on type c.cube
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+drop extension cube cascade;
+NOTICE: drop cascades to table foo column f1
+\d foo
+ Table "public.foo"
+ Column | Type | Modifiers
+--------+---------+-----------
+ f2 | integer |
+
+-- list what's installed
+\dT public.*
+ List of data types
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\df public.*
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+------+------------------+---------------------+------
+(0 rows)
+
+\do public.*
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+-------------
+(0 rows)
+
+\dT c.*
+ List of data types
+ Schema | Name | Description
+--------+------+-------------
+(0 rows)
+
+\df c.*
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+------+------------------+---------------------+------
+(0 rows)
+
+\do c.*
+ List of operators
+ Schema | Name | Left arg type | Right arg type | Result type | Description
+--------+------+---------------+----------------+-------------+-------------
+(0 rows)
+
+drop schema c;
#define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */
#define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */
#define DEPFLAG_EXTENSION 0x0010 /* reached via extension dependency */
+#define DEPFLAG_REVERSE 0x0020 /* reverse internal/extension link */
/* expansible list of ObjectAddresses */
static bool object_address_present_add_flags(const ObjectAddress *object,
int flags,
ObjectAddresses *addrs);
+static bool stack_address_present_add_flags(const ObjectAddress *object,
+ int flags,
+ ObjectAddressStack *stack);
static void getRelationDescription(StringInfo buffer, Oid relid);
static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
ObjectAddress otherObject;
ObjectAddressStack mystack;
ObjectAddressExtra extra;
- ObjectAddressStack *stackptr;
/*
* If the target object is already being visited in an outer recursion
* auto dependency, too, if we had to. However there are no known cases
* where that would be necessary.
*/
- for (stackptr = stack; stackptr; stackptr = stackptr->next)
- {
- if (object->classId == stackptr->object->classId &&
- object->objectId == stackptr->object->objectId)
- {
- if (object->objectSubId == stackptr->object->objectSubId)
- {
- stackptr->flags |= flags;
- return;
- }
-
- /*
- * Could visit column with whole table already on stack; this is
- * the same case noted in object_address_present_add_flags().
- * (It's not clear this can really happen, but we might as well
- * check.)
- */
- if (stackptr->object->objectSubId == 0)
- return;
- }
- }
+ if (stack_address_present_add_flags(object, flags, stack))
+ return;
/*
* It's also possible that the target object has already been completely
/*
* The target object might be internally dependent on some other object
- * (its "owner"). If so, and if we aren't recursing from the owning
- * object, we have to transform this deletion request into a deletion
- * request of the owning object. (We'll eventually recurse back to this
- * object, but the owning object has to be visited first so it will be
- * deleted after.) The way to find out about this is to scan the
- * pg_depend entries that show what this object depends on.
+ * (its "owner"), and/or be a member of an extension (also considered its
+ * owner). If so, and if we aren't recursing from the owning object, we
+ * have to transform this deletion request into a deletion request of the
+ * owning object. (We'll eventually recurse back to this object, but the
+ * owning object has to be visited first so it will be deleted after.)
+ * The way to find out about this is to scan the pg_depend entries that
+ * show what this object depends on.
*/
ScanKeyInit(&key[0],
Anum_pg_depend_classid,
* 1. At the outermost recursion level, disallow the DROP. (We
* just ereport here, rather than proceeding, since no other
* dependencies are likely to be interesting.) However, if
- * the other object is listed in pendingObjects, just release
+ * the owning object is listed in pendingObjects, just release
* the caller's lock and return; we'll eventually complete the
* DROP when we reach that entry in the pending list.
*/
/*
* 2. When recursing from the other end of this dependency,
- * it's okay to continue with the deletion. This holds when
+ * it's okay to continue with the deletion. This holds when
* recursing from a whole object that includes the nominal
- * other end as a component, too.
+ * other end as a component, too. Since there can be more
+ * than one "owning" object, we have to allow matches that
+ * are more than one level down in the stack.
*/
- if (stack->object->classId == otherObject.classId &&
- stack->object->objectId == otherObject.objectId &&
- (stack->object->objectSubId == otherObject.objectSubId ||
- stack->object->objectSubId == 0))
+ if (stack_address_present_add_flags(&otherObject, 0, stack))
break;
/*
- * 3. When recursing from anyplace else, transform this
- * deletion request into a delete of the other object.
+ * 3. Not all the owning objects have been visited, so
+ * transform this deletion request into a delete of this
+ * owning object.
*
* First, release caller's lock on this object and get
- * deletion lock on the other object. (We must release
+ * deletion lock on the owning object. (We must release
* caller's lock to avoid deadlock against a concurrent
- * deletion of the other object.)
+ * deletion of the owning object.)
*/
ReleaseDeletionLock(object);
AcquireDeletionLock(&otherObject);
/*
- * The other object might have been deleted while we waited to
- * lock it; if so, neither it nor the current object are
+ * The owning object might have been deleted while we waited
+ * to lock it; if so, neither it nor the current object are
* interesting anymore. We test this by checking the
* pg_depend entry (see notes below).
*/
}
/*
- * Okay, recurse to the other object instead of proceeding. We
- * treat this exactly as if the original reference had linked
- * to that object instead of this one; hence, pass through the
- * same flags and stack.
+ * Okay, recurse to the owning object instead of proceeding.
+ *
+ * We do not need to stack the current object; we want the
+ * traversal order to be as if the original reference had
+ * linked to the owning object instead of this one.
+ *
+ * The dependency type is a "reverse" dependency: we need to
+ * delete the owning object if this one is to be deleted, but
+ * this linkage is never a reason for an automatic deletion.
*/
findDependentObjects(&otherObject,
- flags,
+ DEPFLAG_REVERSE,
stack,
targetObjects,
pendingObjects,
return false;
}
+/*
+ * Similar to above, except we search an ObjectAddressStack.
+ */
+static bool
+stack_address_present_add_flags(const ObjectAddress *object,
+ int flags,
+ ObjectAddressStack *stack)
+{
+ ObjectAddressStack *stackptr;
+
+ for (stackptr = stack; stackptr; stackptr = stackptr->next)
+ {
+ const ObjectAddress *thisobj = stackptr->object;
+
+ if (object->classId == thisobj->classId &&
+ object->objectId == thisobj->objectId)
+ {
+ if (object->objectSubId == thisobj->objectSubId)
+ {
+ stackptr->flags |= flags;
+ return true;
+ }
+
+ /*
+ * Could visit column with whole table already on stack; this is
+ * the same case noted in object_address_present_add_flags(), and
+ * as in that case, we don't propagate flags for the component to
+ * the whole object.
+ */
+ if (thisobj->objectSubId == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
/*
* Record multiple dependencies from an ObjectAddresses array, after first
* removing any duplicates.