mirror of https://github.com/citusdata/citus.git
Add helper UDFs to inspect object dependencies (#5293)
- citus_get_all_dependencies_for_object: emulate what Citus would qualify as dependency when adding a new node - citus_get_dependencies_for_object: emulate what Citus would qualify as dependency when creating an object Example use: ```SQL -- find all the depedencies of table test SELECT pg_identify_object(t.classid, t.objid, t.objsubid) FROM (SELECT * FROM pg_get_object_address('table', '{test}', '{}')) as addr JOIN LATERAL citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int) ON TRUE ORDER BY 1; ```pull/5370/head
parent
169084f1bb
commit
31c8f279ac
|
@ -207,6 +207,31 @@ GetDependenciesForObject(const ObjectAddress *target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetAllDependenciesForObject returns a list of all the ObjectAddresses to be
|
||||||
|
* created in order before the target object could safely be created on a
|
||||||
|
* worker. As a caller, you probably need GetDependenciesForObject() which
|
||||||
|
* eliminates already distributed objects from the returned list.
|
||||||
|
*
|
||||||
|
* Some of the object might already be created on a worker. It should be created
|
||||||
|
* in an idempotent way.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
GetAllDependenciesForObject(const ObjectAddress *target)
|
||||||
|
{
|
||||||
|
ObjectAddressCollector collector = { 0 };
|
||||||
|
InitObjectAddressCollector(&collector);
|
||||||
|
|
||||||
|
RecurseObjectDependencies(*target,
|
||||||
|
&ExpandCitusSupportedTypes,
|
||||||
|
&FollowAllSupportedDependencies,
|
||||||
|
&ApplyAddToDependencyList,
|
||||||
|
&collector);
|
||||||
|
|
||||||
|
return collector.dependencyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OrderObjectAddressListInDependencyOrder given a list of ObjectAddresses return a new
|
* OrderObjectAddressListInDependencyOrder given a list of ObjectAddresses return a new
|
||||||
* list of the same ObjectAddresses ordered on dependency order where dependencies
|
* list of the same ObjectAddresses ordered on dependency order where dependencies
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* test/src/dependency.c
|
||||||
|
*
|
||||||
|
* This file contains functions to exercise dependency resolution for objects.
|
||||||
|
*
|
||||||
|
* Copyright (c) Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
#include "c.h"
|
||||||
|
#include "fmgr.h"
|
||||||
|
|
||||||
|
#include "distributed/listutils.h"
|
||||||
|
#include "distributed/metadata/dependency.h"
|
||||||
|
#include "distributed/metadata_cache.h"
|
||||||
|
#include "distributed/tuplestore.h"
|
||||||
|
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(citus_get_all_dependencies_for_object);
|
||||||
|
PG_FUNCTION_INFO_V1(citus_get_dependencies_for_object);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* citus_get_all_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
*
|
||||||
|
* citus_get_all_dependencies_for_object gets an object and returns all of its
|
||||||
|
* dependencies irrespective of whether the dependencies are already distributed
|
||||||
|
* or not.
|
||||||
|
*
|
||||||
|
* This is to emulate what Citus would qualify as dependency when adding a new
|
||||||
|
* node.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
citus_get_all_dependencies_for_object(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
CheckCitusVersion(ERROR);
|
||||||
|
|
||||||
|
Oid classid = PG_GETARG_OID(0);
|
||||||
|
Oid objid = PG_GETARG_OID(1);
|
||||||
|
int32 objsubid = PG_GETARG_INT32(2);
|
||||||
|
|
||||||
|
TupleDesc tupleDescriptor = NULL;
|
||||||
|
Tuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);
|
||||||
|
|
||||||
|
ObjectAddress address = { 0 };
|
||||||
|
ObjectAddressSubSet(address, classid, objid, objsubid);
|
||||||
|
|
||||||
|
List *dependencies = GetAllDependenciesForObject(&address);
|
||||||
|
ObjectAddress *dependency = NULL;
|
||||||
|
foreach_ptr(dependency, dependencies)
|
||||||
|
{
|
||||||
|
Datum values[3];
|
||||||
|
bool isNulls[3];
|
||||||
|
|
||||||
|
memset(values, 0, sizeof(values));
|
||||||
|
memset(isNulls, 0, sizeof(isNulls));
|
||||||
|
|
||||||
|
values[0] = ObjectIdGetDatum(dependency->classId);
|
||||||
|
values[1] = ObjectIdGetDatum(dependency->objectId);
|
||||||
|
values[2] = Int32GetDatum(dependency->objectSubId);
|
||||||
|
|
||||||
|
tuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* citus_get_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
*
|
||||||
|
* citus_get_dependencies_for_object gets an object and returns all of its
|
||||||
|
* dependencies that are not already distributed.
|
||||||
|
*
|
||||||
|
* This is to emulate what Citus would qualify as dependency when creating
|
||||||
|
* a new object.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
citus_get_dependencies_for_object(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
CheckCitusVersion(ERROR);
|
||||||
|
|
||||||
|
Oid classid = PG_GETARG_OID(0);
|
||||||
|
Oid objid = PG_GETARG_OID(1);
|
||||||
|
int32 objsubid = PG_GETARG_INT32(2);
|
||||||
|
|
||||||
|
TupleDesc tupleDescriptor = NULL;
|
||||||
|
Tuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);
|
||||||
|
|
||||||
|
ObjectAddress address = { 0 };
|
||||||
|
ObjectAddressSubSet(address, classid, objid, objsubid);
|
||||||
|
|
||||||
|
List *dependencies = GetDependenciesForObject(&address);
|
||||||
|
ObjectAddress *dependency = NULL;
|
||||||
|
foreach_ptr(dependency, dependencies)
|
||||||
|
{
|
||||||
|
Datum values[3];
|
||||||
|
bool isNulls[3];
|
||||||
|
|
||||||
|
memset(values, 0, sizeof(values));
|
||||||
|
memset(isNulls, 0, sizeof(isNulls));
|
||||||
|
|
||||||
|
values[0] = ObjectIdGetDatum(dependency->classId);
|
||||||
|
values[1] = ObjectIdGetDatum(dependency->objectId);
|
||||||
|
values[2] = Int32GetDatum(dependency->objectSubId);
|
||||||
|
|
||||||
|
tuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
}
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
extern List * GetUniqueDependenciesList(List *objectAddressesList);
|
extern List * GetUniqueDependenciesList(List *objectAddressesList);
|
||||||
extern List * GetDependenciesForObject(const ObjectAddress *target);
|
extern List * GetDependenciesForObject(const ObjectAddress *target);
|
||||||
|
extern List * GetAllDependenciesForObject(const ObjectAddress *target);
|
||||||
extern List * OrderObjectAddressListInDependencyOrder(List *objectAddressList);
|
extern List * OrderObjectAddressListInDependencyOrder(List *objectAddressList);
|
||||||
extern bool SupportedDependencyByCitus(const ObjectAddress *address);
|
extern bool SupportedDependencyByCitus(const ObjectAddress *address);
|
||||||
extern List * GetPgDependTuplesForDependingObjects(Oid targetObjectClassId,
|
extern List * GetPgDependTuplesForDependingObjects(Oid targetObjectClassId,
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
CREATE SCHEMA "object prop";
|
||||||
|
SET search_path TO "object prop";
|
||||||
|
CREATE OR REPLACE FUNCTION citus_get_all_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
RETURNS SETOF RECORD
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus', $$citus_get_all_dependencies_for_object$$;
|
||||||
|
COMMENT ON FUNCTION citus_get_all_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
IS 'emulate what Citus would qualify as dependency when adding a new node';
|
||||||
|
CREATE OR REPLACE FUNCTION citus_get_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
RETURNS SETOF RECORD
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus', $$citus_get_dependencies_for_object$$;
|
||||||
|
COMMENT ON FUNCTION citus_get_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
IS 'emulate what Citus would qualify as dependency when creating the object';
|
||||||
|
create type t1 as (a int);
|
||||||
|
create table test(a int, b t1);
|
||||||
|
SELECT create_distributed_table('test', 'a');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
CREATE VIEW v1 AS SELECT * FROM test;
|
||||||
|
-- find all the dependencies of table test
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('table', '{test}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
pg_identify_object
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
("composite type","""object prop""",t1,"""object prop"".t1")
|
||||||
|
(schema,,"""object prop""","""object prop""")
|
||||||
|
(type,"""object prop""",t1,"""object prop"".t1")
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
-- find all the dependencies of view v1
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('view', '{v1}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
pg_identify_object
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
("composite type","""object prop""",t1,"""object prop"".t1")
|
||||||
|
(schema,,"""object prop""","""object prop""")
|
||||||
|
(type,"""object prop""",t1,"""object prop"".t1")
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
-- find all the dependencies of type t1
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('type', '{t1}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
pg_identify_object
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
("composite type","""object prop""",t1,"""object prop"".t1")
|
||||||
|
(schema,,"""object prop""","""object prop""")
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- find non-distributed dependencies of table test
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('table', '{test}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
citus_get_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
pg_identify_object
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- find non-distributed dependencies of the local table test2
|
||||||
|
CREATE SCHEMA objectprop2;
|
||||||
|
create table objectprop2.test2(a int, b t1);
|
||||||
|
SET search_path TO objectprop2;
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('table', '{test2}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
"object prop".citus_get_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
pg_identify_object
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
(schema,,objectprop2,objectprop2)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA "object prop", objectprop2 CASCADE;
|
|
@ -98,6 +98,8 @@ test: binary_protocol
|
||||||
test: alter_table_set_access_method
|
test: alter_table_set_access_method
|
||||||
test: alter_distributed_table
|
test: alter_distributed_table
|
||||||
test: issue_5248
|
test: issue_5248
|
||||||
|
test: object_propagation_debug
|
||||||
|
|
||||||
|
|
||||||
# ---------
|
# ---------
|
||||||
# test that no tests leaked intermediate results. This should always be last
|
# test that no tests leaked intermediate results. This should always be last
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
CREATE SCHEMA "object prop";
|
||||||
|
SET search_path TO "object prop";
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citus_get_all_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
RETURNS SETOF RECORD
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus', $$citus_get_all_dependencies_for_object$$;
|
||||||
|
COMMENT ON FUNCTION citus_get_all_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
IS 'emulate what Citus would qualify as dependency when adding a new node';
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION citus_get_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
RETURNS SETOF RECORD
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus', $$citus_get_dependencies_for_object$$;
|
||||||
|
COMMENT ON FUNCTION citus_get_dependencies_for_object(classid oid, objid oid, objsubid int)
|
||||||
|
IS 'emulate what Citus would qualify as dependency when creating the object';
|
||||||
|
|
||||||
|
create type t1 as (a int);
|
||||||
|
create table test(a int, b t1);
|
||||||
|
SELECT create_distributed_table('test', 'a');
|
||||||
|
CREATE VIEW v1 AS SELECT * FROM test;
|
||||||
|
|
||||||
|
-- find all the dependencies of table test
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('table', '{test}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
|
||||||
|
-- find all the dependencies of view v1
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('view', '{v1}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
|
||||||
|
-- find all the dependencies of type t1
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('type', '{t1}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
|
||||||
|
-- find non-distributed dependencies of table test
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('table', '{test}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
citus_get_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
|
||||||
|
-- find non-distributed dependencies of the local table test2
|
||||||
|
CREATE SCHEMA objectprop2;
|
||||||
|
create table objectprop2.test2(a int, b t1);
|
||||||
|
SET search_path TO objectprop2;
|
||||||
|
SELECT
|
||||||
|
pg_identify_object(t.classid, t.objid, t.objsubid)
|
||||||
|
FROM
|
||||||
|
(SELECT * FROM pg_get_object_address('table', '{test2}', '{}')) as addr
|
||||||
|
JOIN LATERAL
|
||||||
|
"object prop".citus_get_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int)
|
||||||
|
ON TRUE
|
||||||
|
ORDER BY 1;
|
||||||
|
|
||||||
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA "object prop", objectprop2 CASCADE;
|
Loading…
Reference in New Issue