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
Önder Kalacı 2021-10-18 13:46:49 +02:00 committed by GitHub
parent 169084f1bb
commit 31c8f279ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 320 additions and 0 deletions

View File

@ -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
* list of the same ObjectAddresses ordered on dependency order where dependencies

View File

@ -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();
}

View File

@ -19,6 +19,7 @@
extern List * GetUniqueDependenciesList(List *objectAddressesList);
extern List * GetDependenciesForObject(const ObjectAddress *target);
extern List * GetAllDependenciesForObject(const ObjectAddress *target);
extern List * OrderObjectAddressListInDependencyOrder(List *objectAddressList);
extern bool SupportedDependencyByCitus(const ObjectAddress *address);
extern List * GetPgDependTuplesForDependingObjects(Oid targetObjectClassId,

View File

@ -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;

View File

@ -98,6 +98,8 @@ test: binary_protocol
test: alter_table_set_access_method
test: alter_distributed_table
test: issue_5248
test: object_propagation_debug
# ---------
# test that no tests leaked intermediate results. This should always be last

View File

@ -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;