mirror of https://github.com/citusdata/citus.git
220 lines
6.3 KiB
C
220 lines
6.3 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* foreign_key_relationship_query.c
|
|
*
|
|
* This file contains UDFs for getting foreign constraint relationship between
|
|
* distributed tables.
|
|
*
|
|
* Copyright (c) Citus Data, Inc.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
#include "fmgr.h"
|
|
#include "funcapi.h"
|
|
|
|
#include "catalog/dependency.h"
|
|
#include "catalog/pg_constraint.h"
|
|
#include "distributed/foreign_key_relationship.h"
|
|
#include "distributed/coordinator_protocol.h"
|
|
#include "distributed/listutils.h"
|
|
#include "distributed/metadata_cache.h"
|
|
#include "distributed/tuplestore.h"
|
|
#include "distributed/version_compat.h"
|
|
#include "utils/builtins.h"
|
|
|
|
|
|
#define GET_FKEY_CONNECTED_RELATIONS_COLUMNS 1
|
|
|
|
|
|
/* these functions are only exported in the regression tests */
|
|
PG_FUNCTION_INFO_V1(get_referencing_relation_id_list);
|
|
PG_FUNCTION_INFO_V1(get_referenced_relation_id_list);
|
|
PG_FUNCTION_INFO_V1(get_foreign_key_connected_relations);
|
|
PG_FUNCTION_INFO_V1(drop_constraint_cascade_via_perform_deletion);
|
|
|
|
|
|
/*
|
|
* drop_constraint_cascade_via_perform_deletion simply drops constraint on
|
|
* relation via performDeletion.
|
|
*/
|
|
Datum
|
|
drop_constraint_cascade_via_perform_deletion(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid relationId = PG_GETARG_OID(0);
|
|
|
|
if (PG_ARGISNULL(1))
|
|
{
|
|
/* avoid unexpected crashes in regression tests */
|
|
ereport(ERROR, (errmsg("cannot perform operation without constraint "
|
|
"name argument")));
|
|
}
|
|
|
|
text *constraintNameText = PG_GETARG_TEXT_P(1);
|
|
char *constraintName = text_to_cstring(constraintNameText);
|
|
|
|
/* error if constraint does not exist */
|
|
bool missingOk = false;
|
|
Oid constraintId = get_relation_constraint_oid(relationId, constraintName, missingOk);
|
|
|
|
ObjectAddress constraintObjectAddress;
|
|
constraintObjectAddress.classId = ConstraintRelationId;
|
|
constraintObjectAddress.objectId = constraintId;
|
|
constraintObjectAddress.objectSubId = 0;
|
|
|
|
performDeletion(&constraintObjectAddress, DROP_CASCADE, 0);
|
|
|
|
PG_RETURN_VOID();
|
|
}
|
|
|
|
|
|
/*
|
|
* get_referencing_relation_id_list returns the list of table oids that is referencing
|
|
* by given oid recursively. It uses the list cached in the distributed table cache
|
|
* entry.
|
|
*/
|
|
Datum
|
|
get_referencing_relation_id_list(PG_FUNCTION_ARGS)
|
|
{
|
|
CheckCitusVersion(ERROR);
|
|
|
|
FuncCallContext *functionContext = NULL;
|
|
ListCell *foreignRelationCell = NULL;
|
|
|
|
/* for the first we call this UDF, we need to populate the result to return set */
|
|
if (SRF_IS_FIRSTCALL())
|
|
{
|
|
Oid relationId = PG_GETARG_OID(0);
|
|
CitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);
|
|
|
|
/* create a function context for cross-call persistence */
|
|
functionContext = SRF_FIRSTCALL_INIT();
|
|
|
|
MemoryContext oldContext =
|
|
MemoryContextSwitchTo(functionContext->multi_call_memory_ctx);
|
|
List *refList = list_copy(
|
|
cacheEntry->referencingRelationsViaForeignKey);
|
|
ListCellAndListWrapper *wrapper = palloc0(sizeof(ListCellAndListWrapper));
|
|
foreignRelationCell = list_head(refList);
|
|
wrapper->list = refList;
|
|
wrapper->listCell = foreignRelationCell;
|
|
functionContext->user_fctx = wrapper;
|
|
MemoryContextSwitchTo(oldContext);
|
|
}
|
|
|
|
/*
|
|
* On every call to this function, we get the current position in the
|
|
* statement list. We then iterate to the next position in the list and
|
|
* return the current statement, if we have not yet reached the end of
|
|
* list.
|
|
*/
|
|
functionContext = SRF_PERCALL_SETUP();
|
|
|
|
ListCellAndListWrapper *wrapper =
|
|
(ListCellAndListWrapper *) functionContext->user_fctx;
|
|
if (wrapper->listCell != NULL)
|
|
{
|
|
Oid refId = lfirst_oid(wrapper->listCell);
|
|
|
|
wrapper->listCell = lnext(wrapper->list, wrapper->listCell);
|
|
|
|
SRF_RETURN_NEXT(functionContext, ObjectIdGetDatum(refId));
|
|
}
|
|
else
|
|
{
|
|
SRF_RETURN_DONE(functionContext);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* get_referenced_relation_id_list returns the list of table oids that is referenced
|
|
* by given oid recursively. It uses the list cached in the distributed table cache
|
|
* entry.
|
|
*/
|
|
Datum
|
|
get_referenced_relation_id_list(PG_FUNCTION_ARGS)
|
|
{
|
|
CheckCitusVersion(ERROR);
|
|
|
|
FuncCallContext *functionContext = NULL;
|
|
ListCell *foreignRelationCell = NULL;
|
|
|
|
/* for the first we call this UDF, we need to populate the result to return set */
|
|
if (SRF_IS_FIRSTCALL())
|
|
{
|
|
Oid relationId = PG_GETARG_OID(0);
|
|
CitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);
|
|
|
|
/* create a function context for cross-call persistence */
|
|
functionContext = SRF_FIRSTCALL_INIT();
|
|
|
|
MemoryContext oldContext =
|
|
MemoryContextSwitchTo(functionContext->multi_call_memory_ctx);
|
|
List *refList = list_copy(cacheEntry->referencedRelationsViaForeignKey);
|
|
foreignRelationCell = list_head(refList);
|
|
ListCellAndListWrapper *wrapper = palloc0(sizeof(ListCellAndListWrapper));
|
|
wrapper->list = refList;
|
|
wrapper->listCell = foreignRelationCell;
|
|
functionContext->user_fctx = wrapper;
|
|
MemoryContextSwitchTo(oldContext);
|
|
}
|
|
|
|
/*
|
|
* On every call to this function, we get the current position in the
|
|
* statement list. We then iterate to the next position in the list and
|
|
* return the current statement, if we have not yet reached the end of
|
|
* list.
|
|
*/
|
|
functionContext = SRF_PERCALL_SETUP();
|
|
|
|
ListCellAndListWrapper *wrapper =
|
|
(ListCellAndListWrapper *) functionContext->user_fctx;
|
|
|
|
if (wrapper->listCell != NULL)
|
|
{
|
|
Oid refId = lfirst_oid(wrapper->listCell);
|
|
|
|
wrapper->listCell = lnext(wrapper->list, wrapper->listCell);
|
|
|
|
SRF_RETURN_NEXT(functionContext, ObjectIdGetDatum(refId));
|
|
}
|
|
else
|
|
{
|
|
SRF_RETURN_DONE(functionContext);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* get_foreign_key_connected_relations takes a relation, and returns relations
|
|
* that are connected to input relation via a foreign key graph.
|
|
*/
|
|
Datum
|
|
get_foreign_key_connected_relations(PG_FUNCTION_ARGS)
|
|
{
|
|
CheckCitusVersion(ERROR);
|
|
|
|
Oid relationId = PG_GETARG_OID(0);
|
|
|
|
TupleDesc tupleDescriptor = NULL;
|
|
Tuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);
|
|
|
|
Oid connectedRelationId;
|
|
List *fkeyConnectedRelationIdList = GetForeignKeyConnectedRelationIdList(relationId);
|
|
foreach_oid(connectedRelationId, fkeyConnectedRelationIdList)
|
|
{
|
|
Datum values[GET_FKEY_CONNECTED_RELATIONS_COLUMNS];
|
|
bool nulls[GET_FKEY_CONNECTED_RELATIONS_COLUMNS];
|
|
|
|
memset(values, 0, sizeof(values));
|
|
memset(nulls, false, sizeof(nulls));
|
|
|
|
values[0] = ObjectIdGetDatum(connectedRelationId);
|
|
|
|
tuplestore_putvalues(tupleStore, tupleDescriptor, values, nulls);
|
|
}
|
|
PG_RETURN_VOID();
|
|
}
|