mirror of https://github.com/citusdata/citus.git
Merge branch 'main' into alter_database_nonmain2
commit
e089f906b1
|
@ -22,6 +22,7 @@
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/pg_am.h"
|
#include "catalog/pg_am.h"
|
||||||
|
#include "catalog/pg_attrdef.h"
|
||||||
#include "catalog/pg_attribute.h"
|
#include "catalog/pg_attribute.h"
|
||||||
#include "catalog/pg_enum.h"
|
#include "catalog/pg_enum.h"
|
||||||
#include "catalog/pg_extension.h"
|
#include "catalog/pg_extension.h"
|
||||||
|
@ -50,6 +51,7 @@
|
||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/inval.h"
|
#include "utils/inval.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
@ -1696,54 +1698,41 @@ PropagatePrerequisiteObjectsForDistributedTable(Oid relationId)
|
||||||
void
|
void
|
||||||
EnsureSequenceTypeSupported(Oid seqOid, Oid attributeTypeId, Oid ownerRelationId)
|
EnsureSequenceTypeSupported(Oid seqOid, Oid attributeTypeId, Oid ownerRelationId)
|
||||||
{
|
{
|
||||||
List *citusTableIdList = CitusTableTypeIdList(ANY_CITUS_TABLE_TYPE);
|
Oid attrDefOid;
|
||||||
citusTableIdList = list_append_unique_oid(citusTableIdList, ownerRelationId);
|
List *attrDefOids = GetAttrDefsFromSequence(seqOid);
|
||||||
|
|
||||||
Oid citusTableId = InvalidOid;
|
foreach_oid(attrDefOid, attrDefOids)
|
||||||
foreach_oid(citusTableId, citusTableIdList)
|
|
||||||
{
|
{
|
||||||
List *seqInfoList = NIL;
|
ObjectAddress columnAddress = GetAttrDefaultColumnAddress(attrDefOid);
|
||||||
GetDependentSequencesWithRelation(citusTableId, &seqInfoList, 0, DEPENDENCY_AUTO);
|
|
||||||
|
|
||||||
SequenceInfo *seqInfo = NULL;
|
|
||||||
foreach_ptr(seqInfo, seqInfoList)
|
|
||||||
{
|
|
||||||
AttrNumber currentAttnum = seqInfo->attributeNumber;
|
|
||||||
Oid currentSeqOid = seqInfo->sequenceOid;
|
|
||||||
|
|
||||||
if (!seqInfo->isNextValDefault)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If a sequence is not on the nextval, we don't need any check.
|
|
||||||
* This is a dependent sequence via ALTER SEQUENCE .. OWNED BY col
|
|
||||||
*/
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If another distributed table is using the same sequence
|
* If another distributed table is using the same sequence
|
||||||
* in one of its column defaults, make sure the types of the
|
* in one of its column defaults, make sure the types of the
|
||||||
* columns match
|
* columns match.
|
||||||
|
*
|
||||||
|
* We skip non-distributed tables, but we need to check the current
|
||||||
|
* table as it might reference the same sequence multiple times.
|
||||||
*/
|
*/
|
||||||
if (currentSeqOid == seqOid)
|
if (columnAddress.objectId != ownerRelationId &&
|
||||||
|
!IsCitusTable(columnAddress.objectId))
|
||||||
{
|
{
|
||||||
Oid currentAttributeTypId = GetAttributeTypeOid(citusTableId,
|
continue;
|
||||||
currentAttnum);
|
}
|
||||||
|
Oid currentAttributeTypId = GetAttributeTypeOid(columnAddress.objectId,
|
||||||
|
columnAddress.objectSubId);
|
||||||
if (attributeTypeId != currentAttributeTypId)
|
if (attributeTypeId != currentAttributeTypId)
|
||||||
{
|
{
|
||||||
char *sequenceName = generate_qualified_relation_name(
|
char *sequenceName = generate_qualified_relation_name(
|
||||||
seqOid);
|
seqOid);
|
||||||
char *citusTableName =
|
char *citusTableName =
|
||||||
generate_qualified_relation_name(citusTableId);
|
generate_qualified_relation_name(columnAddress.objectId);
|
||||||
ereport(ERROR, (errmsg(
|
ereport(ERROR, (errmsg(
|
||||||
"The sequence %s is already used for a different"
|
"The sequence %s is already used for a different"
|
||||||
" type in column %d of the table %s",
|
" type in column %d of the table %s",
|
||||||
sequenceName, currentAttnum,
|
sequenceName, columnAddress.objectSubId,
|
||||||
citusTableName)));
|
citusTableName)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1093,33 +1093,26 @@ List *
|
||||||
GetDependentFDWsToExtension(Oid extensionId)
|
GetDependentFDWsToExtension(Oid extensionId)
|
||||||
{
|
{
|
||||||
List *extensionFDWs = NIL;
|
List *extensionFDWs = NIL;
|
||||||
ScanKeyData key[3];
|
ScanKeyData key[1];
|
||||||
int scanKeyCount = 3;
|
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
|
|
||||||
Relation pgDepend = table_open(DependRelationId, AccessShareLock);
|
Relation pgDepend = table_open(DependRelationId, AccessShareLock);
|
||||||
|
|
||||||
ScanKeyInit(&key[0],
|
ScanKeyInit(&key[0],
|
||||||
Anum_pg_depend_refclassid,
|
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
|
||||||
ObjectIdGetDatum(ExtensionRelationId));
|
|
||||||
ScanKeyInit(&key[1],
|
|
||||||
Anum_pg_depend_refobjid,
|
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
|
||||||
ObjectIdGetDatum(extensionId));
|
|
||||||
ScanKeyInit(&key[2],
|
|
||||||
Anum_pg_depend_classid,
|
Anum_pg_depend_classid,
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
ObjectIdGetDatum(ForeignDataWrapperRelationId));
|
ObjectIdGetDatum(ForeignDataWrapperRelationId));
|
||||||
|
|
||||||
SysScanDesc scan = systable_beginscan(pgDepend, InvalidOid, false,
|
SysScanDesc scan = systable_beginscan(pgDepend, DependDependerIndexId, true,
|
||||||
NULL, scanKeyCount, key);
|
NULL, lengthof(key), key);
|
||||||
|
|
||||||
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
||||||
{
|
{
|
||||||
Form_pg_depend pgDependEntry = (Form_pg_depend) GETSTRUCT(tup);
|
Form_pg_depend pgDependEntry = (Form_pg_depend) GETSTRUCT(tup);
|
||||||
|
|
||||||
if (pgDependEntry->deptype == DEPENDENCY_EXTENSION)
|
if (pgDependEntry->deptype == DEPENDENCY_EXTENSION &&
|
||||||
|
pgDependEntry->refclassid == ExtensionRelationId &&
|
||||||
|
pgDependEntry->refobjid == extensionId)
|
||||||
{
|
{
|
||||||
extensionFDWs = lappend_oid(extensionFDWs, pgDependEntry->objid);
|
extensionFDWs = lappend_oid(extensionFDWs, pgDependEntry->objid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_constraint.h"
|
#include "catalog/pg_constraint.h"
|
||||||
|
#include "catalog/pg_depend.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
#include "distributed/commands.h"
|
#include "distributed/commands.h"
|
||||||
#include "distributed/commands/sequence.h"
|
#include "distributed/commands/sequence.h"
|
||||||
#include "distributed/coordinator_protocol.h"
|
#include "distributed/coordinator_protocol.h"
|
||||||
|
#include "distributed/hash_helpers.h"
|
||||||
#include "distributed/listutils.h"
|
#include "distributed/listutils.h"
|
||||||
#include "distributed/multi_join_order.h"
|
#include "distributed/multi_join_order.h"
|
||||||
#include "distributed/namespace_utils.h"
|
#include "distributed/namespace_utils.h"
|
||||||
|
@ -1199,64 +1201,16 @@ TableHasExternalForeignKeys(Oid relationId)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetForeignKeyOids takes in a relationId, and returns a list of OIDs for
|
* ForeignConstraintMatchesFlags is a function with logic that's very specific
|
||||||
* foreign constraints that the relation with relationId is involved according
|
* to GetForeignKeyOids. There's no reason to use it in any other context.
|
||||||
* to "flags" argument. See ExtractForeignKeyConstraintsMode enum definition
|
|
||||||
* for usage of the flags.
|
|
||||||
*/
|
*/
|
||||||
List *
|
static bool
|
||||||
GetForeignKeyOids(Oid relationId, int flags)
|
ForeignConstraintMatchesFlags(Form_pg_constraint constraintForm,
|
||||||
|
int flags)
|
||||||
{
|
{
|
||||||
AttrNumber pgConstraintTargetAttrNumber = InvalidAttrNumber;
|
|
||||||
|
|
||||||
bool extractReferencing = (flags & INCLUDE_REFERENCING_CONSTRAINTS);
|
|
||||||
bool extractReferenced = (flags & INCLUDE_REFERENCED_CONSTRAINTS);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only one of them should be passed at a time since the way we scan
|
|
||||||
* pg_constraint differs for those columns. Anum_pg_constraint_conrelid
|
|
||||||
* supports index scan while Anum_pg_constraint_confrelid does not.
|
|
||||||
*/
|
|
||||||
Assert(!(extractReferencing && extractReferenced));
|
|
||||||
Assert(extractReferencing || extractReferenced);
|
|
||||||
|
|
||||||
bool useIndex = false;
|
|
||||||
Oid indexOid = InvalidOid;
|
|
||||||
|
|
||||||
if (extractReferencing)
|
|
||||||
{
|
|
||||||
pgConstraintTargetAttrNumber = Anum_pg_constraint_conrelid;
|
|
||||||
|
|
||||||
useIndex = true;
|
|
||||||
indexOid = ConstraintRelidTypidNameIndexId;
|
|
||||||
}
|
|
||||||
else if (extractReferenced)
|
|
||||||
{
|
|
||||||
pgConstraintTargetAttrNumber = Anum_pg_constraint_confrelid;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool excludeSelfReference = (flags & EXCLUDE_SELF_REFERENCES);
|
|
||||||
|
|
||||||
List *foreignKeyOids = NIL;
|
|
||||||
|
|
||||||
ScanKeyData scanKey[1];
|
|
||||||
int scanKeyCount = 1;
|
|
||||||
|
|
||||||
Relation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);
|
|
||||||
ScanKeyInit(&scanKey[0], pgConstraintTargetAttrNumber,
|
|
||||||
BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));
|
|
||||||
SysScanDesc scanDescriptor = systable_beginscan(pgConstraint, indexOid, useIndex,
|
|
||||||
NULL, scanKeyCount, scanKey);
|
|
||||||
|
|
||||||
HeapTuple heapTuple = systable_getnext(scanDescriptor);
|
|
||||||
while (HeapTupleIsValid(heapTuple))
|
|
||||||
{
|
|
||||||
Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);
|
|
||||||
|
|
||||||
if (constraintForm->contype != CONSTRAINT_FOREIGN)
|
if (constraintForm->contype != CONSTRAINT_FOREIGN)
|
||||||
{
|
{
|
||||||
heapTuple = systable_getnext(scanDescriptor);
|
return false;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inheritedConstraint = OidIsValid(constraintForm->conparentid);
|
bool inheritedConstraint = OidIsValid(constraintForm->conparentid);
|
||||||
|
@ -1267,38 +1221,141 @@ GetForeignKeyOids(Oid relationId, int flags)
|
||||||
* the table as we already process the constraints from parent tables
|
* the table as we already process the constraints from parent tables
|
||||||
* implicitly when a command is issued
|
* implicitly when a command is issued
|
||||||
*/
|
*/
|
||||||
heapTuple = systable_getnext(scanDescriptor);
|
return false;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Oid constraintId = constraintForm->oid;
|
bool excludeSelfReference = (flags & EXCLUDE_SELF_REFERENCES);
|
||||||
|
|
||||||
bool isSelfReference = (constraintForm->conrelid == constraintForm->confrelid);
|
bool isSelfReference = (constraintForm->conrelid == constraintForm->confrelid);
|
||||||
if (excludeSelfReference && isSelfReference)
|
if (excludeSelfReference && isSelfReference)
|
||||||
{
|
{
|
||||||
heapTuple = systable_getnext(scanDescriptor);
|
return false;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Oid otherTableId = InvalidOid;
|
Oid otherTableId = InvalidOid;
|
||||||
if (extractReferencing)
|
if (flags & INCLUDE_REFERENCING_CONSTRAINTS)
|
||||||
{
|
{
|
||||||
otherTableId = constraintForm->confrelid;
|
otherTableId = constraintForm->confrelid;
|
||||||
}
|
}
|
||||||
else if (extractReferenced)
|
else
|
||||||
{
|
{
|
||||||
otherTableId = constraintForm->conrelid;
|
otherTableId = constraintForm->conrelid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsTableTypeIncluded(otherTableId, flags))
|
return IsTableTypeIncluded(otherTableId, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetForeignKeyOidsForReferencedTable returns a list of foreign key OIDs that
|
||||||
|
* reference the relationId and match the given flags.
|
||||||
|
*
|
||||||
|
* This is separated from GetForeignKeyOids because we need to scan pg_depend
|
||||||
|
* instead of pg_constraint directly. The reason for this is that there is no
|
||||||
|
* index on the confrelid of pg_constraint, so searching by that column
|
||||||
|
* requires a seqscan.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
GetForeignKeyOidsForReferencedTable(Oid relationId, int flags)
|
||||||
|
{
|
||||||
|
HTAB *foreignKeyOidsSet = CreateSimpleHashSetWithName(
|
||||||
|
Oid, "ReferencingForeignKeyOidsSet");
|
||||||
|
List *foreignKeyOidsList = NIL;
|
||||||
|
ScanKeyData key[2];
|
||||||
|
HeapTuple dependTup;
|
||||||
|
Relation depRel = table_open(DependRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&key[0],
|
||||||
|
Anum_pg_depend_refclassid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(RelationRelationId));
|
||||||
|
ScanKeyInit(&key[1],
|
||||||
|
Anum_pg_depend_refobjid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(relationId));
|
||||||
|
SysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,
|
||||||
|
NULL, lengthof(key), key);
|
||||||
|
while (HeapTupleIsValid(dependTup = systable_getnext(scan)))
|
||||||
|
{
|
||||||
|
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(dependTup);
|
||||||
|
|
||||||
|
if (deprec->classid != ConstraintRelationId ||
|
||||||
|
deprec->deptype != DEPENDENCY_NORMAL ||
|
||||||
|
hash_search(foreignKeyOidsSet, &deprec->objid, HASH_FIND, NULL))
|
||||||
{
|
{
|
||||||
heapTuple = systable_getnext(scanDescriptor);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreignKeyOids = lappend_oid(foreignKeyOids, constraintId);
|
|
||||||
|
|
||||||
heapTuple = systable_getnext(scanDescriptor);
|
HeapTuple constraintTup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(
|
||||||
|
deprec->objid));
|
||||||
|
if (!HeapTupleIsValid(constraintTup)) /* can happen during DROP TABLE */
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(constraintTup);
|
||||||
|
if (constraint->confrelid == relationId &&
|
||||||
|
ForeignConstraintMatchesFlags(constraint, flags))
|
||||||
|
{
|
||||||
|
foreignKeyOidsList = lappend_oid(foreignKeyOidsList, constraint->oid);
|
||||||
|
hash_search(foreignKeyOidsSet, &constraint->oid, HASH_ENTER, NULL);
|
||||||
|
}
|
||||||
|
ReleaseSysCache(constraintTup);
|
||||||
|
}
|
||||||
|
systable_endscan(scan);
|
||||||
|
table_close(depRel, AccessShareLock);
|
||||||
|
return foreignKeyOidsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetForeignKeyOids takes in a relationId, and returns a list of OIDs for
|
||||||
|
* foreign constraints that the relation with relationId is involved according
|
||||||
|
* to "flags" argument. See ExtractForeignKeyConstraintsMode enum definition
|
||||||
|
* for usage of the flags.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
GetForeignKeyOids(Oid relationId, int flags)
|
||||||
|
{
|
||||||
|
bool extractReferencing PG_USED_FOR_ASSERTS_ONLY = (flags &
|
||||||
|
INCLUDE_REFERENCING_CONSTRAINTS);
|
||||||
|
bool extractReferenced = (flags & INCLUDE_REFERENCED_CONSTRAINTS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only one of them should be passed at a time since the way we scan
|
||||||
|
* pg_constraint differs for those columns. Anum_pg_constraint_conrelid
|
||||||
|
* supports index scan while Anum_pg_constraint_confrelid does not.
|
||||||
|
*/
|
||||||
|
Assert(!(extractReferencing && extractReferenced));
|
||||||
|
Assert(extractReferencing || extractReferenced);
|
||||||
|
|
||||||
|
if (extractReferenced)
|
||||||
|
{
|
||||||
|
return GetForeignKeyOidsForReferencedTable(relationId, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
List *foreignKeyOids = NIL;
|
||||||
|
|
||||||
|
ScanKeyData scanKey[1];
|
||||||
|
int scanKeyCount = 1;
|
||||||
|
|
||||||
|
Relation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);
|
||||||
|
ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));
|
||||||
|
|
||||||
|
SysScanDesc scanDescriptor = systable_beginscan(pgConstraint,
|
||||||
|
ConstraintRelidTypidNameIndexId, true,
|
||||||
|
NULL, scanKeyCount, scanKey);
|
||||||
|
|
||||||
|
HeapTuple heapTuple;
|
||||||
|
while (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))
|
||||||
|
{
|
||||||
|
Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);
|
||||||
|
|
||||||
|
if (ForeignConstraintMatchesFlags(constraintForm, flags))
|
||||||
|
{
|
||||||
|
foreignKeyOids = lappend_oid(foreignKeyOids, constraintForm->oid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
systable_endscan(scanDescriptor);
|
systable_endscan(scanDescriptor);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
|
#include "catalog/pg_attrdef.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "commands/extension.h"
|
#include "commands/extension.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
|
@ -507,22 +508,14 @@ PreprocessAlterSequenceStmt(Node *node, const char *queryString,
|
||||||
static Oid
|
static Oid
|
||||||
SequenceUsedInDistributedTable(const ObjectAddress *sequenceAddress, char depType)
|
SequenceUsedInDistributedTable(const ObjectAddress *sequenceAddress, char depType)
|
||||||
{
|
{
|
||||||
List *citusTableIdList = CitusTableTypeIdList(ANY_CITUS_TABLE_TYPE);
|
Oid relationId;
|
||||||
Oid citusTableId = InvalidOid;
|
List *relations = GetDependentRelationsWithSequence(sequenceAddress->objectId,
|
||||||
foreach_oid(citusTableId, citusTableIdList)
|
depType);
|
||||||
|
foreach_oid(relationId, relations)
|
||||||
{
|
{
|
||||||
List *seqInfoList = NIL;
|
if (IsCitusTable(relationId))
|
||||||
GetDependentSequencesWithRelation(citusTableId, &seqInfoList, 0, depType);
|
|
||||||
SequenceInfo *seqInfo = NULL;
|
|
||||||
foreach_ptr(seqInfo, seqInfoList)
|
|
||||||
{
|
{
|
||||||
/*
|
return relationId;
|
||||||
* This sequence is used in a distributed table
|
|
||||||
*/
|
|
||||||
if (seqInfo->sequenceOid == sequenceAddress->objectId)
|
|
||||||
{
|
|
||||||
return citusTableId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,9 +271,24 @@ GetConnParams(ConnectionHashKey *key, char ***keywords, char ***values,
|
||||||
* We allocate everything in the provided context so as to facilitate using
|
* We allocate everything in the provided context so as to facilitate using
|
||||||
* pfree on all runtime parameters when connections using these entries are
|
* pfree on all runtime parameters when connections using these entries are
|
||||||
* invalidated during config reloads.
|
* invalidated during config reloads.
|
||||||
|
*
|
||||||
|
* Also, when "host" is already provided in global parameters, we use hostname
|
||||||
|
* from the key as "hostaddr" instead of "host" to avoid host name lookup. In
|
||||||
|
* that case, the value for "host" becomes useful only if the authentication
|
||||||
|
* method requires it.
|
||||||
*/
|
*/
|
||||||
|
bool gotHostParamFromGlobalParams = false;
|
||||||
|
for (Size paramIndex = 0; paramIndex < ConnParams.size; paramIndex++)
|
||||||
|
{
|
||||||
|
if (strcmp(ConnParams.keywords[paramIndex], "host") == 0)
|
||||||
|
{
|
||||||
|
gotHostParamFromGlobalParams = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char *runtimeKeywords[] = {
|
const char *runtimeKeywords[] = {
|
||||||
"host",
|
gotHostParamFromGlobalParams ? "hostaddr" : "host",
|
||||||
"port",
|
"port",
|
||||||
"dbname",
|
"dbname",
|
||||||
"user",
|
"user",
|
||||||
|
|
|
@ -883,7 +883,7 @@ WaitForAllConnections(List *connectionList, bool raiseInterrupts)
|
||||||
palloc(totalConnectionCount * sizeof(MultiConnection *));
|
palloc(totalConnectionCount * sizeof(MultiConnection *));
|
||||||
WaitEvent *events = palloc(totalConnectionCount * sizeof(WaitEvent));
|
WaitEvent *events = palloc(totalConnectionCount * sizeof(WaitEvent));
|
||||||
bool *connectionReady = palloc(totalConnectionCount * sizeof(bool));
|
bool *connectionReady = palloc(totalConnectionCount * sizeof(bool));
|
||||||
WaitEventSet *waitEventSet = NULL;
|
WaitEventSet *volatile waitEventSet = NULL;
|
||||||
|
|
||||||
/* convert connection list to an array such that we can move items around */
|
/* convert connection list to an array such that we can move items around */
|
||||||
MultiConnection *connectionItem = NULL;
|
MultiConnection *connectionItem = NULL;
|
||||||
|
|
|
@ -1637,6 +1637,74 @@ GetDependentSequencesWithRelation(Oid relationId, List **seqInfoList,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetDependentDependentRelationsWithSequence returns a list of oids of
|
||||||
|
* relations that have have a dependency on the given sequence.
|
||||||
|
* There are three types of dependencies:
|
||||||
|
* 1. direct auto (owned sequences), created using SERIAL or BIGSERIAL
|
||||||
|
* 2. indirect auto (through an AttrDef), created using DEFAULT nextval('..')
|
||||||
|
* 3. internal, created using GENERATED ALWAYS AS IDENTITY
|
||||||
|
*
|
||||||
|
* Depending on the passed deptype, we return the relations that have the
|
||||||
|
* given type(s):
|
||||||
|
* - DEPENDENCY_AUTO returns both 1 and 2
|
||||||
|
* - DEPENDENCY_INTERNAL returns 3
|
||||||
|
*
|
||||||
|
* The returned list can contain duplicates, as the same relation can have
|
||||||
|
* multiple dependencies on the sequence.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
GetDependentRelationsWithSequence(Oid sequenceOid, char depType)
|
||||||
|
{
|
||||||
|
List *relations = NIL;
|
||||||
|
ScanKeyData key[2];
|
||||||
|
HeapTuple tup;
|
||||||
|
|
||||||
|
Relation depRel = table_open(DependRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&key[0],
|
||||||
|
Anum_pg_depend_classid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(RelationRelationId));
|
||||||
|
ScanKeyInit(&key[1],
|
||||||
|
Anum_pg_depend_objid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(sequenceOid));
|
||||||
|
SysScanDesc scan = systable_beginscan(depRel, DependDependerIndexId, true,
|
||||||
|
NULL, lengthof(key), key);
|
||||||
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
||||||
|
{
|
||||||
|
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
|
||||||
|
|
||||||
|
if (
|
||||||
|
deprec->refclassid == RelationRelationId &&
|
||||||
|
deprec->refobjsubid != 0 &&
|
||||||
|
deprec->deptype == depType)
|
||||||
|
{
|
||||||
|
relations = lappend_oid(relations, deprec->refobjid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
systable_endscan(scan);
|
||||||
|
|
||||||
|
table_close(depRel, AccessShareLock);
|
||||||
|
|
||||||
|
if (depType == DEPENDENCY_AUTO)
|
||||||
|
{
|
||||||
|
Oid attrDefOid;
|
||||||
|
List *attrDefOids = GetAttrDefsFromSequence(sequenceOid);
|
||||||
|
|
||||||
|
foreach_oid(attrDefOid, attrDefOids)
|
||||||
|
{
|
||||||
|
ObjectAddress columnAddress = GetAttrDefaultColumnAddress(attrDefOid);
|
||||||
|
relations = lappend_oid(relations, columnAddress.objectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetSequencesFromAttrDef returns a list of sequence OIDs that have
|
* GetSequencesFromAttrDef returns a list of sequence OIDs that have
|
||||||
* dependency with the given attrdefOid in pg_depend
|
* dependency with the given attrdefOid in pg_depend
|
||||||
|
@ -1682,6 +1750,90 @@ GetSequencesFromAttrDef(Oid attrdefOid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < PG_VERSION_15
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a pg_attrdef OID, return the relation OID and column number of
|
||||||
|
* the owning column (represented as an ObjectAddress for convenience).
|
||||||
|
*
|
||||||
|
* Returns InvalidObjectAddress if there is no such pg_attrdef entry.
|
||||||
|
*/
|
||||||
|
ObjectAddress
|
||||||
|
GetAttrDefaultColumnAddress(Oid attrdefoid)
|
||||||
|
{
|
||||||
|
ObjectAddress result = InvalidObjectAddress;
|
||||||
|
ScanKeyData skey[1];
|
||||||
|
HeapTuple tup;
|
||||||
|
|
||||||
|
Relation attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
|
||||||
|
ScanKeyInit(&skey[0],
|
||||||
|
Anum_pg_attrdef_oid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(attrdefoid));
|
||||||
|
SysScanDesc scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
|
||||||
|
NULL, 1, skey);
|
||||||
|
|
||||||
|
if (HeapTupleIsValid(tup = systable_getnext(scan)))
|
||||||
|
{
|
||||||
|
Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
|
||||||
|
|
||||||
|
result.classId = RelationRelationId;
|
||||||
|
result.objectId = atdform->adrelid;
|
||||||
|
result.objectSubId = atdform->adnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
systable_endscan(scan);
|
||||||
|
table_close(attrdef, AccessShareLock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetAttrDefsFromSequence returns a list of attrdef OIDs that have
|
||||||
|
* a dependency on the given sequence
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
GetAttrDefsFromSequence(Oid seqOid)
|
||||||
|
{
|
||||||
|
List *attrDefsResult = NIL;
|
||||||
|
ScanKeyData key[2];
|
||||||
|
HeapTuple tup;
|
||||||
|
|
||||||
|
Relation depRel = table_open(DependRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&key[0],
|
||||||
|
Anum_pg_depend_refclassid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(RelationRelationId));
|
||||||
|
ScanKeyInit(&key[1],
|
||||||
|
Anum_pg_depend_refobjid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(seqOid));
|
||||||
|
SysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,
|
||||||
|
NULL, lengthof(key), key);
|
||||||
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
||||||
|
{
|
||||||
|
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
|
||||||
|
|
||||||
|
if (deprec->classid == AttrDefaultRelationId &&
|
||||||
|
deprec->deptype == DEPENDENCY_NORMAL)
|
||||||
|
{
|
||||||
|
attrDefsResult = lappend_oid(attrDefsResult, deprec->objid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
systable_endscan(scan);
|
||||||
|
|
||||||
|
table_close(depRel, AccessShareLock);
|
||||||
|
|
||||||
|
return attrDefsResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetDependentFunctionsWithRelation returns the dependent functions for the
|
* GetDependentFunctionsWithRelation returns the dependent functions for the
|
||||||
* given relation id.
|
* given relation id.
|
||||||
|
|
|
@ -2929,6 +2929,7 @@ NodeConninfoGucCheckHook(char **newval, void **extra, GucSource source)
|
||||||
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
|
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
|
||||||
"gsslib",
|
"gsslib",
|
||||||
#endif
|
#endif
|
||||||
|
"host",
|
||||||
"keepalives",
|
"keepalives",
|
||||||
"keepalives_count",
|
"keepalives_count",
|
||||||
"keepalives_idle",
|
"keepalives_idle",
|
||||||
|
|
|
@ -54,3 +54,4 @@ DROP VIEW IF EXISTS pg_catalog.citus_lock_waits;
|
||||||
#include "udfs/citus_internal_update_placement_metadata/12.2-1.sql"
|
#include "udfs/citus_internal_update_placement_metadata/12.2-1.sql"
|
||||||
#include "udfs/citus_internal_update_relation_colocation/12.2-1.sql"
|
#include "udfs/citus_internal_update_relation_colocation/12.2-1.sql"
|
||||||
#include "udfs/repl_origin_helper/12.2-1.sql"
|
#include "udfs/repl_origin_helper/12.2-1.sql"
|
||||||
|
#include "udfs/citus_finish_pg_upgrade/12.2-1.sql"
|
||||||
|
|
|
@ -54,3 +54,4 @@ DROP FUNCTION citus_internal.update_relation_colocation(oid, int);
|
||||||
DROP FUNCTION citus_internal.start_replication_origin_tracking();
|
DROP FUNCTION citus_internal.start_replication_origin_tracking();
|
||||||
DROP FUNCTION citus_internal.stop_replication_origin_tracking();
|
DROP FUNCTION citus_internal.stop_replication_origin_tracking();
|
||||||
DROP FUNCTION citus_internal.is_replication_origin_tracking_active();
|
DROP FUNCTION citus_internal.is_replication_origin_tracking_active();
|
||||||
|
#include "../udfs/citus_finish_pg_upgrade/12.1-1.sql"
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()
|
||||||
|
RETURNS void
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SET search_path = pg_catalog
|
||||||
|
AS $cppu$
|
||||||
|
DECLARE
|
||||||
|
table_name regclass;
|
||||||
|
command text;
|
||||||
|
trigger_name text;
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
|
||||||
|
IF substring(current_Setting('server_version'), '\d+')::int >= 14 THEN
|
||||||
|
EXECUTE $cmd$
|
||||||
|
-- disable propagation to prevent EnsureCoordinator errors
|
||||||
|
-- the aggregate created here does not depend on Citus extension (yet)
|
||||||
|
-- since we add the dependency with the next command
|
||||||
|
SET citus.enable_ddl_propagation TO OFF;
|
||||||
|
CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);
|
||||||
|
COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)
|
||||||
|
IS 'concatenate input arrays into a single array';
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
$cmd$;
|
||||||
|
ELSE
|
||||||
|
EXECUTE $cmd$
|
||||||
|
SET citus.enable_ddl_propagation TO OFF;
|
||||||
|
CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);
|
||||||
|
COMMENT ON AGGREGATE array_cat_agg(anyarray)
|
||||||
|
IS 'concatenate input arrays into a single array';
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
$cmd$;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Citus creates the array_cat_agg but because of a compatibility
|
||||||
|
-- issue between pg13-pg14, we drop and create it during upgrade.
|
||||||
|
-- And as Citus creates it, there needs to be a dependency to the
|
||||||
|
-- Citus extension, so we create that dependency here.
|
||||||
|
-- We are not using:
|
||||||
|
-- ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg
|
||||||
|
-- because we don't have an easy way to check if the aggregate
|
||||||
|
-- exists with anyarray type or anycompatiblearray type.
|
||||||
|
|
||||||
|
INSERT INTO pg_depend
|
||||||
|
SELECT
|
||||||
|
'pg_proc'::regclass::oid as classid,
|
||||||
|
(SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,
|
||||||
|
0 as objsubid,
|
||||||
|
'pg_extension'::regclass::oid as refclassid,
|
||||||
|
(select oid from pg_extension where extname = 'citus') as refobjid,
|
||||||
|
0 as refobjsubid ,
|
||||||
|
'e' as deptype;
|
||||||
|
|
||||||
|
-- PG16 has its own any_value, so only create it pre PG16.
|
||||||
|
-- We can remove this part when we drop support for PG16
|
||||||
|
IF substring(current_Setting('server_version'), '\d+')::int < 16 THEN
|
||||||
|
EXECUTE $cmd$
|
||||||
|
-- disable propagation to prevent EnsureCoordinator errors
|
||||||
|
-- the aggregate created here does not depend on Citus extension (yet)
|
||||||
|
-- since we add the dependency with the next command
|
||||||
|
SET citus.enable_ddl_propagation TO OFF;
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.any_value_agg ( anyelement, anyelement )
|
||||||
|
RETURNS anyelement AS $$
|
||||||
|
SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;
|
||||||
|
$$ LANGUAGE SQL STABLE;
|
||||||
|
|
||||||
|
CREATE AGGREGATE pg_catalog.any_value (
|
||||||
|
sfunc = pg_catalog.any_value_agg,
|
||||||
|
combinefunc = pg_catalog.any_value_agg,
|
||||||
|
basetype = anyelement,
|
||||||
|
stype = anyelement
|
||||||
|
);
|
||||||
|
COMMENT ON AGGREGATE pg_catalog.any_value(anyelement) IS
|
||||||
|
'Returns the value of any row in the group. It is mostly useful when you know there will be only 1 element.';
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
--
|
||||||
|
-- Citus creates the any_value aggregate but because of a compatibility
|
||||||
|
-- issue between pg15-pg16 -- any_value is created in PG16, we drop
|
||||||
|
-- and create it during upgrade IF upgraded version is less than 16.
|
||||||
|
-- And as Citus creates it, there needs to be a dependency to the
|
||||||
|
-- Citus extension, so we create that dependency here.
|
||||||
|
|
||||||
|
INSERT INTO pg_depend
|
||||||
|
SELECT
|
||||||
|
'pg_proc'::regclass::oid as classid,
|
||||||
|
(SELECT oid FROM pg_proc WHERE proname = 'any_value_agg') as objid,
|
||||||
|
0 as objsubid,
|
||||||
|
'pg_extension'::regclass::oid as refclassid,
|
||||||
|
(select oid from pg_extension where extname = 'citus') as refobjid,
|
||||||
|
0 as refobjsubid ,
|
||||||
|
'e' as deptype;
|
||||||
|
|
||||||
|
INSERT INTO pg_depend
|
||||||
|
SELECT
|
||||||
|
'pg_proc'::regclass::oid as classid,
|
||||||
|
(SELECT oid FROM pg_proc WHERE proname = 'any_value') as objid,
|
||||||
|
0 as objsubid,
|
||||||
|
'pg_extension'::regclass::oid as refclassid,
|
||||||
|
(select oid from pg_extension where extname = 'citus') as refobjid,
|
||||||
|
0 as refobjsubid ,
|
||||||
|
'e' as deptype;
|
||||||
|
$cmd$;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- restore citus catalog tables
|
||||||
|
--
|
||||||
|
INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;
|
||||||
|
|
||||||
|
-- if we are upgrading from PG14/PG15 to PG16+,
|
||||||
|
-- we need to regenerate the partkeys because they will include varnullingrels as well.
|
||||||
|
UPDATE pg_catalog.pg_dist_partition
|
||||||
|
SET partkey = column_name_to_column(pg_dist_partkeys_pre_16_upgrade.logicalrelid, col_name)
|
||||||
|
FROM public.pg_dist_partkeys_pre_16_upgrade
|
||||||
|
WHERE pg_dist_partkeys_pre_16_upgrade.logicalrelid = pg_dist_partition.logicalrelid;
|
||||||
|
DROP TABLE public.pg_dist_partkeys_pre_16_upgrade;
|
||||||
|
|
||||||
|
INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema;
|
||||||
|
-- enterprise catalog tables
|
||||||
|
INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;
|
||||||
|
|
||||||
|
-- Temporarily disable trigger to check for validity of functions while
|
||||||
|
-- inserting. The current contents of the table might be invalid if one of
|
||||||
|
-- the functions was removed by the user without also removing the
|
||||||
|
-- rebalance strategy. Obviously that's not great, but it should be no
|
||||||
|
-- reason to fail the upgrade.
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT
|
||||||
|
name,
|
||||||
|
default_strategy,
|
||||||
|
shard_cost_function::regprocedure::regproc,
|
||||||
|
node_capacity_function::regprocedure::regproc,
|
||||||
|
shard_allowed_on_node_function::regprocedure::regproc,
|
||||||
|
default_threshold,
|
||||||
|
minimum_threshold,
|
||||||
|
improvement_threshold
|
||||||
|
FROM public.pg_dist_rebalance_strategy;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- drop backup tables
|
||||||
|
--
|
||||||
|
DROP TABLE public.pg_dist_authinfo;
|
||||||
|
DROP TABLE public.pg_dist_colocation;
|
||||||
|
DROP TABLE public.pg_dist_local_group;
|
||||||
|
DROP TABLE public.pg_dist_node;
|
||||||
|
DROP TABLE public.pg_dist_node_metadata;
|
||||||
|
DROP TABLE public.pg_dist_partition;
|
||||||
|
DROP TABLE public.pg_dist_placement;
|
||||||
|
DROP TABLE public.pg_dist_poolinfo;
|
||||||
|
DROP TABLE public.pg_dist_shard;
|
||||||
|
DROP TABLE public.pg_dist_transaction;
|
||||||
|
DROP TABLE public.pg_dist_rebalance_strategy;
|
||||||
|
DROP TABLE public.pg_dist_cleanup;
|
||||||
|
DROP TABLE public.pg_dist_schema;
|
||||||
|
--
|
||||||
|
-- reset sequences
|
||||||
|
--
|
||||||
|
PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);
|
||||||
|
PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);
|
||||||
|
PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);
|
||||||
|
PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);
|
||||||
|
PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);
|
||||||
|
PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);
|
||||||
|
PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);
|
||||||
|
PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false);
|
||||||
|
DROP TABLE public.pg_dist_clock_logical_seq;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- register triggers
|
||||||
|
--
|
||||||
|
FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'
|
||||||
|
LOOP
|
||||||
|
trigger_name := 'truncate_trigger_' || table_name::oid;
|
||||||
|
command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';
|
||||||
|
EXECUTE command;
|
||||||
|
command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);
|
||||||
|
EXECUTE command;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- set dependencies
|
||||||
|
--
|
||||||
|
INSERT INTO pg_depend
|
||||||
|
SELECT
|
||||||
|
'pg_class'::regclass::oid as classid,
|
||||||
|
p.logicalrelid::regclass::oid as objid,
|
||||||
|
0 as objsubid,
|
||||||
|
'pg_extension'::regclass::oid as refclassid,
|
||||||
|
(select oid from pg_extension where extname = 'citus') as refobjid,
|
||||||
|
0 as refobjsubid ,
|
||||||
|
'n' as deptype
|
||||||
|
FROM pg_catalog.pg_dist_partition p;
|
||||||
|
|
||||||
|
-- set dependencies for columnar table access method
|
||||||
|
PERFORM columnar_internal.columnar_ensure_am_depends_catalog();
|
||||||
|
|
||||||
|
-- restore pg_dist_object from the stable identifiers
|
||||||
|
TRUNCATE pg_catalog.pg_dist_object;
|
||||||
|
INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)
|
||||||
|
SELECT
|
||||||
|
address.classid,
|
||||||
|
address.objid,
|
||||||
|
address.objsubid,
|
||||||
|
naming.distribution_argument_index,
|
||||||
|
naming.colocationid
|
||||||
|
FROM
|
||||||
|
public.pg_dist_object naming,
|
||||||
|
pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;
|
||||||
|
|
||||||
|
DROP TABLE public.pg_dist_object;
|
||||||
|
END;
|
||||||
|
$cppu$;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()
|
||||||
|
IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';
|
|
@ -128,6 +128,12 @@ BEGIN
|
||||||
INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;
|
INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;
|
||||||
INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;
|
INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;
|
||||||
|
|
||||||
|
-- Temporarily disable trigger to check for validity of functions while
|
||||||
|
-- inserting. The current contents of the table might be invalid if one of
|
||||||
|
-- the functions was removed by the user without also removing the
|
||||||
|
-- rebalance strategy. Obviously that's not great, but it should be no
|
||||||
|
-- reason to fail the upgrade.
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;
|
||||||
INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT
|
INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT
|
||||||
name,
|
name,
|
||||||
default_strategy,
|
default_strategy,
|
||||||
|
@ -138,6 +144,7 @@ BEGIN
|
||||||
minimum_threshold,
|
minimum_threshold,
|
||||||
improvement_threshold
|
improvement_threshold
|
||||||
FROM public.pg_dist_rebalance_strategy;
|
FROM public.pg_dist_rebalance_strategy;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- drop backup tables
|
-- drop backup tables
|
||||||
|
|
|
@ -465,8 +465,8 @@ static bool
|
||||||
AnyObjectViolatesOwnership(DropStmt *dropStmt)
|
AnyObjectViolatesOwnership(DropStmt *dropStmt)
|
||||||
{
|
{
|
||||||
bool hasOwnershipViolation = false;
|
bool hasOwnershipViolation = false;
|
||||||
volatile ObjectAddress objectAddress = { 0 };
|
ObjectAddress objectAddress = { 0 };
|
||||||
Relation relation = NULL;
|
volatile Relation relation = NULL;
|
||||||
ObjectType objectType = dropStmt->removeType;
|
ObjectType objectType = dropStmt->removeType;
|
||||||
bool missingOk = dropStmt->missing_ok;
|
bool missingOk = dropStmt->missing_ok;
|
||||||
|
|
||||||
|
@ -480,8 +480,17 @@ AnyObjectViolatesOwnership(DropStmt *dropStmt)
|
||||||
Node *object = NULL;
|
Node *object = NULL;
|
||||||
foreach_ptr(object, dropStmt->objects)
|
foreach_ptr(object, dropStmt->objects)
|
||||||
{
|
{
|
||||||
|
Relation rel = NULL;
|
||||||
objectAddress = get_object_address(objectType, object,
|
objectAddress = get_object_address(objectType, object,
|
||||||
&relation, AccessShareLock, missingOk);
|
&rel, AccessShareLock, missingOk);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The object relation is qualified with volatile and its value is obtained from
|
||||||
|
* get_object_address(). Unless we can qualify the corresponding parameter of
|
||||||
|
* get_object_address() with volatile (this is a function defined in PostgreSQL),
|
||||||
|
* we cannot get rid of this assignment.
|
||||||
|
*/
|
||||||
|
relation = rel;
|
||||||
|
|
||||||
if (OidIsValid(objectAddress.objectId))
|
if (OidIsValid(objectAddress.objectId))
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,6 +54,7 @@ static bool ShouldHideShardsInternal(void);
|
||||||
static bool IsPgBgWorker(void);
|
static bool IsPgBgWorker(void);
|
||||||
static bool FilterShardsFromPgclass(Node *node, void *context);
|
static bool FilterShardsFromPgclass(Node *node, void *context);
|
||||||
static Node * CreateRelationIsAKnownShardFilter(int pgClassVarno);
|
static Node * CreateRelationIsAKnownShardFilter(int pgClassVarno);
|
||||||
|
static bool HasRangeTableRef(Node *node, int *varno);
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(citus_table_is_visible);
|
PG_FUNCTION_INFO_V1(citus_table_is_visible);
|
||||||
PG_FUNCTION_INFO_V1(relation_is_a_known_shard);
|
PG_FUNCTION_INFO_V1(relation_is_a_known_shard);
|
||||||
|
@ -421,8 +422,8 @@ IsPgBgWorker(void)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FilterShardsFromPgclass adds a NOT relation_is_a_known_shard(oid) filter
|
* FilterShardsFromPgclass adds a "relation_is_a_known_shard(oid) IS NOT TRUE"
|
||||||
* to the security quals of pg_class RTEs.
|
* filter to the quals of queries that query pg_class.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
FilterShardsFromPgclass(Node *node, void *context)
|
FilterShardsFromPgclass(Node *node, void *context)
|
||||||
|
@ -456,12 +457,35 @@ FilterShardsFromPgclass(Node *node, void *context)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip if pg_class is not actually queried. This is possible on
|
||||||
|
* INSERT statements that insert into pg_class.
|
||||||
|
*/
|
||||||
|
if (!expression_tree_walker((Node *) query->jointree->fromlist,
|
||||||
|
HasRangeTableRef, &varno))
|
||||||
|
{
|
||||||
|
/* the query references pg_class */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* make sure the expression is in the right memory context */
|
/* make sure the expression is in the right memory context */
|
||||||
MemoryContext originalContext = MemoryContextSwitchTo(queryContext);
|
MemoryContext originalContext = MemoryContextSwitchTo(queryContext);
|
||||||
|
|
||||||
/* add NOT relation_is_a_known_shard(oid) to the security quals of the RTE */
|
|
||||||
rangeTableEntry->securityQuals =
|
/* add relation_is_a_known_shard(oid) IS NOT TRUE to the quals of the query */
|
||||||
list_make1(CreateRelationIsAKnownShardFilter(varno));
|
Node *newQual = CreateRelationIsAKnownShardFilter(varno);
|
||||||
|
Node *oldQuals = query->jointree->quals;
|
||||||
|
if (oldQuals)
|
||||||
|
{
|
||||||
|
query->jointree->quals = (Node *) makeBoolExpr(
|
||||||
|
AND_EXPR,
|
||||||
|
list_make2(oldQuals, newQual),
|
||||||
|
-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query->jointree->quals = newQual;
|
||||||
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(originalContext);
|
MemoryContextSwitchTo(originalContext);
|
||||||
}
|
}
|
||||||
|
@ -473,9 +497,32 @@ FilterShardsFromPgclass(Node *node, void *context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HasRangeTableRef passed to expression_tree_walker to check if a node is a
|
||||||
|
* RangeTblRef of the given varno is present in a fromlist.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
HasRangeTableRef(Node *node, int *varno)
|
||||||
|
{
|
||||||
|
if (IsA(node, RangeTblRef))
|
||||||
|
{
|
||||||
|
RangeTblRef *rangeTblRef = (RangeTblRef *) node;
|
||||||
|
return rangeTblRef->rtindex == *varno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression_tree_walker(node, HasRangeTableRef, varno);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CreateRelationIsAKnownShardFilter constructs an expression of the form:
|
* CreateRelationIsAKnownShardFilter constructs an expression of the form:
|
||||||
* NOT pg_catalog.relation_is_a_known_shard(oid)
|
* pg_catalog.relation_is_a_known_shard(oid) IS NOT TRUE
|
||||||
|
*
|
||||||
|
* The difference between "NOT pg_catalog.relation_is_a_known_shard(oid)" and
|
||||||
|
* "pg_catalog.relation_is_a_known_shard(oid) IS NOT TRUE" is that the former
|
||||||
|
* will return FALSE if the function returns NULL, while the second will return
|
||||||
|
* TRUE. This difference is important in the case of outer joins, because this
|
||||||
|
* filter might be applied on an oid that is then NULL.
|
||||||
*/
|
*/
|
||||||
static Node *
|
static Node *
|
||||||
CreateRelationIsAKnownShardFilter(int pgClassVarno)
|
CreateRelationIsAKnownShardFilter(int pgClassVarno)
|
||||||
|
@ -496,9 +543,9 @@ CreateRelationIsAKnownShardFilter(int pgClassVarno)
|
||||||
funcExpr->location = -1;
|
funcExpr->location = -1;
|
||||||
funcExpr->args = list_make1(oidVar);
|
funcExpr->args = list_make1(oidVar);
|
||||||
|
|
||||||
BoolExpr *notExpr = makeNode(BoolExpr);
|
BooleanTest *notExpr = makeNode(BooleanTest);
|
||||||
notExpr->boolop = NOT_EXPR;
|
notExpr->booltesttype = IS_NOT_TRUE;
|
||||||
notExpr->args = list_make1(funcExpr);
|
notExpr->arg = (Expr *) funcExpr;
|
||||||
notExpr->location = -1;
|
notExpr->location = -1;
|
||||||
|
|
||||||
return (Node *) notExpr;
|
return (Node *) notExpr;
|
||||||
|
|
|
@ -130,8 +130,13 @@ extern List * IdentitySequenceDependencyCommandList(Oid targetRelationId);
|
||||||
|
|
||||||
extern List * DDLCommandsForSequence(Oid sequenceOid, char *ownerName);
|
extern List * DDLCommandsForSequence(Oid sequenceOid, char *ownerName);
|
||||||
extern List * GetSequencesFromAttrDef(Oid attrdefOid);
|
extern List * GetSequencesFromAttrDef(Oid attrdefOid);
|
||||||
|
#if PG_VERSION_NUM < PG_VERSION_15
|
||||||
|
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid);
|
||||||
|
#endif
|
||||||
|
extern List * GetAttrDefsFromSequence(Oid seqOid);
|
||||||
extern void GetDependentSequencesWithRelation(Oid relationId, List **seqInfoList,
|
extern void GetDependentSequencesWithRelation(Oid relationId, List **seqInfoList,
|
||||||
AttrNumber attnum, char depType);
|
AttrNumber attnum, char depType);
|
||||||
|
extern List * GetDependentRelationsWithSequence(Oid seqId, char depType);
|
||||||
extern List * GetDependentFunctionsWithRelation(Oid relationId);
|
extern List * GetDependentFunctionsWithRelation(Oid relationId);
|
||||||
extern Oid GetAttributeTypeOid(Oid relationId, AttrNumber attnum);
|
extern Oid GetAttributeTypeOid(Oid relationId, AttrNumber attnum);
|
||||||
extern void SetLocalEnableMetadataSync(bool state);
|
extern void SetLocalEnableMetadataSync(bool state);
|
||||||
|
|
|
@ -294,6 +294,9 @@ def _run_pg_regress(
|
||||||
output_dir,
|
output_dir,
|
||||||
"--use-existing",
|
"--use-existing",
|
||||||
]
|
]
|
||||||
|
if PG_MAJOR_VERSION >= 16:
|
||||||
|
command.append("--expecteddir")
|
||||||
|
command.append(output_dir)
|
||||||
if extra_tests != "":
|
if extra_tests != "":
|
||||||
command.append(extra_tests)
|
command.append(extra_tests)
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,52 @@ SELECT relname FROM pg_catalog.pg_class WHERE relnamespace = 'mx_hide_shard_name
|
||||||
test_table
|
test_table
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- Even when using subquery and having no existing quals on pg_clcass
|
||||||
|
SELECT relname FROM (SELECT relname, relnamespace FROM pg_catalog.pg_class) AS q WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname;
|
||||||
|
relname
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
test_table
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Check that inserts into pg_class don't add the filter
|
||||||
|
EXPLAIN (COSTS OFF) INSERT INTO pg_class VALUES (1);
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
Insert on pg_class
|
||||||
|
-> Result
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Unless it's an INSERT SELECT that queries from pg_class;
|
||||||
|
EXPLAIN (COSTS OFF) INSERT INTO pg_class SELECT * FROM pg_class;
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
Insert on pg_class
|
||||||
|
-> Seq Scan on pg_class pg_class_1
|
||||||
|
Filter: (relation_is_a_known_shard(oid) IS NOT TRUE)
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
-- Check that query that psql "\d test_table" does gets optimized to an index
|
||||||
|
-- scan
|
||||||
|
EXPLAIN (COSTS OFF) SELECT c.oid,
|
||||||
|
n.nspname,
|
||||||
|
c.relname
|
||||||
|
FROM pg_catalog.pg_class c
|
||||||
|
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||||
|
WHERE c.relname OPERATOR(pg_catalog.~) '^(test_table)$' COLLATE pg_catalog.default
|
||||||
|
AND pg_catalog.pg_table_is_visible(c.oid)
|
||||||
|
ORDER BY 2, 3;
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: n.nspname, c.relname
|
||||||
|
-> Nested Loop Left Join
|
||||||
|
Join Filter: (n.oid = c.relnamespace)
|
||||||
|
-> Index Scan using pg_class_relname_nsp_index on pg_class c
|
||||||
|
Index Cond: (relname = 'test_table'::text)
|
||||||
|
Filter: ((relname ~ '^(test_table)$'::text) AND (relation_is_a_known_shard(oid) IS NOT TRUE) AND pg_table_is_visible(oid))
|
||||||
|
-> Seq Scan on pg_namespace n
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
commit prepared 'take-aggressive-lock';
|
commit prepared 'take-aggressive-lock';
|
||||||
-- now create an index
|
-- now create an index
|
||||||
\c - - - :master_port
|
\c - - - :master_port
|
||||||
|
|
|
@ -520,5 +520,61 @@ show citus.node_conninfo;
|
||||||
|
|
||||||
-- Should work again
|
-- Should work again
|
||||||
ALTER TABLE test ADD COLUMN e INT;
|
ALTER TABLE test ADD COLUMN e INT;
|
||||||
|
-- show that we allow providing "host" param via citus.node_conninfo
|
||||||
|
ALTER SYSTEM SET citus.node_conninfo = 'sslmode=require host=nosuchhost';
|
||||||
|
SELECT pg_reload_conf();
|
||||||
|
pg_reload_conf
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_sleep(0.1);
|
||||||
|
pg_sleep
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- fails due to invalid host
|
||||||
|
SELECT COUNT(*)>=0 FROM test;
|
||||||
|
WARNING: connection to the remote node postgres@localhost:xxxxx failed with the following error: could not parse network address "localhost": Name or service not known
|
||||||
|
ERROR: connection to the remote node postgres@localhost:xxxxx failed with the following error: could not parse network address "localhost": Name or service not known
|
||||||
|
SELECT array_agg(nodeid) as updated_nodeids from pg_dist_node WHERE nodename = 'localhost' \gset
|
||||||
|
UPDATE pg_dist_node SET nodename = '127.0.0.1' WHERE nodeid = ANY(:'updated_nodeids'::int[]);
|
||||||
|
ALTER SYSTEM SET citus.node_conninfo = 'sslmode=require host=localhost';
|
||||||
|
SELECT pg_reload_conf();
|
||||||
|
pg_reload_conf
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_sleep(0.1);
|
||||||
|
pg_sleep
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- works when hostaddr is specified in pg_dist_node after providing host in citus.node_conninfo
|
||||||
|
SELECT COUNT(*)>=0 FROM test;
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- restore original nodenames into pg_dist_node
|
||||||
|
UPDATE pg_dist_node SET nodename = 'localhost' WHERE nodeid = ANY(:'updated_nodeids'::int[]);
|
||||||
|
-- reset it
|
||||||
|
ALTER SYSTEM RESET citus.node_conninfo;
|
||||||
|
select pg_reload_conf();
|
||||||
|
pg_reload_conf
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select pg_sleep(0.1); -- wait for config reload to apply
|
||||||
|
pg_sleep
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
DROP SCHEMA node_conninfo_reload CASCADE;
|
DROP SCHEMA node_conninfo_reload CASCADE;
|
||||||
NOTICE: drop cascades to table test
|
NOTICE: drop cascades to table test
|
||||||
|
|
|
@ -4,5 +4,6 @@ SELECT * FROM pg_catalog.pg_dist_rebalance_strategy ORDER BY name;
|
||||||
by_disk_size | f | citus_shard_cost_by_disk_size | citus_node_capacity_1 | citus_shard_allowed_on_node_true | 0.1 | 0.01 | 0.5
|
by_disk_size | f | citus_shard_cost_by_disk_size | citus_node_capacity_1 | citus_shard_allowed_on_node_true | 0.1 | 0.01 | 0.5
|
||||||
by_shard_count | f | citus_shard_cost_1 | citus_node_capacity_1 | citus_shard_allowed_on_node_true | 0 | 0 | 0
|
by_shard_count | f | citus_shard_cost_1 | citus_node_capacity_1 | citus_shard_allowed_on_node_true | 0 | 0 | 0
|
||||||
custom_strategy | t | upgrade_rebalance_strategy.shard_cost_2 | upgrade_rebalance_strategy.capacity_high_worker_1 | upgrade_rebalance_strategy.only_worker_2 | 0.5 | 0.2 | 0.3
|
custom_strategy | t | upgrade_rebalance_strategy.shard_cost_2 | upgrade_rebalance_strategy.capacity_high_worker_1 | upgrade_rebalance_strategy.only_worker_2 | 0.5 | 0.2 | 0.3
|
||||||
(3 rows)
|
invalid_strategy | f | 1234567 | upgrade_rebalance_strategy.capacity_high_worker_1 | upgrade_rebalance_strategy.only_worker_2 | 0.5 | 0.2 | 0.3
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
|
|
@ -35,3 +35,23 @@ SELECT citus_set_default_rebalance_strategy('custom_strategy');
|
||||||
|
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- Disable the trigger temporarily to allow the invalid strategy to be added.
|
||||||
|
-- Normally an invalid strategy can end up in the table by deleting one of the
|
||||||
|
-- functions it depends on. But we do directly in this test because we want to
|
||||||
|
-- have a consistent OID, so we get consistent test output.
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;
|
||||||
|
SELECT citus_add_rebalance_strategy(
|
||||||
|
'invalid_strategy',
|
||||||
|
1234567,
|
||||||
|
'capacity_high_worker_1',
|
||||||
|
'only_worker_2',
|
||||||
|
0.5,
|
||||||
|
0.2,
|
||||||
|
0.3
|
||||||
|
);
|
||||||
|
citus_add_rebalance_strategy
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;
|
||||||
|
|
|
@ -1127,6 +1127,22 @@ sub RunVanillaTests
|
||||||
system("mkdir", ("-p", "$pgregressOutputdir/sql")) == 0
|
system("mkdir", ("-p", "$pgregressOutputdir/sql")) == 0
|
||||||
or die "Could not create vanilla sql dir.";
|
or die "Could not create vanilla sql dir.";
|
||||||
|
|
||||||
|
if ($majorversion >= "16")
|
||||||
|
{
|
||||||
|
$exitcode = system("$plainRegress",
|
||||||
|
("--dlpath", $dlpath),
|
||||||
|
("--inputdir", $pgregressInputdir),
|
||||||
|
("--outputdir", $pgregressOutputdir),
|
||||||
|
("--expecteddir", $pgregressOutputdir),
|
||||||
|
("--schedule", catfile("$pgregressInputdir", "parallel_schedule")),
|
||||||
|
("--use-existing"),
|
||||||
|
("--host","$host"),
|
||||||
|
("--port","$masterPort"),
|
||||||
|
("--user","$user"),
|
||||||
|
("--dbname", "$dbName"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
$exitcode = system("$plainRegress",
|
$exitcode = system("$plainRegress",
|
||||||
("--dlpath", $dlpath),
|
("--dlpath", $dlpath),
|
||||||
("--inputdir", $pgregressInputdir),
|
("--inputdir", $pgregressInputdir),
|
||||||
|
@ -1137,6 +1153,7 @@ sub RunVanillaTests
|
||||||
("--port","$masterPort"),
|
("--port","$masterPort"),
|
||||||
("--user","$user"),
|
("--user","$user"),
|
||||||
("--dbname", "$dbName"));
|
("--dbname", "$dbName"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($useMitmproxy) {
|
if ($useMitmproxy) {
|
||||||
|
|
|
@ -50,6 +50,24 @@ prepare transaction 'take-aggressive-lock';
|
||||||
|
|
||||||
-- shards are hidden when using psql as application_name
|
-- shards are hidden when using psql as application_name
|
||||||
SELECT relname FROM pg_catalog.pg_class WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname;
|
SELECT relname FROM pg_catalog.pg_class WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname;
|
||||||
|
-- Even when using subquery and having no existing quals on pg_clcass
|
||||||
|
SELECT relname FROM (SELECT relname, relnamespace FROM pg_catalog.pg_class) AS q WHERE relnamespace = 'mx_hide_shard_names'::regnamespace ORDER BY relname;
|
||||||
|
|
||||||
|
-- Check that inserts into pg_class don't add the filter
|
||||||
|
EXPLAIN (COSTS OFF) INSERT INTO pg_class VALUES (1);
|
||||||
|
-- Unless it's an INSERT SELECT that queries from pg_class;
|
||||||
|
EXPLAIN (COSTS OFF) INSERT INTO pg_class SELECT * FROM pg_class;
|
||||||
|
|
||||||
|
-- Check that query that psql "\d test_table" does gets optimized to an index
|
||||||
|
-- scan
|
||||||
|
EXPLAIN (COSTS OFF) SELECT c.oid,
|
||||||
|
n.nspname,
|
||||||
|
c.relname
|
||||||
|
FROM pg_catalog.pg_class c
|
||||||
|
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||||
|
WHERE c.relname OPERATOR(pg_catalog.~) '^(test_table)$' COLLATE pg_catalog.default
|
||||||
|
AND pg_catalog.pg_table_is_visible(c.oid)
|
||||||
|
ORDER BY 2, 3;
|
||||||
|
|
||||||
commit prepared 'take-aggressive-lock';
|
commit prepared 'take-aggressive-lock';
|
||||||
|
|
||||||
|
|
|
@ -205,4 +205,30 @@ show citus.node_conninfo;
|
||||||
-- Should work again
|
-- Should work again
|
||||||
ALTER TABLE test ADD COLUMN e INT;
|
ALTER TABLE test ADD COLUMN e INT;
|
||||||
|
|
||||||
|
-- show that we allow providing "host" param via citus.node_conninfo
|
||||||
|
ALTER SYSTEM SET citus.node_conninfo = 'sslmode=require host=nosuchhost';
|
||||||
|
SELECT pg_reload_conf();
|
||||||
|
SELECT pg_sleep(0.1);
|
||||||
|
|
||||||
|
-- fails due to invalid host
|
||||||
|
SELECT COUNT(*)>=0 FROM test;
|
||||||
|
|
||||||
|
SELECT array_agg(nodeid) as updated_nodeids from pg_dist_node WHERE nodename = 'localhost' \gset
|
||||||
|
UPDATE pg_dist_node SET nodename = '127.0.0.1' WHERE nodeid = ANY(:'updated_nodeids'::int[]);
|
||||||
|
|
||||||
|
ALTER SYSTEM SET citus.node_conninfo = 'sslmode=require host=localhost';
|
||||||
|
SELECT pg_reload_conf();
|
||||||
|
SELECT pg_sleep(0.1);
|
||||||
|
|
||||||
|
-- works when hostaddr is specified in pg_dist_node after providing host in citus.node_conninfo
|
||||||
|
SELECT COUNT(*)>=0 FROM test;
|
||||||
|
|
||||||
|
-- restore original nodenames into pg_dist_node
|
||||||
|
UPDATE pg_dist_node SET nodename = 'localhost' WHERE nodeid = ANY(:'updated_nodeids'::int[]);
|
||||||
|
|
||||||
|
-- reset it
|
||||||
|
ALTER SYSTEM RESET citus.node_conninfo;
|
||||||
|
select pg_reload_conf();
|
||||||
|
select pg_sleep(0.1); -- wait for config reload to apply
|
||||||
|
|
||||||
DROP SCHEMA node_conninfo_reload CASCADE;
|
DROP SCHEMA node_conninfo_reload CASCADE;
|
||||||
|
|
|
@ -29,3 +29,19 @@ SELECT citus_add_rebalance_strategy(
|
||||||
0.3
|
0.3
|
||||||
);
|
);
|
||||||
SELECT citus_set_default_rebalance_strategy('custom_strategy');
|
SELECT citus_set_default_rebalance_strategy('custom_strategy');
|
||||||
|
|
||||||
|
-- Disable the trigger temporarily to allow the invalid strategy to be added.
|
||||||
|
-- Normally an invalid strategy can end up in the table by deleting one of the
|
||||||
|
-- functions it depends on. But we do directly in this test because we want to
|
||||||
|
-- have a consistent OID, so we get consistent test output.
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;
|
||||||
|
SELECT citus_add_rebalance_strategy(
|
||||||
|
'invalid_strategy',
|
||||||
|
1234567,
|
||||||
|
'capacity_high_worker_1',
|
||||||
|
'only_worker_2',
|
||||||
|
0.5,
|
||||||
|
0.2,
|
||||||
|
0.3
|
||||||
|
);
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;
|
||||||
|
|
Loading…
Reference in New Issue