mirror of https://github.com/citusdata/citus.git
Merge branch 'main' into ihalatci-extension-compat-test-report
commit
cf9a4476e0
|
|
@ -58,6 +58,7 @@
|
||||||
|
|
||||||
#include "distributed/argutils.h"
|
#include "distributed/argutils.h"
|
||||||
#include "distributed/backend_data.h"
|
#include "distributed/backend_data.h"
|
||||||
|
#include "distributed/background_worker_utils.h"
|
||||||
#include "distributed/citus_ruleutils.h"
|
#include "distributed/citus_ruleutils.h"
|
||||||
#include "distributed/colocation_utils.h"
|
#include "distributed/colocation_utils.h"
|
||||||
#include "distributed/commands.h"
|
#include "distributed/commands.h"
|
||||||
|
|
@ -3156,37 +3157,26 @@ MetadataSyncSigAlrmHandler(SIGNAL_ARGS)
|
||||||
BackgroundWorkerHandle *
|
BackgroundWorkerHandle *
|
||||||
SpawnSyncNodeMetadataToNodes(Oid database, Oid extensionOwner)
|
SpawnSyncNodeMetadataToNodes(Oid database, Oid extensionOwner)
|
||||||
{
|
{
|
||||||
BackgroundWorker worker;
|
char workerName[BGW_MAXLEN];
|
||||||
BackgroundWorkerHandle *handle = NULL;
|
|
||||||
|
|
||||||
/* Configure a worker. */
|
SafeSnprintf(workerName, BGW_MAXLEN,
|
||||||
memset(&worker, 0, sizeof(worker));
|
|
||||||
SafeSnprintf(worker.bgw_name, BGW_MAXLEN,
|
|
||||||
"Citus Metadata Sync: %u/%u",
|
"Citus Metadata Sync: %u/%u",
|
||||||
database, extensionOwner);
|
database, extensionOwner);
|
||||||
worker.bgw_flags =
|
|
||||||
BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
|
|
||||||
worker.bgw_start_time = BgWorkerStart_ConsistentState;
|
|
||||||
|
|
||||||
/* don't restart, we manage restarts from maintenance daemon */
|
CitusBackgroundWorkerConfig config = {
|
||||||
worker.bgw_restart_time = BGW_NEVER_RESTART;
|
.workerName = workerName,
|
||||||
strcpy_s(worker.bgw_library_name, sizeof(worker.bgw_library_name), "citus");
|
.functionName = "SyncNodeMetadataToNodesMain",
|
||||||
strcpy_s(worker.bgw_function_name, sizeof(worker.bgw_library_name),
|
.mainArg = ObjectIdGetDatum(MyDatabaseId),
|
||||||
"SyncNodeMetadataToNodesMain");
|
.extensionOwner = extensionOwner,
|
||||||
worker.bgw_main_arg = ObjectIdGetDatum(MyDatabaseId);
|
.needsNotification = true,
|
||||||
memcpy_s(worker.bgw_extra, sizeof(worker.bgw_extra), &extensionOwner,
|
.waitForStartup = false,
|
||||||
sizeof(Oid));
|
.restartTime = CITUS_BGW_NEVER_RESTART,
|
||||||
worker.bgw_notify_pid = MyProcPid;
|
.startTime = CITUS_BGW_DEFAULT_START_TIME,
|
||||||
|
.workerType = NULL, /* use default */
|
||||||
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
|
.extraData = NULL,
|
||||||
{
|
.extraDataSize = 0
|
||||||
return NULL;
|
};
|
||||||
}
|
return RegisterCitusBackgroundWorker(&config);
|
||||||
|
|
||||||
pid_t pid;
|
|
||||||
WaitForBackgroundWorkerStartup(handle, &pid);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3130,10 +3130,10 @@ ScheduleBackgroundTask(int64 jobId, Oid owner, char *command, int dependingTaskC
|
||||||
|
|
||||||
/* 2. insert new task */
|
/* 2. insert new task */
|
||||||
{
|
{
|
||||||
Datum values[Natts_pg_dist_background_task] = { 0 };
|
TupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTask);
|
||||||
bool nulls[Natts_pg_dist_background_task] = { 0 };
|
|
||||||
|
|
||||||
memset(nulls, true, sizeof(nulls));
|
Datum *values = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));
|
||||||
|
bool *nulls = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
|
||||||
int64 taskId = GetNextBackgroundTaskTaskId();
|
int64 taskId = GetNextBackgroundTaskTaskId();
|
||||||
|
|
||||||
|
|
@ -3164,15 +3164,17 @@ ScheduleBackgroundTask(int64 jobId, Oid owner, char *command, int dependingTaskC
|
||||||
values[Anum_pg_dist_background_task_message - 1] = CStringGetTextDatum("");
|
values[Anum_pg_dist_background_task_message - 1] = CStringGetTextDatum("");
|
||||||
nulls[Anum_pg_dist_background_task_message - 1] = false;
|
nulls[Anum_pg_dist_background_task_message - 1] = false;
|
||||||
|
|
||||||
values[Anum_pg_dist_background_task_nodes_involved - 1] =
|
int nodesInvolvedIndex =
|
||||||
IntArrayToDatum(nodesInvolvedCount, nodesInvolved);
|
GetNodesInvolvedAttrIndexInPgDistBackgroundTask(tupleDescriptor);
|
||||||
nulls[Anum_pg_dist_background_task_nodes_involved - 1] =
|
values[nodesInvolvedIndex] = IntArrayToDatum(nodesInvolvedCount, nodesInvolved);
|
||||||
(nodesInvolvedCount == 0);
|
nulls[nodesInvolvedIndex] = (nodesInvolvedCount == 0);
|
||||||
|
|
||||||
HeapTuple newTuple = heap_form_tuple(RelationGetDescr(pgDistBackgroundTask),
|
HeapTuple newTuple = heap_form_tuple(tupleDescriptor, values, nulls);
|
||||||
values, nulls);
|
|
||||||
CatalogTupleInsert(pgDistBackgroundTask, newTuple);
|
CatalogTupleInsert(pgDistBackgroundTask, newTuple);
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(nulls);
|
||||||
|
|
||||||
task = palloc0(sizeof(BackgroundTask));
|
task = palloc0(sizeof(BackgroundTask));
|
||||||
task->taskid = taskId;
|
task->taskid = taskId;
|
||||||
task->status = BACKGROUND_TASK_STATUS_RUNNABLE;
|
task->status = BACKGROUND_TASK_STATUS_RUNNABLE;
|
||||||
|
|
@ -3285,11 +3287,12 @@ ResetRunningBackgroundTasks(void)
|
||||||
List *taskIdsToWait = NIL;
|
List *taskIdsToWait = NIL;
|
||||||
while (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))
|
while (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))
|
||||||
{
|
{
|
||||||
Datum values[Natts_pg_dist_background_task] = { 0 };
|
|
||||||
bool isnull[Natts_pg_dist_background_task] = { 0 };
|
|
||||||
bool replace[Natts_pg_dist_background_task] = { 0 };
|
|
||||||
|
|
||||||
TupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);
|
TupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);
|
||||||
|
|
||||||
|
Datum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));
|
||||||
|
bool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
bool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
|
||||||
heap_deform_tuple(taskTuple, tupleDescriptor, values, isnull);
|
heap_deform_tuple(taskTuple, tupleDescriptor, values, isnull);
|
||||||
|
|
||||||
values[Anum_pg_dist_background_task_status - 1] =
|
values[Anum_pg_dist_background_task_status - 1] =
|
||||||
|
|
@ -3358,6 +3361,10 @@ ResetRunningBackgroundTasks(void)
|
||||||
replace);
|
replace);
|
||||||
|
|
||||||
CatalogTupleUpdate(pgDistBackgroundTasks, &taskTuple->t_self, taskTuple);
|
CatalogTupleUpdate(pgDistBackgroundTasks, &taskTuple->t_self, taskTuple);
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(isnull);
|
||||||
|
pfree(replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list_length(taskIdsToWait) > 0)
|
if (list_length(taskIdsToWait) > 0)
|
||||||
|
|
@ -3441,8 +3448,9 @@ DeformBackgroundJobHeapTuple(TupleDesc tupleDescriptor, HeapTuple jobTuple)
|
||||||
static BackgroundTask *
|
static BackgroundTask *
|
||||||
DeformBackgroundTaskHeapTuple(TupleDesc tupleDescriptor, HeapTuple taskTuple)
|
DeformBackgroundTaskHeapTuple(TupleDesc tupleDescriptor, HeapTuple taskTuple)
|
||||||
{
|
{
|
||||||
Datum values[Natts_pg_dist_background_task] = { 0 };
|
Datum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));
|
||||||
bool nulls[Natts_pg_dist_background_task] = { 0 };
|
bool *nulls = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
|
||||||
heap_deform_tuple(taskTuple, tupleDescriptor, values, nulls);
|
heap_deform_tuple(taskTuple, tupleDescriptor, values, nulls);
|
||||||
|
|
||||||
BackgroundTask *task = palloc0(sizeof(BackgroundTask));
|
BackgroundTask *task = palloc0(sizeof(BackgroundTask));
|
||||||
|
|
@ -3480,13 +3488,18 @@ DeformBackgroundTaskHeapTuple(TupleDesc tupleDescriptor, HeapTuple taskTuple)
|
||||||
TextDatumGetCString(values[Anum_pg_dist_background_task_message - 1]);
|
TextDatumGetCString(values[Anum_pg_dist_background_task_message - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nulls[Anum_pg_dist_background_task_nodes_involved - 1])
|
int nodesInvolvedIndex =
|
||||||
|
GetNodesInvolvedAttrIndexInPgDistBackgroundTask(tupleDescriptor);
|
||||||
|
if (!nulls[nodesInvolvedIndex])
|
||||||
{
|
{
|
||||||
ArrayType *nodesInvolvedArrayObject =
|
ArrayType *nodesInvolvedArrayObject =
|
||||||
DatumGetArrayTypeP(values[Anum_pg_dist_background_task_nodes_involved - 1]);
|
DatumGetArrayTypeP(values[nodesInvolvedIndex]);
|
||||||
task->nodesInvolved = IntegerArrayTypeToList(nodesInvolvedArrayObject);
|
task->nodesInvolved = IntegerArrayTypeToList(nodesInvolvedArrayObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(nulls);
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3751,8 +3764,8 @@ JobTasksStatusCount(int64 jobId)
|
||||||
HeapTuple heapTuple = NULL;
|
HeapTuple heapTuple = NULL;
|
||||||
while (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))
|
while (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))
|
||||||
{
|
{
|
||||||
Datum values[Natts_pg_dist_background_task] = { 0 };
|
Datum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));
|
||||||
bool isnull[Natts_pg_dist_background_task] = { 0 };
|
bool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
|
||||||
heap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);
|
heap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);
|
||||||
|
|
||||||
|
|
@ -3760,6 +3773,9 @@ JobTasksStatusCount(int64 jobId)
|
||||||
1]);
|
1]);
|
||||||
BackgroundTaskStatus status = BackgroundTaskStatusByOid(statusOid);
|
BackgroundTaskStatus status = BackgroundTaskStatusByOid(statusOid);
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(isnull);
|
||||||
|
|
||||||
switch (status)
|
switch (status)
|
||||||
{
|
{
|
||||||
case BACKGROUND_TASK_STATUS_BLOCKED:
|
case BACKGROUND_TASK_STATUS_BLOCKED:
|
||||||
|
|
@ -4012,9 +4028,9 @@ UpdateBackgroundJob(int64 jobId)
|
||||||
UINT64_FORMAT, jobId)));
|
UINT64_FORMAT, jobId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum values[Natts_pg_dist_background_task] = { 0 };
|
Datum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));
|
||||||
bool isnull[Natts_pg_dist_background_task] = { 0 };
|
bool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));
|
||||||
bool replace[Natts_pg_dist_background_task] = { 0 };
|
bool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
|
||||||
heap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);
|
heap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);
|
||||||
|
|
||||||
|
|
@ -4058,6 +4074,10 @@ UpdateBackgroundJob(int64 jobId)
|
||||||
|
|
||||||
systable_endscan(scanDescriptor);
|
systable_endscan(scanDescriptor);
|
||||||
table_close(pgDistBackgroundJobs, NoLock);
|
table_close(pgDistBackgroundJobs, NoLock);
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(isnull);
|
||||||
|
pfree(replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4093,9 +4113,9 @@ UpdateBackgroundTask(BackgroundTask *task)
|
||||||
task->jobid, task->taskid)));
|
task->jobid, task->taskid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum values[Natts_pg_dist_background_task] = { 0 };
|
Datum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));
|
||||||
bool isnull[Natts_pg_dist_background_task] = { 0 };
|
bool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));
|
||||||
bool replace[Natts_pg_dist_background_task] = { 0 };
|
bool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
|
||||||
heap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);
|
heap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);
|
||||||
|
|
||||||
|
|
@ -4164,6 +4184,10 @@ UpdateBackgroundTask(BackgroundTask *task)
|
||||||
|
|
||||||
systable_endscan(scanDescriptor);
|
systable_endscan(scanDescriptor);
|
||||||
table_close(pgDistBackgroundTasks, NoLock);
|
table_close(pgDistBackgroundTasks, NoLock);
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(isnull);
|
||||||
|
pfree(replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4251,9 +4275,10 @@ CancelTasksForJob(int64 jobid)
|
||||||
HeapTuple taskTuple = NULL;
|
HeapTuple taskTuple = NULL;
|
||||||
while (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))
|
while (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))
|
||||||
{
|
{
|
||||||
Datum values[Natts_pg_dist_background_task] = { 0 };
|
Datum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));
|
||||||
bool nulls[Natts_pg_dist_background_task] = { 0 };
|
bool *nulls = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));
|
||||||
bool replace[Natts_pg_dist_background_task] = { 0 };
|
bool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
|
||||||
heap_deform_tuple(taskTuple, tupleDescriptor, values, nulls);
|
heap_deform_tuple(taskTuple, tupleDescriptor, values, nulls);
|
||||||
|
|
||||||
Oid statusOid =
|
Oid statusOid =
|
||||||
|
|
@ -4302,6 +4327,10 @@ CancelTasksForJob(int64 jobid)
|
||||||
taskTuple = heap_modify_tuple(taskTuple, tupleDescriptor, values, nulls,
|
taskTuple = heap_modify_tuple(taskTuple, tupleDescriptor, values, nulls,
|
||||||
replace);
|
replace);
|
||||||
CatalogTupleUpdate(pgDistBackgroundTasks, &taskTuple->t_self, taskTuple);
|
CatalogTupleUpdate(pgDistBackgroundTasks, &taskTuple->t_self, taskTuple);
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(nulls);
|
||||||
|
pfree(replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
systable_endscan(scanDescriptor);
|
systable_endscan(scanDescriptor);
|
||||||
|
|
@ -4358,9 +4387,9 @@ UnscheduleDependentTasks(BackgroundTask *task)
|
||||||
"task_id: " UINT64_FORMAT, cTaskId)));
|
"task_id: " UINT64_FORMAT, cTaskId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum values[Natts_pg_dist_background_task] = { 0 };
|
Datum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));
|
||||||
bool isnull[Natts_pg_dist_background_task] = { 0 };
|
bool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));
|
||||||
bool replace[Natts_pg_dist_background_task] = { 0 };
|
bool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));
|
||||||
|
|
||||||
values[Anum_pg_dist_background_task_status - 1] =
|
values[Anum_pg_dist_background_task_status - 1] =
|
||||||
ObjectIdGetDatum(CitusTaskStatusUnscheduledId());
|
ObjectIdGetDatum(CitusTaskStatusUnscheduledId());
|
||||||
|
|
@ -4372,6 +4401,10 @@ UnscheduleDependentTasks(BackgroundTask *task)
|
||||||
CatalogTupleUpdate(pgDistBackgroundTasks, &heapTuple->t_self, heapTuple);
|
CatalogTupleUpdate(pgDistBackgroundTasks, &heapTuple->t_self, heapTuple);
|
||||||
|
|
||||||
systable_endscan(scanDescriptor);
|
systable_endscan(scanDescriptor);
|
||||||
|
|
||||||
|
pfree(values);
|
||||||
|
pfree(isnull);
|
||||||
|
pfree(replace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4457,3 +4490,23 @@ GetAutoConvertedAttrIndexInPgDistPartition(TupleDesc tupleDesc)
|
||||||
? (Anum_pg_dist_partition_autoconverted - 1)
|
? (Anum_pg_dist_partition_autoconverted - 1)
|
||||||
: tupleDesc->natts - 1;
|
: tupleDesc->natts - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetNodesInvolvedAttrIndexInPgDistBackgroundTask returns attrnum for nodes_involved attr.
|
||||||
|
*
|
||||||
|
* nodes_involved attr was added to table pg_dist_background_task using alter operation after
|
||||||
|
* the version where Citus started supporting downgrades, and it's only column that we've
|
||||||
|
* introduced to pg_dist_background_task since then.
|
||||||
|
*
|
||||||
|
* And in case of a downgrade + upgrade, tupleDesc->natts becomes greater than
|
||||||
|
* Natts_pg_dist_background_task and when this happens, then we know that attrnum nodes_involved is
|
||||||
|
* not Anum_pg_dist_background_task_nodes_involved anymore but tupleDesc->natts - 1.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
GetNodesInvolvedAttrIndexInPgDistBackgroundTask(TupleDesc tupleDesc)
|
||||||
|
{
|
||||||
|
return TupleDescSize(tupleDesc) == Natts_pg_dist_background_task
|
||||||
|
? (Anum_pg_dist_background_task_nodes_involved - 1)
|
||||||
|
: tupleDesc->natts - 1;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4583,11 +4583,10 @@ FindReferencedTableColumn(Expr *columnExpression, List *parentQueryList, Query *
|
||||||
else if (rangeTableEntry->rtekind == RTE_CTE)
|
else if (rangeTableEntry->rtekind == RTE_CTE)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* When outerVars are considered, we modify parentQueryList, so this
|
* Resolve through a CTE even when skipOuterVars == false.
|
||||||
* logic might need to change when we support outervars in CTEs.
|
* Maintain the invariant that each recursion level owns a private,
|
||||||
|
* correctly-bounded copy of parentQueryList.
|
||||||
*/
|
*/
|
||||||
Assert(skipOuterVars);
|
|
||||||
|
|
||||||
int cteParentListIndex = list_length(parentQueryList) -
|
int cteParentListIndex = list_length(parentQueryList) -
|
||||||
rangeTableEntry->ctelevelsup - 1;
|
rangeTableEntry->ctelevelsup - 1;
|
||||||
Query *cteParentQuery = NULL;
|
Query *cteParentQuery = NULL;
|
||||||
|
|
@ -4618,16 +4617,36 @@ FindReferencedTableColumn(Expr *columnExpression, List *parentQueryList, Query *
|
||||||
if (cte != NULL)
|
if (cte != NULL)
|
||||||
{
|
{
|
||||||
Query *cteQuery = (Query *) cte->ctequery;
|
Query *cteQuery = (Query *) cte->ctequery;
|
||||||
List *targetEntryList = cteQuery->targetList;
|
|
||||||
AttrNumber targetEntryIndex = candidateColumn->varattno - 1;
|
AttrNumber targetEntryIndex = candidateColumn->varattno - 1;
|
||||||
TargetEntry *targetEntry = list_nth(targetEntryList, targetEntryIndex);
|
|
||||||
|
|
||||||
parentQueryList = lappend(parentQueryList, query);
|
if (targetEntryIndex >= 0 &&
|
||||||
FindReferencedTableColumn(targetEntry->expr, parentQueryList,
|
targetEntryIndex < list_length(cteQuery->targetList))
|
||||||
cteQuery, column, rteContainingReferencedColumn,
|
{
|
||||||
|
TargetEntry *targetEntry =
|
||||||
|
list_nth(cteQuery->targetList, targetEntryIndex);
|
||||||
|
|
||||||
|
/* Build a private, bounded parentQueryList before recursing into the CTE.
|
||||||
|
* Invariant: list is [top … current], owned by this call (no aliasing).
|
||||||
|
* For RTE_CTE:
|
||||||
|
* owner_idx = list_length(parentQueryList) - rangeTableEntry->ctelevelsup - 1;
|
||||||
|
* newParent = lappend(list_truncate(list_copy(parentQueryList), owner_idx + 1), query);
|
||||||
|
* Example (Q0 owns CTE; we’re in Q2 via nested subquery):
|
||||||
|
* parent=[Q0,Q1,Q2], ctelevelsup=2 ⇒ owner_idx=0 ⇒ newParent=[Q0,Q2].
|
||||||
|
* Keeps outer-Var level math correct without mutating the caller’s list.
|
||||||
|
*/
|
||||||
|
List *newParent = list_copy(parentQueryList);
|
||||||
|
newParent = list_truncate(newParent, cteParentListIndex + 1);
|
||||||
|
newParent = lappend(newParent, query);
|
||||||
|
|
||||||
|
FindReferencedTableColumn(targetEntry->expr,
|
||||||
|
newParent,
|
||||||
|
cteQuery,
|
||||||
|
column,
|
||||||
|
rteContainingReferencedColumn,
|
||||||
skipOuterVars);
|
skipOuterVars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
#include "storage/latch.h"
|
#include "storage/latch.h"
|
||||||
#include "utils/snapmgr.h"
|
#include "utils/snapmgr.h"
|
||||||
|
|
||||||
|
#include "distributed/background_worker_utils.h"
|
||||||
#include "distributed/citus_acquire_lock.h"
|
#include "distributed/citus_acquire_lock.h"
|
||||||
#include "distributed/citus_safe_lib.h"
|
#include "distributed/citus_safe_lib.h"
|
||||||
#include "distributed/connection_management.h"
|
#include "distributed/connection_management.h"
|
||||||
|
|
@ -65,34 +66,33 @@ static bool got_sigterm = false;
|
||||||
BackgroundWorkerHandle *
|
BackgroundWorkerHandle *
|
||||||
StartLockAcquireHelperBackgroundWorker(int backendToHelp, int32 lock_cooldown)
|
StartLockAcquireHelperBackgroundWorker(int backendToHelp, int32 lock_cooldown)
|
||||||
{
|
{
|
||||||
BackgroundWorkerHandle *handle = NULL;
|
|
||||||
LockAcquireHelperArgs args;
|
LockAcquireHelperArgs args;
|
||||||
BackgroundWorker worker;
|
|
||||||
memset(&args, 0, sizeof(args));
|
memset(&args, 0, sizeof(args));
|
||||||
memset(&worker, 0, sizeof(worker));
|
|
||||||
|
|
||||||
/* collect the extra arguments required for the background worker */
|
/* collect the extra arguments required for the background worker */
|
||||||
args.DatabaseId = MyDatabaseId;
|
args.DatabaseId = MyDatabaseId;
|
||||||
args.lock_cooldown = lock_cooldown;
|
args.lock_cooldown = lock_cooldown;
|
||||||
|
|
||||||
/* construct the background worker and start it */
|
char workerName[BGW_MAXLEN];
|
||||||
SafeSnprintf(worker.bgw_name, sizeof(worker.bgw_name),
|
SafeSnprintf(workerName, BGW_MAXLEN,
|
||||||
"Citus Lock Acquire Helper: %d/%u", backendToHelp, MyDatabaseId);
|
"Citus Lock Acquire Helper: %d/%u", backendToHelp, MyDatabaseId);
|
||||||
strcpy_s(worker.bgw_type, sizeof(worker.bgw_type), "citus_lock_aqcuire");
|
|
||||||
|
|
||||||
worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
|
CitusBackgroundWorkerConfig config = {
|
||||||
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
|
.workerName = workerName,
|
||||||
worker.bgw_restart_time = BGW_NEVER_RESTART;
|
.functionName = "LockAcquireHelperMain",
|
||||||
|
.mainArg = Int32GetDatum(backendToHelp),
|
||||||
|
.extensionOwner = InvalidOid,
|
||||||
|
.needsNotification = false,
|
||||||
|
.waitForStartup = false,
|
||||||
|
.restartTime = CITUS_BGW_NEVER_RESTART,
|
||||||
|
.startTime = BgWorkerStart_RecoveryFinished,
|
||||||
|
.workerType = "citus_lock_aqcuire",
|
||||||
|
.extraData = &args,
|
||||||
|
.extraDataSize = sizeof(args)
|
||||||
|
};
|
||||||
|
|
||||||
strcpy_s(worker.bgw_library_name, sizeof(worker.bgw_library_name), "citus");
|
BackgroundWorkerHandle *handle = RegisterCitusBackgroundWorker(&config);
|
||||||
strcpy_s(worker.bgw_function_name, sizeof(worker.bgw_function_name),
|
if (!handle)
|
||||||
"LockAcquireHelperMain");
|
|
||||||
worker.bgw_main_arg = Int32GetDatum(backendToHelp);
|
|
||||||
worker.bgw_notify_pid = 0;
|
|
||||||
|
|
||||||
memcpy_s(worker.bgw_extra, sizeof(worker.bgw_extra), &args, sizeof(args));
|
|
||||||
|
|
||||||
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
|
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
#include "utils/timeout.h"
|
#include "utils/timeout.h"
|
||||||
|
|
||||||
#include "distributed/background_jobs.h"
|
#include "distributed/background_jobs.h"
|
||||||
|
#include "distributed/background_worker_utils.h"
|
||||||
#include "distributed/citus_safe_lib.h"
|
#include "distributed/citus_safe_lib.h"
|
||||||
#include "distributed/hash_helpers.h"
|
#include "distributed/hash_helpers.h"
|
||||||
#include "distributed/listutils.h"
|
#include "distributed/listutils.h"
|
||||||
|
|
@ -417,37 +418,26 @@ citus_task_wait_internal(int64 taskid, BackgroundTaskStatus *desiredStatus)
|
||||||
BackgroundWorkerHandle *
|
BackgroundWorkerHandle *
|
||||||
StartCitusBackgroundTaskQueueMonitor(Oid database, Oid extensionOwner)
|
StartCitusBackgroundTaskQueueMonitor(Oid database, Oid extensionOwner)
|
||||||
{
|
{
|
||||||
BackgroundWorker worker = { 0 };
|
char workerName[BGW_MAXLEN];
|
||||||
BackgroundWorkerHandle *handle = NULL;
|
|
||||||
|
|
||||||
/* Configure a worker. */
|
SafeSnprintf(workerName, BGW_MAXLEN,
|
||||||
memset(&worker, 0, sizeof(worker));
|
|
||||||
SafeSnprintf(worker.bgw_name, BGW_MAXLEN,
|
|
||||||
"Citus Background Task Queue Monitor: %u/%u",
|
"Citus Background Task Queue Monitor: %u/%u",
|
||||||
database, extensionOwner);
|
database, extensionOwner);
|
||||||
worker.bgw_flags =
|
|
||||||
BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
|
|
||||||
worker.bgw_start_time = BgWorkerStart_ConsistentState;
|
|
||||||
|
|
||||||
/* don't restart, we manage restarts from maintenance daemon */
|
CitusBackgroundWorkerConfig config = {
|
||||||
worker.bgw_restart_time = BGW_NEVER_RESTART;
|
.workerName = workerName,
|
||||||
strcpy_s(worker.bgw_library_name, sizeof(worker.bgw_library_name), "citus");
|
.functionName = "CitusBackgroundTaskQueueMonitorMain",
|
||||||
strcpy_s(worker.bgw_function_name, sizeof(worker.bgw_library_name),
|
.mainArg = ObjectIdGetDatum(MyDatabaseId),
|
||||||
"CitusBackgroundTaskQueueMonitorMain");
|
.extensionOwner = extensionOwner,
|
||||||
worker.bgw_main_arg = ObjectIdGetDatum(MyDatabaseId);
|
.needsNotification = true,
|
||||||
memcpy_s(worker.bgw_extra, sizeof(worker.bgw_extra), &extensionOwner,
|
.waitForStartup = true,
|
||||||
sizeof(Oid));
|
.restartTime = CITUS_BGW_NEVER_RESTART,
|
||||||
worker.bgw_notify_pid = MyProcPid;
|
.startTime = CITUS_BGW_DEFAULT_START_TIME,
|
||||||
|
.workerType = NULL, /* use default */
|
||||||
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
|
.extraData = NULL,
|
||||||
{
|
.extraDataSize = 0
|
||||||
return NULL;
|
};
|
||||||
}
|
return RegisterCitusBackgroundWorker(&config);
|
||||||
|
|
||||||
pid_t pid;
|
|
||||||
WaitForBackgroundWorkerStartup(handle, &pid);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1661,33 +1651,31 @@ StartCitusBackgroundTaskExecutor(char *database, char *user, char *command,
|
||||||
{
|
{
|
||||||
dsm_segment *seg = StoreArgumentsInDSM(database, user, command, taskId, jobId);
|
dsm_segment *seg = StoreArgumentsInDSM(database, user, command, taskId, jobId);
|
||||||
|
|
||||||
/* Configure a worker. */
|
char workerName[BGW_MAXLEN];
|
||||||
BackgroundWorker worker = { 0 };
|
SafeSnprintf(workerName, BGW_MAXLEN,
|
||||||
memset(&worker, 0, sizeof(worker));
|
|
||||||
SafeSnprintf(worker.bgw_name, BGW_MAXLEN,
|
|
||||||
"Citus Background Task Queue Executor: %s/%s for (%ld/%ld)",
|
"Citus Background Task Queue Executor: %s/%s for (%ld/%ld)",
|
||||||
database, user, jobId, taskId);
|
database, user, jobId, taskId);
|
||||||
worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
|
|
||||||
worker.bgw_start_time = BgWorkerStart_ConsistentState;
|
|
||||||
|
|
||||||
/* don't restart, we manage restarts from maintenance daemon */
|
CitusBackgroundWorkerConfig config = {
|
||||||
worker.bgw_restart_time = BGW_NEVER_RESTART;
|
.workerName = workerName,
|
||||||
strcpy_s(worker.bgw_library_name, sizeof(worker.bgw_library_name), "citus");
|
.functionName = "CitusBackgroundTaskExecutor",
|
||||||
strcpy_s(worker.bgw_function_name, sizeof(worker.bgw_library_name),
|
.mainArg = UInt32GetDatum(dsm_segment_handle(seg)),
|
||||||
"CitusBackgroundTaskExecutor");
|
.extensionOwner = InvalidOid,
|
||||||
worker.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(seg));
|
.needsNotification = true,
|
||||||
worker.bgw_notify_pid = MyProcPid;
|
.waitForStartup = true,
|
||||||
|
.restartTime = CITUS_BGW_NEVER_RESTART,
|
||||||
BackgroundWorkerHandle *handle = NULL;
|
.startTime = CITUS_BGW_DEFAULT_START_TIME,
|
||||||
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
|
.workerType = NULL, /* use default */
|
||||||
|
.extraData = NULL,
|
||||||
|
.extraDataSize = 0
|
||||||
|
};
|
||||||
|
BackgroundWorkerHandle *handle = RegisterCitusBackgroundWorker(&config);
|
||||||
|
if (!handle)
|
||||||
{
|
{
|
||||||
dsm_detach(seg);
|
dsm_detach(seg);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t pid = { 0 };
|
|
||||||
WaitForBackgroundWorkerStartup(handle, &pid);
|
|
||||||
|
|
||||||
if (pSegment)
|
if (pSegment)
|
||||||
{
|
{
|
||||||
*pSegment = seg;
|
*pSegment = seg;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* background_worker_utils.c
|
||||||
|
* Common utilities for initializing PostgreSQL background workers
|
||||||
|
* used by Citus distributed infrastructure.
|
||||||
|
*
|
||||||
|
* Copyright (c) Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "miscadmin.h"
|
||||||
|
|
||||||
|
#include "postmaster/bgworker.h"
|
||||||
|
#include "storage/proc.h"
|
||||||
|
|
||||||
|
#include "distributed/background_worker_utils.h"
|
||||||
|
#include "distributed/citus_safe_lib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InitializeCitusBackgroundWorker initializes a BackgroundWorker struct
|
||||||
|
* with common Citus background worker settings.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
InitializeCitusBackgroundWorker(BackgroundWorker *worker,
|
||||||
|
const CitusBackgroundWorkerConfig *config)
|
||||||
|
{
|
||||||
|
Assert(worker != NULL);
|
||||||
|
Assert(config != NULL);
|
||||||
|
Assert(config->workerName != NULL);
|
||||||
|
Assert(config->functionName != NULL);
|
||||||
|
|
||||||
|
/* Initialize the worker structure */
|
||||||
|
memset(worker, 0, sizeof(BackgroundWorker));
|
||||||
|
|
||||||
|
/* Set worker name */
|
||||||
|
strcpy_s(worker->bgw_name, sizeof(worker->bgw_name), config->workerName);
|
||||||
|
|
||||||
|
/* Set worker type if provided */
|
||||||
|
if (config->workerType != NULL)
|
||||||
|
{
|
||||||
|
strcpy_s(worker->bgw_type, sizeof(worker->bgw_type), config->workerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set standard flags for Citus workers */
|
||||||
|
worker->bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
|
||||||
|
|
||||||
|
/* Set start time - use custom start time if provided, otherwise use default */
|
||||||
|
worker->bgw_start_time = (config->startTime != 0) ? config->startTime :
|
||||||
|
CITUS_BGW_DEFAULT_START_TIME;
|
||||||
|
|
||||||
|
/* Set restart behavior */
|
||||||
|
worker->bgw_restart_time = config->restartTime;
|
||||||
|
|
||||||
|
/* Set library and function names */
|
||||||
|
strcpy_s(worker->bgw_library_name, sizeof(worker->bgw_library_name), "citus");
|
||||||
|
strcpy_s(worker->bgw_function_name, sizeof(worker->bgw_function_name),
|
||||||
|
config->functionName);
|
||||||
|
|
||||||
|
/* Set main argument */
|
||||||
|
worker->bgw_main_arg = config->mainArg;
|
||||||
|
|
||||||
|
/* Set extension owner if provided */
|
||||||
|
if (OidIsValid(config->extensionOwner))
|
||||||
|
{
|
||||||
|
memcpy_s(worker->bgw_extra, sizeof(worker->bgw_extra),
|
||||||
|
&config->extensionOwner, sizeof(Oid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set additional extra data if provided */
|
||||||
|
if (config->extraData != NULL && config->extraDataSize > 0)
|
||||||
|
{
|
||||||
|
size_t remainingSpace = sizeof(worker->bgw_extra);
|
||||||
|
size_t usedSpace = OidIsValid(config->extensionOwner) ? sizeof(Oid) : 0;
|
||||||
|
|
||||||
|
if (usedSpace + config->extraDataSize <= remainingSpace)
|
||||||
|
{
|
||||||
|
memcpy_s(((char *) worker->bgw_extra) + usedSpace,
|
||||||
|
remainingSpace - usedSpace,
|
||||||
|
config->extraData,
|
||||||
|
config->extraDataSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set notification PID if needed */
|
||||||
|
if (config->needsNotification)
|
||||||
|
{
|
||||||
|
worker->bgw_notify_pid = MyProcPid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RegisterCitusBackgroundWorker creates and registers a Citus background worker
|
||||||
|
* with the specified configuration. Returns the worker handle on success,
|
||||||
|
* NULL on failure.
|
||||||
|
*/
|
||||||
|
BackgroundWorkerHandle *
|
||||||
|
RegisterCitusBackgroundWorker(const CitusBackgroundWorkerConfig *config)
|
||||||
|
{
|
||||||
|
BackgroundWorker worker;
|
||||||
|
BackgroundWorkerHandle *handle = NULL;
|
||||||
|
|
||||||
|
/* Initialize the worker structure */
|
||||||
|
InitializeCitusBackgroundWorker(&worker, config);
|
||||||
|
|
||||||
|
/* Register the background worker */
|
||||||
|
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for startup if requested */
|
||||||
|
if (config->waitForStartup && handle != NULL)
|
||||||
|
{
|
||||||
|
pid_t pid = 0;
|
||||||
|
WaitForBackgroundWorkerStartup(handle, &pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
#include "pg_version_constants.h"
|
#include "pg_version_constants.h"
|
||||||
|
|
||||||
#include "distributed/background_jobs.h"
|
#include "distributed/background_jobs.h"
|
||||||
|
#include "distributed/background_worker_utils.h"
|
||||||
#include "distributed/citus_safe_lib.h"
|
#include "distributed/citus_safe_lib.h"
|
||||||
#include "distributed/coordinator_protocol.h"
|
#include "distributed/coordinator_protocol.h"
|
||||||
#include "distributed/distributed_deadlock_detection.h"
|
#include "distributed/distributed_deadlock_detection.h"
|
||||||
|
|
@ -188,32 +189,21 @@ InitializeMaintenanceDaemonForMainDb(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CitusBackgroundWorkerConfig config = {
|
||||||
|
.workerName = "Citus Maintenance Daemon for Main DB",
|
||||||
|
.functionName = "CitusMaintenanceDaemonMain",
|
||||||
|
.mainArg = (Datum) 0,
|
||||||
|
.extensionOwner = InvalidOid,
|
||||||
|
.needsNotification = false,
|
||||||
|
.waitForStartup = false,
|
||||||
|
.restartTime = CITUS_BGW_DEFAULT_RESTART_TIME,
|
||||||
|
.startTime = CITUS_BGW_DEFAULT_START_TIME,
|
||||||
|
.workerType = NULL, /* use default */
|
||||||
|
.extraData = NULL,
|
||||||
|
.extraDataSize = 0
|
||||||
|
};
|
||||||
BackgroundWorker worker;
|
BackgroundWorker worker;
|
||||||
|
InitializeCitusBackgroundWorker(&worker, &config);
|
||||||
memset(&worker, 0, sizeof(worker));
|
|
||||||
|
|
||||||
|
|
||||||
strcpy_s(worker.bgw_name, sizeof(worker.bgw_name),
|
|
||||||
"Citus Maintenance Daemon for Main DB");
|
|
||||||
|
|
||||||
/* request ability to connect to target database */
|
|
||||||
worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* No point in getting started before able to run query, but we do
|
|
||||||
* want to get started on Hot-Standby.
|
|
||||||
*/
|
|
||||||
worker.bgw_start_time = BgWorkerStart_ConsistentState;
|
|
||||||
|
|
||||||
/* Restart after a bit after errors, but don't bog the system. */
|
|
||||||
worker.bgw_restart_time = 5;
|
|
||||||
strcpy_s(worker.bgw_library_name,
|
|
||||||
sizeof(worker.bgw_library_name), "citus");
|
|
||||||
strcpy_s(worker.bgw_function_name, sizeof(worker.bgw_library_name),
|
|
||||||
"CitusMaintenanceDaemonMain");
|
|
||||||
|
|
||||||
worker.bgw_main_arg = (Datum) 0;
|
|
||||||
|
|
||||||
RegisterBackgroundWorker(&worker);
|
RegisterBackgroundWorker(&worker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,37 +246,28 @@ InitializeMaintenanceDaemonBackend(void)
|
||||||
{
|
{
|
||||||
Assert(dbData->workerPid == 0);
|
Assert(dbData->workerPid == 0);
|
||||||
|
|
||||||
BackgroundWorker worker;
|
char workerName[BGW_MAXLEN];
|
||||||
BackgroundWorkerHandle *handle = NULL;
|
|
||||||
|
|
||||||
memset(&worker, 0, sizeof(worker));
|
SafeSnprintf(workerName, sizeof(workerName),
|
||||||
|
|
||||||
SafeSnprintf(worker.bgw_name, sizeof(worker.bgw_name),
|
|
||||||
"Citus Maintenance Daemon: %u/%u",
|
"Citus Maintenance Daemon: %u/%u",
|
||||||
MyDatabaseId, extensionOwner);
|
MyDatabaseId, extensionOwner);
|
||||||
|
|
||||||
/* request ability to connect to target database */
|
CitusBackgroundWorkerConfig config = {
|
||||||
worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
|
.workerName = workerName,
|
||||||
|
.functionName = "CitusMaintenanceDaemonMain",
|
||||||
|
.mainArg = ObjectIdGetDatum(MyDatabaseId),
|
||||||
|
.extensionOwner = extensionOwner,
|
||||||
|
.needsNotification = true,
|
||||||
|
.waitForStartup = true,
|
||||||
|
.restartTime = CITUS_BGW_DEFAULT_RESTART_TIME,
|
||||||
|
.startTime = CITUS_BGW_DEFAULT_START_TIME,
|
||||||
|
.workerType = NULL, /* use default */
|
||||||
|
.extraData = NULL,
|
||||||
|
.extraDataSize = 0
|
||||||
|
};
|
||||||
|
BackgroundWorkerHandle *handle = RegisterCitusBackgroundWorker(&config);
|
||||||
|
|
||||||
/*
|
if (!handle)
|
||||||
* No point in getting started before able to run query, but we do
|
|
||||||
* want to get started on Hot-Standby.
|
|
||||||
*/
|
|
||||||
worker.bgw_start_time = BgWorkerStart_ConsistentState;
|
|
||||||
|
|
||||||
/* Restart after a bit after errors, but don't bog the system. */
|
|
||||||
worker.bgw_restart_time = 5;
|
|
||||||
strcpy_s(worker.bgw_library_name,
|
|
||||||
sizeof(worker.bgw_library_name), "citus");
|
|
||||||
strcpy_s(worker.bgw_function_name, sizeof(worker.bgw_library_name),
|
|
||||||
"CitusMaintenanceDaemonMain");
|
|
||||||
|
|
||||||
worker.bgw_main_arg = ObjectIdGetDatum(MyDatabaseId);
|
|
||||||
memcpy_s(worker.bgw_extra, sizeof(worker.bgw_extra), &extensionOwner,
|
|
||||||
sizeof(Oid));
|
|
||||||
worker.bgw_notify_pid = MyProcPid;
|
|
||||||
|
|
||||||
if (!RegisterDynamicBackgroundWorker(&worker, &handle))
|
|
||||||
{
|
{
|
||||||
WarnMaintenanceDaemonNotStarted();
|
WarnMaintenanceDaemonNotStarted();
|
||||||
dbData->daemonStarted = false;
|
dbData->daemonStarted = false;
|
||||||
|
|
@ -301,9 +282,6 @@ InitializeMaintenanceDaemonBackend(void)
|
||||||
dbData->triggerNodeMetadataSync = false;
|
dbData->triggerNodeMetadataSync = false;
|
||||||
LWLockRelease(&MaintenanceDaemonControl->lock);
|
LWLockRelease(&MaintenanceDaemonControl->lock);
|
||||||
|
|
||||||
pid_t pid;
|
|
||||||
WaitForBackgroundWorkerStartup(handle, &pid);
|
|
||||||
|
|
||||||
pfree(handle);
|
pfree(handle);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* background_worker_utils.h
|
||||||
|
* Common utilities for initializing PostgreSQL background workers
|
||||||
|
* used by Citus distributed infrastructure.
|
||||||
|
*
|
||||||
|
* Copyright (c) Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BACKGROUND_WORKER_UTILS_H
|
||||||
|
#define BACKGROUND_WORKER_UTILS_H
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "postmaster/bgworker.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Background worker configuration parameters
|
||||||
|
*/
|
||||||
|
typedef struct CitusBackgroundWorkerConfig
|
||||||
|
{
|
||||||
|
/* Worker identification */
|
||||||
|
const char *workerName;
|
||||||
|
const char *functionName;
|
||||||
|
const char *workerType;
|
||||||
|
|
||||||
|
/* Worker parameters */
|
||||||
|
Datum mainArg;
|
||||||
|
Oid extensionOwner;
|
||||||
|
|
||||||
|
/* Worker behavior flags */
|
||||||
|
bool needsNotification;
|
||||||
|
bool waitForStartup;
|
||||||
|
int restartTime;
|
||||||
|
|
||||||
|
/* Worker timing */
|
||||||
|
BgWorkerStartTime startTime;
|
||||||
|
|
||||||
|
/* Optional extra data */
|
||||||
|
const void *extraData;
|
||||||
|
size_t extraDataSize;
|
||||||
|
} CitusBackgroundWorkerConfig;
|
||||||
|
|
||||||
|
/* Default configuration values */
|
||||||
|
#define CITUS_BGW_DEFAULT_RESTART_TIME 5
|
||||||
|
#define CITUS_BGW_NEVER_RESTART BGW_NEVER_RESTART
|
||||||
|
#define CITUS_BGW_DEFAULT_START_TIME BgWorkerStart_ConsistentState
|
||||||
|
|
||||||
|
/* Function declarations */
|
||||||
|
extern BackgroundWorkerHandle * RegisterCitusBackgroundWorker(const
|
||||||
|
CitusBackgroundWorkerConfig
|
||||||
|
*config);
|
||||||
|
|
||||||
|
extern void InitializeCitusBackgroundWorker(BackgroundWorker *worker,
|
||||||
|
const CitusBackgroundWorkerConfig *config);
|
||||||
|
|
||||||
|
#endif /* BACKGROUND_WORKER_UTILS_H */
|
||||||
|
|
@ -27,4 +27,6 @@
|
||||||
#define Anum_pg_dist_background_task_message 9
|
#define Anum_pg_dist_background_task_message 9
|
||||||
#define Anum_pg_dist_background_task_nodes_involved 10
|
#define Anum_pg_dist_background_task_nodes_involved 10
|
||||||
|
|
||||||
|
extern int GetNodesInvolvedAttrIndexInPgDistBackgroundTask(TupleDesc tupleDesc);
|
||||||
|
|
||||||
#endif /* CITUS_PG_DIST_BACKGROUND_TASK_H */
|
#endif /* CITUS_PG_DIST_BACKGROUND_TASK_H */
|
||||||
|
|
|
||||||
|
|
@ -328,3 +328,19 @@ s/\| CHECK ([a-zA-Z])(.*)/| CHECK \(\1\2\)/g
|
||||||
|
|
||||||
# pg18 change: strip trailing “.00” (or “.0…”) from actual rows counts
|
# pg18 change: strip trailing “.00” (or “.0…”) from actual rows counts
|
||||||
s/(actual rows=[0-9]+)\.[0-9]+/\1/g
|
s/(actual rows=[0-9]+)\.[0-9]+/\1/g
|
||||||
|
|
||||||
|
# pg18 “Disabled” change start
|
||||||
|
# ignore any “Disabled:” lines in test output
|
||||||
|
/^\s*Disabled:/d
|
||||||
|
|
||||||
|
# ignore any JSON-style Disabled field
|
||||||
|
/^\s*"Disabled":/d
|
||||||
|
|
||||||
|
# ignore XML <Disabled>true</Disabled> or <Disabled>false</Disabled>
|
||||||
|
/^\s*<Disabled>.*<\/Disabled>/d
|
||||||
|
# pg18 “Disabled” change end
|
||||||
|
|
||||||
|
# PG18 psql: headings changed from "List of relations" to per-type titles
|
||||||
|
s/^([ \t]*)List of tables$/\1List of relations/g
|
||||||
|
s/^([ \t]*)List of indexes$/\1List of relations/g
|
||||||
|
s/^([ \t]*)List of sequences$/\1List of relations/g
|
||||||
|
|
|
||||||
|
|
@ -3637,5 +3637,157 @@ SELECT id, val FROM version_dist_union ORDER BY id;
|
||||||
(6 rows)
|
(6 rows)
|
||||||
|
|
||||||
-- End of Issue #7784
|
-- End of Issue #7784
|
||||||
|
-- PR #8106 — CTE traversal works when following outer Vars
|
||||||
|
-- This script exercises three shapes:
|
||||||
|
-- T1) CTE referenced inside a correlated subquery (one level down)
|
||||||
|
-- T2) CTE referenced inside a nested subquery (two levels down)
|
||||||
|
-- T3) Subquery targetlist uses a scalar sublink into the outer CTE
|
||||||
|
CREATE SCHEMA pr8106_cte_outervar;
|
||||||
|
SET search_path = pr8106_cte_outervar, public;
|
||||||
|
-- Base tables for the tests
|
||||||
|
DROP TABLE IF EXISTS raw_events_first CASCADE;
|
||||||
|
NOTICE: table "raw_events_first" does not exist, skipping
|
||||||
|
DROP TABLE IF EXISTS agg_events CASCADE;
|
||||||
|
NOTICE: table "agg_events" does not exist, skipping
|
||||||
|
CREATE TABLE raw_events_first(
|
||||||
|
user_id int,
|
||||||
|
value_1 int
|
||||||
|
);
|
||||||
|
CREATE TABLE agg_events(
|
||||||
|
user_id int,
|
||||||
|
value_1_agg int
|
||||||
|
);
|
||||||
|
-- Distribute and colocate (distribution key = user_id)
|
||||||
|
SELECT create_distributed_table('raw_events_first', 'user_id');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_distributed_table('agg_events', 'user_id');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Seed data (duplicates on some user_ids; some NULL value_1’s)
|
||||||
|
INSERT INTO raw_events_first(user_id, value_1) VALUES
|
||||||
|
(1, 10), (1, 20), (1, NULL),
|
||||||
|
(2, NULL),
|
||||||
|
(3, 30),
|
||||||
|
(4, NULL),
|
||||||
|
(5, 50), (5, NULL),
|
||||||
|
(6, NULL);
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
-- T1) CTE referenced inside a correlated subquery (one level down)
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
TRUNCATE agg_events;
|
||||||
|
WITH c AS MATERIALIZED (
|
||||||
|
SELECT user_id FROM raw_events_first
|
||||||
|
)
|
||||||
|
INSERT INTO agg_events (user_id)
|
||||||
|
SELECT t.user_id
|
||||||
|
FROM raw_events_first t
|
||||||
|
WHERE EXISTS (SELECT 1 FROM c WHERE c.user_id = t.user_id);
|
||||||
|
-- Expect one insert per row in raw_events_first (EXISTS always true per user_id)
|
||||||
|
SELECT 't1_count_matches' AS test,
|
||||||
|
(SELECT count(*) FROM agg_events) =
|
||||||
|
(SELECT count(*) FROM raw_events_first) AS ok;
|
||||||
|
test | ok
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t1_count_matches | t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Spot-check: how many rows were inserted
|
||||||
|
SELECT 't1_rows' AS test, count(*) AS rows FROM agg_events;
|
||||||
|
test | rows
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t1_rows | 9
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
-- T2) CTE referenced inside a nested subquery (two levels down)
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
TRUNCATE agg_events;
|
||||||
|
WITH c AS MATERIALIZED (
|
||||||
|
SELECT user_id FROM raw_events_first
|
||||||
|
)
|
||||||
|
INSERT INTO agg_events (user_id)
|
||||||
|
SELECT t.user_id
|
||||||
|
FROM raw_events_first t
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM (SELECT user_id FROM c) c2
|
||||||
|
WHERE c2.user_id = t.user_id
|
||||||
|
);
|
||||||
|
-- Same cardinality expectation as T1
|
||||||
|
SELECT 't2_count_matches' AS test,
|
||||||
|
(SELECT count(*) FROM agg_events) =
|
||||||
|
(SELECT count(*) FROM raw_events_first) AS ok;
|
||||||
|
test | ok
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t2_count_matches | t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT 't2_rows' AS test, count(*) AS rows FROM agg_events;
|
||||||
|
test | rows
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t2_rows | 9
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
-- T3) Subquery targetlist uses a scalar sublink into the outer CTE
|
||||||
|
-- (use MAX() to keep scalar subquery single-row)
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
TRUNCATE agg_events;
|
||||||
|
WITH c AS (SELECT user_id, value_1 FROM raw_events_first)
|
||||||
|
INSERT INTO agg_events (user_id, value_1_agg)
|
||||||
|
SELECT d.user_id, d.value_1_agg
|
||||||
|
FROM (
|
||||||
|
SELECT t.user_id,
|
||||||
|
(SELECT max(c.value_1) FROM c WHERE c.user_id = t.user_id) AS value_1_agg
|
||||||
|
FROM raw_events_first t
|
||||||
|
) AS d
|
||||||
|
WHERE d.value_1_agg IS NOT NULL;
|
||||||
|
-- Expect one insert per row in raw_events_first whose user_id has at least one non-NULL value_1
|
||||||
|
SELECT 't3_count_matches' AS test,
|
||||||
|
(SELECT count(*) FROM agg_events) =
|
||||||
|
(
|
||||||
|
SELECT count(*)
|
||||||
|
FROM raw_events_first t
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1 FROM raw_events_first c
|
||||||
|
WHERE c.user_id = t.user_id AND c.value_1 IS NOT NULL
|
||||||
|
)
|
||||||
|
) AS ok;
|
||||||
|
test | ok
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t3_count_matches | t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Also verify no NULLs were inserted into value_1_agg
|
||||||
|
SELECT 't3_no_null_value_1_agg' AS test,
|
||||||
|
NOT EXISTS (SELECT 1 FROM agg_events WHERE value_1_agg IS NULL) AS ok;
|
||||||
|
test | ok
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t3_no_null_value_1_agg | t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Deterministic sample of results
|
||||||
|
SELECT 't3_sample' AS test, user_id, value_1_agg
|
||||||
|
FROM agg_events
|
||||||
|
ORDER BY user_id
|
||||||
|
LIMIT 5;
|
||||||
|
test | user_id | value_1_agg
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t3_sample | 1 | 20
|
||||||
|
t3_sample | 1 | 20
|
||||||
|
t3_sample | 1 | 20
|
||||||
|
t3_sample | 3 | 30
|
||||||
|
t3_sample | 5 | 50
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
-- End of PR #8106 — CTE traversal works when following outer Vars
|
||||||
SET client_min_messages TO ERROR;
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA pr8106_cte_outervar CASCADE;
|
||||||
DROP SCHEMA multi_insert_select CASCADE;
|
DROP SCHEMA multi_insert_select CASCADE;
|
||||||
|
|
|
||||||
|
|
@ -1262,15 +1262,17 @@ SET search_path TO pg17;
|
||||||
ALTER TABLE distributed_partitioned_table DROP CONSTRAINT dist_exclude_named;
|
ALTER TABLE distributed_partitioned_table DROP CONSTRAINT dist_exclude_named;
|
||||||
ALTER TABLE local_partitioned_table DROP CONSTRAINT local_exclude_named;
|
ALTER TABLE local_partitioned_table DROP CONSTRAINT local_exclude_named;
|
||||||
-- Step 10: Verify the constraints were dropped
|
-- Step 10: Verify the constraints were dropped
|
||||||
SELECT * FROM pg_constraint WHERE conname = 'dist_exclude_named' AND contype = 'x';
|
SELECT COUNT(*) FROM pg_constraint WHERE conname = 'dist_exclude_named' AND contype = 'x';
|
||||||
oid | conname | connamespace | contype | condeferrable | condeferred | convalidated | conrelid | contypid | conindid | conparentid | confrelid | confupdtype | confdeltype | confmatchtype | conislocal | coninhcount | connoinherit | conkey | confkey | conpfeqop | conppeqop | conffeqop | confdelsetcols | conexclop | conbin
|
count
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
(0 rows)
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT * FROM pg_constraint WHERE conname = 'local_exclude_named' AND contype = 'x';
|
SELECT COUNT(*) FROM pg_constraint WHERE conname = 'local_exclude_named' AND contype = 'x';
|
||||||
oid | conname | connamespace | contype | condeferrable | condeferred | convalidated | conrelid | contypid | conindid | conparentid | confrelid | confupdtype | confdeltype | confmatchtype | conislocal | coninhcount | connoinherit | conkey | confkey | conpfeqop | conppeqop | conffeqop | confdelsetcols | conexclop | conbin
|
count
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
(0 rows)
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
-- Step 11: Clean up - Drop the tables
|
-- Step 11: Clean up - Drop the tables
|
||||||
DROP TABLE distributed_partitioned_table CASCADE;
|
DROP TABLE distributed_partitioned_table CASCADE;
|
||||||
|
|
|
||||||
|
|
@ -2583,5 +2583,127 @@ SELECT id, val FROM version_dist_union ORDER BY id;
|
||||||
|
|
||||||
-- End of Issue #7784
|
-- End of Issue #7784
|
||||||
|
|
||||||
|
-- PR #8106 — CTE traversal works when following outer Vars
|
||||||
|
-- This script exercises three shapes:
|
||||||
|
-- T1) CTE referenced inside a correlated subquery (one level down)
|
||||||
|
-- T2) CTE referenced inside a nested subquery (two levels down)
|
||||||
|
-- T3) Subquery targetlist uses a scalar sublink into the outer CTE
|
||||||
|
|
||||||
|
CREATE SCHEMA pr8106_cte_outervar;
|
||||||
|
SET search_path = pr8106_cte_outervar, public;
|
||||||
|
|
||||||
|
-- Base tables for the tests
|
||||||
|
DROP TABLE IF EXISTS raw_events_first CASCADE;
|
||||||
|
DROP TABLE IF EXISTS agg_events CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE raw_events_first(
|
||||||
|
user_id int,
|
||||||
|
value_1 int
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE agg_events(
|
||||||
|
user_id int,
|
||||||
|
value_1_agg int
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Distribute and colocate (distribution key = user_id)
|
||||||
|
SELECT create_distributed_table('raw_events_first', 'user_id');
|
||||||
|
SELECT create_distributed_table('agg_events', 'user_id');
|
||||||
|
|
||||||
|
-- Seed data (duplicates on some user_ids; some NULL value_1’s)
|
||||||
|
INSERT INTO raw_events_first(user_id, value_1) VALUES
|
||||||
|
(1, 10), (1, 20), (1, NULL),
|
||||||
|
(2, NULL),
|
||||||
|
(3, 30),
|
||||||
|
(4, NULL),
|
||||||
|
(5, 50), (5, NULL),
|
||||||
|
(6, NULL);
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- T1) CTE referenced inside a correlated subquery (one level down)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
TRUNCATE agg_events;
|
||||||
|
|
||||||
|
WITH c AS MATERIALIZED (
|
||||||
|
SELECT user_id FROM raw_events_first
|
||||||
|
)
|
||||||
|
INSERT INTO agg_events (user_id)
|
||||||
|
SELECT t.user_id
|
||||||
|
FROM raw_events_first t
|
||||||
|
WHERE EXISTS (SELECT 1 FROM c WHERE c.user_id = t.user_id);
|
||||||
|
|
||||||
|
-- Expect one insert per row in raw_events_first (EXISTS always true per user_id)
|
||||||
|
SELECT 't1_count_matches' AS test,
|
||||||
|
(SELECT count(*) FROM agg_events) =
|
||||||
|
(SELECT count(*) FROM raw_events_first) AS ok;
|
||||||
|
|
||||||
|
-- Spot-check: how many rows were inserted
|
||||||
|
SELECT 't1_rows' AS test, count(*) AS rows FROM agg_events;
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- T2) CTE referenced inside a nested subquery (two levels down)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
TRUNCATE agg_events;
|
||||||
|
|
||||||
|
WITH c AS MATERIALIZED (
|
||||||
|
SELECT user_id FROM raw_events_first
|
||||||
|
)
|
||||||
|
INSERT INTO agg_events (user_id)
|
||||||
|
SELECT t.user_id
|
||||||
|
FROM raw_events_first t
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM (SELECT user_id FROM c) c2
|
||||||
|
WHERE c2.user_id = t.user_id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Same cardinality expectation as T1
|
||||||
|
SELECT 't2_count_matches' AS test,
|
||||||
|
(SELECT count(*) FROM agg_events) =
|
||||||
|
(SELECT count(*) FROM raw_events_first) AS ok;
|
||||||
|
|
||||||
|
SELECT 't2_rows' AS test, count(*) AS rows FROM agg_events;
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- T3) Subquery targetlist uses a scalar sublink into the outer CTE
|
||||||
|
-- (use MAX() to keep scalar subquery single-row)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
TRUNCATE agg_events;
|
||||||
|
|
||||||
|
WITH c AS (SELECT user_id, value_1 FROM raw_events_first)
|
||||||
|
INSERT INTO agg_events (user_id, value_1_agg)
|
||||||
|
SELECT d.user_id, d.value_1_agg
|
||||||
|
FROM (
|
||||||
|
SELECT t.user_id,
|
||||||
|
(SELECT max(c.value_1) FROM c WHERE c.user_id = t.user_id) AS value_1_agg
|
||||||
|
FROM raw_events_first t
|
||||||
|
) AS d
|
||||||
|
WHERE d.value_1_agg IS NOT NULL;
|
||||||
|
|
||||||
|
-- Expect one insert per row in raw_events_first whose user_id has at least one non-NULL value_1
|
||||||
|
SELECT 't3_count_matches' AS test,
|
||||||
|
(SELECT count(*) FROM agg_events) =
|
||||||
|
(
|
||||||
|
SELECT count(*)
|
||||||
|
FROM raw_events_first t
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1 FROM raw_events_first c
|
||||||
|
WHERE c.user_id = t.user_id AND c.value_1 IS NOT NULL
|
||||||
|
)
|
||||||
|
) AS ok;
|
||||||
|
|
||||||
|
-- Also verify no NULLs were inserted into value_1_agg
|
||||||
|
SELECT 't3_no_null_value_1_agg' AS test,
|
||||||
|
NOT EXISTS (SELECT 1 FROM agg_events WHERE value_1_agg IS NULL) AS ok;
|
||||||
|
|
||||||
|
-- Deterministic sample of results
|
||||||
|
SELECT 't3_sample' AS test, user_id, value_1_agg
|
||||||
|
FROM agg_events
|
||||||
|
ORDER BY user_id
|
||||||
|
LIMIT 5;
|
||||||
|
|
||||||
|
-- End of PR #8106 — CTE traversal works when following outer Vars
|
||||||
|
|
||||||
SET client_min_messages TO ERROR;
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA pr8106_cte_outervar CASCADE;
|
||||||
DROP SCHEMA multi_insert_select CASCADE;
|
DROP SCHEMA multi_insert_select CASCADE;
|
||||||
|
|
|
||||||
|
|
@ -669,8 +669,8 @@ ALTER TABLE distributed_partitioned_table DROP CONSTRAINT dist_exclude_named;
|
||||||
ALTER TABLE local_partitioned_table DROP CONSTRAINT local_exclude_named;
|
ALTER TABLE local_partitioned_table DROP CONSTRAINT local_exclude_named;
|
||||||
|
|
||||||
-- Step 10: Verify the constraints were dropped
|
-- Step 10: Verify the constraints were dropped
|
||||||
SELECT * FROM pg_constraint WHERE conname = 'dist_exclude_named' AND contype = 'x';
|
SELECT COUNT(*) FROM pg_constraint WHERE conname = 'dist_exclude_named' AND contype = 'x';
|
||||||
SELECT * FROM pg_constraint WHERE conname = 'local_exclude_named' AND contype = 'x';
|
SELECT COUNT(*) FROM pg_constraint WHERE conname = 'local_exclude_named' AND contype = 'x';
|
||||||
|
|
||||||
-- Step 11: Clean up - Drop the tables
|
-- Step 11: Clean up - Drop the tables
|
||||||
DROP TABLE distributed_partitioned_table CASCADE;
|
DROP TABLE distributed_partitioned_table CASCADE;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue