diff --git a/configure b/configure index 40603eccd..c93de84de 100755 --- a/configure +++ b/configure @@ -2543,7 +2543,7 @@ if test -z "$version_num"; then as_fn_error $? "Could not detect PostgreSQL version from pg_config." "$LINENO" 5 fi -if test "$version_num" != '10' -a "$version_num" != '11' -a "$version_num" != '12'; then +if test "$version_num" != '11' -a "$version_num" != '12'; then as_fn_error $? "Citus is not compatible with the detected PostgreSQL version ${version_num}." "$LINENO" 5 else { $as_echo "$as_me:${as_lineno-$LINENO}: building against PostgreSQL $version_num" >&5 diff --git a/configure.in b/configure.in index e60a00703..d54144d15 100644 --- a/configure.in +++ b/configure.in @@ -74,7 +74,7 @@ if test -z "$version_num"; then AC_MSG_ERROR([Could not detect PostgreSQL version from pg_config.]) fi -if test "$version_num" != '10' -a "$version_num" != '11' -a "$version_num" != '12'; then +if test "$version_num" != '11' -a "$version_num" != '12'; then AC_MSG_ERROR([Citus is not compatible with the detected PostgreSQL version ${version_num}.]) else AC_MSG_NOTICE([building against PostgreSQL $version_num]) diff --git a/src/backend/distributed/commands/call.c b/src/backend/distributed/commands/call.c index c8a7c1d52..69cf088ad 100644 --- a/src/backend/distributed/commands/call.c +++ b/src/backend/distributed/commands/call.c @@ -12,8 +12,6 @@ #include "postgres.h" -#if PG_VERSION_NUM >= 110000 - #include "catalog/pg_proc.h" #include "commands/defrem.h" #include "distributed/citus_ruleutils.h" @@ -207,6 +205,3 @@ CallFuncExprRemotely(CallStmt *callStmt, DistObjectCacheEntry *procedure, return true; } - - -#endif /* PG_VERSION_NUM >= 110000 */ diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 6055f0673..59fc01737 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -22,9 +22,6 @@ #include "catalog/index.h" #include "catalog/pg_am.h" #include "catalog/pg_attribute.h" -#if (PG_VERSION_NUM < 110000) -#include "catalog/pg_constraint_fn.h" -#endif #include "catalog/pg_enum.h" #include "catalog/pg_extension.h" #include "catalog/pg_opclass.h" @@ -1166,9 +1163,9 @@ CreateTruncateTrigger(Oid relationId) trigger->whenClause = NULL; trigger->isconstraint = false; - CreateTriggerInternal(trigger, NULL, relationId, InvalidOid, InvalidOid, InvalidOid, - InvalidOid, InvalidOid, NULL, - internal, false); + CreateTrigger(trigger, NULL, relationId, InvalidOid, InvalidOid, InvalidOid, + InvalidOid, InvalidOid, NULL, + internal, false); } diff --git a/src/backend/distributed/commands/foreign_constraint.c b/src/backend/distributed/commands/foreign_constraint.c index 85a556c63..9bf8b613c 100644 --- a/src/backend/distributed/commands/foreign_constraint.c +++ b/src/backend/distributed/commands/foreign_constraint.c @@ -18,9 +18,6 @@ #if (PG_VERSION_NUM >= 120000) #include "access/genam.h" #endif -#if (PG_VERSION_NUM < 110000) -#include "catalog/pg_constraint_fn.h" -#endif #include "catalog/pg_type.h" #include "distributed/colocation_utils.h" #include "distributed/commands.h" @@ -151,7 +148,8 @@ ErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDis pgConstraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, relation->rd_id); - scanDescriptor = systable_beginscan(pgConstraint, ConstraintRelidIndexId, true, NULL, + scanDescriptor = systable_beginscan(pgConstraint, ConstraintRelidTypidNameIndexId, + true, NULL, scanKeyCount, scanKey); heapTuple = systable_getnext(scanDescriptor); @@ -515,7 +513,8 @@ GetTableForeignConstraintCommands(Oid relationId) pgConstraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, relationId); - scanDescriptor = systable_beginscan(pgConstraint, ConstraintRelidIndexId, true, NULL, + scanDescriptor = systable_beginscan(pgConstraint, ConstraintRelidTypidNameIndexId, + true, NULL, scanKeyCount, scanKey); heapTuple = systable_getnext(scanDescriptor); @@ -523,11 +522,7 @@ GetTableForeignConstraintCommands(Oid relationId) { Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple); -#if (PG_VERSION_NUM >= 110000) bool inheritedConstraint = OidIsValid(constraintForm->conparentid); -#else - bool inheritedConstraint = false; -#endif if (!inheritedConstraint && constraintForm->contype == CONSTRAINT_FOREIGN) { @@ -571,7 +566,8 @@ HasForeignKeyToReferenceTable(Oid relationId) pgConstraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, relationId); - scanDescriptor = systable_beginscan(pgConstraint, ConstraintRelidIndexId, true, NULL, + scanDescriptor = systable_beginscan(pgConstraint, ConstraintRelidTypidNameIndexId, + true, NULL, scanKeyCount, scanKey); heapTuple = systable_getnext(scanDescriptor); @@ -679,7 +675,7 @@ HeapTupleOfForeignConstraintIncludesColumn(HeapTuple heapTuple, Oid relationId, { AttrNumber attrNo = DatumGetInt16(columnArray[attrIdx]); - char *colName = get_attname_internal(relationId, attrNo, false); + char *colName = get_attname(relationId, attrNo, false); if (strncmp(colName, columnName, NAMEDATALEN) == 0) { return true; diff --git a/src/backend/distributed/commands/function.c b/src/backend/distributed/commands/function.c index 063156a12..a02fd2e5d 100644 --- a/src/backend/distributed/commands/function.c +++ b/src/backend/distributed/commands/function.c @@ -79,13 +79,8 @@ static void ErrorIfFunctionDependsOnExtension(const ObjectAddress *functionAddre PG_FUNCTION_INFO_V1(create_distributed_function); -#if PG_VERSION_NUM >= 110000 #define AssertIsFunctionOrProcedure(objtype) \ Assert((objtype) == OBJECT_FUNCTION || (objtype) == OBJECT_PROCEDURE) -#else -#define AssertIsFunctionOrProcedure(objtype) \ - Assert(objtype == OBJECT_FUNCTION) -#endif /* @@ -597,9 +592,7 @@ GetFunctionAlterOwnerCommand(const RegProcedure funcOid) procOwner = procform->proowner; -#if (PG_VERSION_NUM >= 110000) isProcedure = procform->prokind == PROKIND_PROCEDURE; -#endif ReleaseSysCache(proctup); } @@ -878,12 +871,10 @@ CreateFunctionStmtObjectAddress(CreateFunctionStmt *stmt, bool missing_ok) ObjectWithArgs *objectWithArgs = NULL; ListCell *parameterCell = NULL; -#if PG_VERSION_NUM >= 110000 if (stmt->is_procedure) { objectType = OBJECT_PROCEDURE; } -#endif objectWithArgs = makeNode(ObjectWithArgs); objectWithArgs->objname = stmt->funcname; @@ -910,10 +901,7 @@ PlanAlterFunctionStmt(AlterFunctionStmt *stmt, const char *queryString) const ObjectAddress *address = NULL; List *commands = NIL; - /* AlterFunctionStmt->objtype has only been added since pg11 */ -#if PG_VERSION_NUM >= 110000 AssertIsFunctionOrProcedure(stmt->objtype); -#endif address = GetObjectAddressFromParseTree((Node *) stmt, false); if (!ShouldPropagateAlterFunction(address)) @@ -1249,13 +1237,7 @@ ProcessAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryStr const ObjectAddress * AlterFunctionStmtObjectAddress(AlterFunctionStmt *stmt, bool missing_ok) { - ObjectType objectType = OBJECT_FUNCTION; - -#if PG_VERSION_NUM >= 110000 - objectType = stmt->objtype; -#endif - - return FunctionToObjectAddress(objectType, stmt->func, missing_ok); + return FunctionToObjectAddress(stmt->objtype, stmt->func, missing_ok); } @@ -1303,7 +1285,7 @@ AlterFunctionSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, bool missing_o AssertIsFunctionOrProcedure(stmt->objectType); objectWithArgs = castNode(ObjectWithArgs, stmt->object); - funcOid = LookupFuncWithArgsCompat(stmt->objectType, objectWithArgs, true); + funcOid = LookupFuncWithArgs(stmt->objectType, objectWithArgs, true); names = objectWithArgs->objname; if (funcOid == InvalidOid) @@ -1322,7 +1304,7 @@ AlterFunctionSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, bool missing_o * error if the type didn't exist in the first place. */ objectWithArgs->objname = newNames; - funcOid = LookupFuncWithArgsCompat(stmt->objectType, objectWithArgs, true); + funcOid = LookupFuncWithArgs(stmt->objectType, objectWithArgs, true); objectWithArgs->objname = names; /* restore the original names */ /* @@ -1336,8 +1318,8 @@ AlterFunctionSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, bool missing_o * has just been created (if possible at all). For safety we assign the * funcOid. */ - funcOid = LookupFuncWithArgsCompat(stmt->objectType, objectWithArgs, - missing_ok); + funcOid = LookupFuncWithArgs(stmt->objectType, objectWithArgs, + missing_ok); } } @@ -1363,7 +1345,7 @@ FunctionToObjectAddress(ObjectType objectType, ObjectWithArgs *objectWithArgs, AssertIsFunctionOrProcedure(objectType); - funcOid = LookupFuncWithArgsCompat(objectType, objectWithArgs, missing_ok); + funcOid = LookupFuncWithArgs(objectType, objectWithArgs, missing_ok); address = palloc0(sizeof(ObjectAddress)); ObjectAddressSet(*address, ProcedureRelationId, funcOid); diff --git a/src/backend/distributed/commands/index.c b/src/backend/distributed/commands/index.c index 9a65718bf..6c5614216 100644 --- a/src/backend/distributed/commands/index.c +++ b/src/backend/distributed/commands/index.c @@ -234,7 +234,7 @@ PlanReindexStmt(ReindexStmt *reindexStatement, const char *reindexCommand) #endif state.locked_table_oid = InvalidOid; - indOid = RangeVarGetRelidInternal(reindexStatement->relation, + indOid = RangeVarGetRelidExtended(reindexStatement->relation, lockmode, 0, RangeVarCallbackForReindexIndex, &state); @@ -243,7 +243,7 @@ PlanReindexStmt(ReindexStmt *reindexStatement, const char *reindexCommand) } else { - RangeVarGetRelidInternal(reindexStatement->relation, lockmode, 0, + RangeVarGetRelidExtended(reindexStatement->relation, lockmode, 0, RangeVarCallbackOwnsTable, NULL); relation = heap_openrv(reindexStatement->relation, NoLock); @@ -349,7 +349,7 @@ PlanDropIndexStmt(DropStmt *dropIndexStatement, const char *dropIndexCommand) state.heapOid = InvalidOid; state.concurrent = dropIndexStatement->concurrent; - indexId = RangeVarGetRelidInternal(rangeVar, lockmode, rvrFlags, + indexId = RangeVarGetRelidExtended(rangeVar, lockmode, rvrFlags, RangeVarCallbackForDropIndex, (void *) &state); @@ -654,10 +654,8 @@ RangeVarCallbackForDropIndex(const RangeVar *rel, Oid relOid, Oid oldRelOid, voi */ expected_relkind = classform->relkind; -#if PG_VERSION_NUM >= 110000 if (expected_relkind == RELKIND_PARTITIONED_INDEX) expected_relkind = RELKIND_INDEX; -#endif if (expected_relkind != relkind) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -667,7 +665,7 @@ RangeVarCallbackForDropIndex(const RangeVar *rel, Oid relOid, Oid oldRelOid, voi if (!pg_class_ownercheck(relOid, GetUserId()) && !pg_namespace_ownercheck(classform->relnamespace, GetUserId())) { - aclcheck_error(ACLCHECK_NOT_OWNER, ACLCHECK_OBJECT_INDEX, rel->relname); + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, rel->relname); } if (!allowSystemTableMods && IsSystemClass(relOid, classform)) @@ -747,11 +745,7 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, Oid relId, Oid oldRelI relkind = get_rel_relkind(relId); if (!relkind) return; - if (relkind != RELKIND_INDEX -#if PG_VERSION_NUM >= 110000 - && relkind != RELKIND_PARTITIONED_INDEX -#endif - ) + if (relkind != RELKIND_INDEX && relkind != RELKIND_PARTITIONED_INDEX) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", relation->relname))); diff --git a/src/backend/distributed/commands/schema.c b/src/backend/distributed/commands/schema.c index fd95883b1..158053c5c 100644 --- a/src/backend/distributed/commands/schema.c +++ b/src/backend/distributed/commands/schema.c @@ -121,9 +121,7 @@ PlanAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryString) return PlanAlterTypeSchemaStmt(stmt, queryString); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return PlanAlterFunctionSchemaStmt(stmt, queryString); @@ -198,9 +196,7 @@ ProcessAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryStrin return; } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { ProcessAlterFunctionSchemaStmt(stmt, queryString); diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index f1068e853..ae97f7796 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -857,7 +857,7 @@ ErrorIfUnsupportedConstraint(Relation relation, char distributionMethod, } attributeCount = indexInfo->ii_NumIndexAttrs; - attributeNumberArray = IndexInfoAttributeNumberArray(indexInfo); + attributeNumberArray = indexInfo->ii_IndexAttrNumbers; for (attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) { diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index 8c5c78e4a..b821bf589 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -211,7 +211,7 @@ multi_ProcessUtility(PlannedStmt *pstmt, parsetree = ProcessCreateSubscriptionStmt(createSubStmt); } -#if (PG_VERSION_NUM >= 110000) + if (IsA(parsetree, CallStmt)) { CallStmt *callStmt = (CallStmt *) parsetree; @@ -253,7 +253,6 @@ multi_ProcessUtility(PlannedStmt *pstmt, return; } -#endif if (IsA(parsetree, DoStmt)) { @@ -426,9 +425,7 @@ multi_ProcessUtility(PlannedStmt *pstmt, break; } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { ddlJobs = PlanDropFunctionStmt(dropStatement, queryString); @@ -484,9 +481,7 @@ multi_ProcessUtility(PlannedStmt *pstmt, break; } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { ddlJobs = PlanRenameFunctionStmt(renameStmt, queryString); @@ -843,9 +838,7 @@ PlanAlterOwnerStmt(AlterOwnerStmt *stmt, const char *queryString) return PlanAlterTypeOwnerStmt(stmt, queryString); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return PlanAlterFunctionOwnerStmt(stmt, queryString); @@ -871,9 +864,7 @@ PlanAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, const char *queryString { switch (stmt->objectType) { -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return PlanAlterFunctionDependsStmt(stmt, queryString); diff --git a/src/backend/distributed/commands/vacuum.c b/src/backend/distributed/commands/vacuum.c index e779242e6..32024254c 100644 --- a/src/backend/distributed/commands/vacuum.c +++ b/src/backend/distributed/commands/vacuum.c @@ -44,6 +44,8 @@ static List * VacuumTaskList(Oid relationId, CitusVacuumParams vacuumParams, List *vacuumColumnList); static StringInfo DeparseVacuumStmtPrefix(CitusVacuumParams vacuumParams); static char * DeparseVacuumColumnNames(List *columnNameList); +static List * VacuumColumnList(VacuumStmt *vacuumStmt, int relationIndex); +static List * ExtractVacuumTargetRels(VacuumStmt *vacuumStmt); static CitusVacuumParams VacuumStmtParams(VacuumStmt *vacstmt); /* @@ -379,6 +381,40 @@ DeparseVacuumColumnNames(List *columnNameList) } +/* + * VacuumColumnList returns list of columns from relation + * in the vacuum statement at specified relationIndex. + */ +static List * +VacuumColumnList(VacuumStmt *vacuumStmt, int relationIndex) +{ + VacuumRelation *vacuumRelation = (VacuumRelation *) list_nth(vacuumStmt->rels, + relationIndex); + + return vacuumRelation->va_cols; +} + + +/* + * ExtractVacuumTargetRels returns list of target + * relations from vacuum statement. + */ +static List * +ExtractVacuumTargetRels(VacuumStmt *vacuumStmt) +{ + List *vacuumList = NIL; + + ListCell *vacuumRelationCell = NULL; + foreach(vacuumRelationCell, vacuumStmt->rels) + { + VacuumRelation *vacuumRelation = (VacuumRelation *) lfirst(vacuumRelationCell); + vacuumList = lappend(vacuumList, vacuumRelation->relation); + } + + return vacuumList; +} + + /* * VacuumStmtParams returns a CitusVacuumParams based on the supplied VacuumStmt. */ diff --git a/src/backend/distributed/deparser/deparse.c b/src/backend/distributed/deparser/deparse.c index c57b94629..d9b814c46 100644 --- a/src/backend/distributed/deparser/deparse.c +++ b/src/backend/distributed/deparser/deparse.c @@ -120,9 +120,7 @@ DeparseDropStmt(DropStmt *stmt) return DeparseDropTypeStmt(stmt); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return DeparseDropFunctionStmt(stmt); @@ -188,9 +186,7 @@ DeparseRenameStmt(RenameStmt *stmt) return DeparseRenameAttributeStmt(stmt); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return DeparseRenameFunctionStmt(stmt); @@ -243,9 +239,7 @@ DeparseAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) return DeparseAlterTypeSchemaStmt(stmt); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return DeparseAlterFunctionSchemaStmt(stmt); @@ -277,9 +271,7 @@ DeparseAlterOwnerStmt(AlterOwnerStmt *stmt) return DeparseAlterTypeOwnerStmt(stmt); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return DeparseAlterFunctionOwnerStmt(stmt); @@ -306,9 +298,7 @@ DeparseAlterObjectDependsStmt(AlterObjectDependsStmt *stmt) { switch (stmt->objectType) { -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return DeparseAlterFunctionDependsStmt(stmt); diff --git a/src/backend/distributed/deparser/deparse_function_stmts.c b/src/backend/distributed/deparser/deparse_function_stmts.c index a11632a91..ccc114f11 100644 --- a/src/backend/distributed/deparser/deparse_function_stmts.c +++ b/src/backend/distributed/deparser/deparse_function_stmts.c @@ -86,11 +86,6 @@ AppendAlterFunctionStmt(StringInfo buf, AlterFunctionStmt *stmt) { ListCell *actionCell = NULL; -#if (PG_VERSION_NUM < 110000) - appendStringInfo(buf, "ALTER FUNCTION "); - - AppendFunctionName(buf, stmt->func, OBJECT_FUNCTION); -#else if (stmt->objtype == OBJECT_FUNCTION) { appendStringInfo(buf, "ALTER FUNCTION "); @@ -101,7 +96,6 @@ AppendAlterFunctionStmt(StringInfo buf, AlterFunctionStmt *stmt) } AppendFunctionName(buf, stmt->func, stmt->objtype); -#endif foreach(actionCell, stmt->actions) @@ -304,11 +298,7 @@ DeparseRenameFunctionStmt(RenameStmt *stmt) StringInfoData str = { 0 }; initStringInfo(&str); -#if (PG_VERSION_NUM < 110000) - Assert(stmt->renameType == OBJECT_FUNCTION); -#else Assert(stmt->renameType == OBJECT_FUNCTION || stmt->renameType == OBJECT_PROCEDURE); -#endif AppendRenameFunctionStmt(&str, stmt); @@ -324,9 +314,6 @@ AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt) { ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object); -#if (PG_VERSION_NUM < 110000) - appendStringInfo(buf, "ALTER FUNCTION "); -#else if (stmt->renameType == OBJECT_FUNCTION) { appendStringInfoString(buf, "ALTER FUNCTION "); @@ -335,7 +322,6 @@ AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt) { appendStringInfoString(buf, "ALTER PROCEDURE "); } -#endif AppendFunctionName(buf, func, stmt->renameType); @@ -352,11 +338,7 @@ DeparseAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt) StringInfoData str = { 0 }; initStringInfo(&str); -#if (PG_VERSION_NUM < 110000) - Assert(stmt->objectType == OBJECT_FUNCTION); -#else Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE); -#endif AppendAlterFunctionSchemaStmt(&str, stmt); @@ -372,9 +354,6 @@ AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt) { ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object); -#if (PG_VERSION_NUM < 110000) - appendStringInfo(buf, "ALTER FUNCTION "); -#else if (stmt->objectType == OBJECT_FUNCTION) { appendStringInfoString(buf, "ALTER FUNCTION "); @@ -383,7 +362,6 @@ AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt) { appendStringInfoString(buf, "ALTER PROCEDURE "); } -#endif AppendFunctionName(buf, func, stmt->objectType); appendStringInfo(buf, " SET SCHEMA %s;", quote_identifier(stmt->newschema)); @@ -399,11 +377,7 @@ DeparseAlterFunctionOwnerStmt(AlterOwnerStmt *stmt) StringInfoData str = { 0 }; initStringInfo(&str); -#if (PG_VERSION_NUM < 110000) - Assert(stmt->objectType == OBJECT_FUNCTION); -#else Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE); -#endif AppendAlterFunctionOwnerStmt(&str, stmt); @@ -419,9 +393,6 @@ AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt) { ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object); -#if (PG_VERSION_NUM < 110000) - appendStringInfo(buf, "ALTER FUNCTION "); -#else if (stmt->objectType == OBJECT_FUNCTION) { appendStringInfoString(buf, "ALTER FUNCTION "); @@ -430,7 +401,6 @@ AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt) { appendStringInfoString(buf, "ALTER PROCEDURE "); } -#endif AppendFunctionName(buf, func, stmt->objectType); appendStringInfo(buf, " OWNER TO %s;", RoleSpecString(stmt->newowner)); @@ -446,11 +416,7 @@ DeparseAlterFunctionDependsStmt(AlterObjectDependsStmt *stmt) StringInfoData str = { 0 }; initStringInfo(&str); -#if (PG_VERSION_NUM < 110000) - Assert(stmt->objectType == OBJECT_FUNCTION); -#else Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE); -#endif AppendAlterFunctionDependsStmt(&str, stmt); @@ -466,9 +432,6 @@ AppendAlterFunctionDependsStmt(StringInfo buf, AlterObjectDependsStmt *stmt) { ObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object); -#if (PG_VERSION_NUM < 110000) - appendStringInfo(buf, "ALTER FUNCTION "); -#else if (stmt->objectType == OBJECT_FUNCTION) { appendStringInfoString(buf, "ALTER FUNCTION "); @@ -477,7 +440,6 @@ AppendAlterFunctionDependsStmt(StringInfo buf, AlterObjectDependsStmt *stmt) { appendStringInfoString(buf, "ALTER PROCEDURE "); } -#endif AppendFunctionName(buf, func, stmt->objectType); appendStringInfo(buf, " DEPENDS ON EXTENSION %s;", strVal(stmt->extname)); @@ -493,11 +455,7 @@ DeparseDropFunctionStmt(DropStmt *stmt) StringInfoData str = { 0 }; initStringInfo(&str); -#if (PG_VERSION_NUM < 110000) - Assert(stmt->removeType == OBJECT_FUNCTION); -#else Assert(stmt->removeType == OBJECT_FUNCTION || stmt->removeType == OBJECT_PROCEDURE); -#endif AppendDropFunctionStmt(&str, stmt); @@ -511,9 +469,6 @@ DeparseDropFunctionStmt(DropStmt *stmt) static void AppendDropFunctionStmt(StringInfo buf, DropStmt *stmt) { -#if (PG_VERSION_NUM < 110000) - appendStringInfo(buf, "DROP FUNCTION "); -#else if (stmt->removeType == OBJECT_FUNCTION) { appendStringInfoString(buf, "DROP FUNCTION "); @@ -522,7 +477,6 @@ AppendDropFunctionStmt(StringInfo buf, DropStmt *stmt) { appendStringInfoString(buf, "DROP PROCEDURE "); } -#endif if (stmt->missing_ok) { @@ -576,7 +530,7 @@ AppendFunctionName(StringInfo buf, ObjectWithArgs *func, ObjectType objtype) char *schemaName = NULL; char *qualifiedFunctionName; - funcid = LookupFuncWithArgsCompat(objtype, func, true); + funcid = LookupFuncWithArgs(objtype, func, true); proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) diff --git a/src/backend/distributed/deparser/objectaddress.c b/src/backend/distributed/deparser/objectaddress.c index 2a6f2975e..fb31e06b4 100644 --- a/src/backend/distributed/deparser/objectaddress.c +++ b/src/backend/distributed/deparser/objectaddress.c @@ -143,9 +143,7 @@ RenameStmtObjectAddress(RenameStmt *stmt, bool missing_ok) return RenameAttributeStmtObjectAddress(stmt, missing_ok); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return RenameFunctionStmtObjectAddress(stmt, missing_ok); @@ -170,9 +168,7 @@ AlterObjectSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, bool missing_ok) return AlterTypeSchemaStmtObjectAddress(stmt, missing_ok); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return AlterFunctionSchemaStmtObjectAddress(stmt, missing_ok); @@ -218,9 +214,7 @@ AlterOwnerStmtObjectAddress(AlterOwnerStmt *stmt, bool missing_ok) return AlterTypeOwnerObjectAddress(stmt, missing_ok); } -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return AlterFunctionOwnerObjectAddress(stmt, missing_ok); @@ -250,9 +244,7 @@ AlterObjectDependsStmtObjectAddress(AlterObjectDependsStmt *stmt, bool missing_o { switch (stmt->objectType) { -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif case OBJECT_FUNCTION: { return AlterFunctionDependsStmtObjectAddress(stmt, missing_ok); diff --git a/src/backend/distributed/deparser/qualify.c b/src/backend/distributed/deparser/qualify.c index 6e5ab94ef..0406d1c9b 100644 --- a/src/backend/distributed/deparser/qualify.c +++ b/src/backend/distributed/deparser/qualify.c @@ -126,12 +126,10 @@ QualifyRenameStmt(RenameStmt *stmt) } case OBJECT_FUNCTION: -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif - { - QualifyRenameFunctionStmt(stmt); - } + { + QualifyRenameFunctionStmt(stmt); + } default: { @@ -199,12 +197,10 @@ QualifyAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) } case OBJECT_FUNCTION: -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif - { - QualifyAlterFunctionSchemaStmt(stmt); - } + { + QualifyAlterFunctionSchemaStmt(stmt); + } default: { @@ -227,12 +223,10 @@ QualifyAlterOwnerStmt(AlterOwnerStmt *stmt) } case OBJECT_FUNCTION: -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif - { - QualifyAlterFunctionOwnerStmt(stmt); - } + { + QualifyAlterFunctionOwnerStmt(stmt); + } default: { @@ -248,12 +242,10 @@ QualifyAlterObjectDependsStmt(AlterObjectDependsStmt *stmt) switch (stmt->objectType) { case OBJECT_FUNCTION: -#if PG_VERSION_NUM >= 110000 case OBJECT_PROCEDURE: -#endif - { - QualifyAlterFunctionDependsStmt(stmt); - } + { + QualifyAlterFunctionDependsStmt(stmt); + } default: { diff --git a/src/backend/distributed/deparser/qualify_function_stmt.c b/src/backend/distributed/deparser/qualify_function_stmt.c index d9e635329..f2481fbd1 100644 --- a/src/backend/distributed/deparser/qualify_function_stmt.c +++ b/src/backend/distributed/deparser/qualify_function_stmt.c @@ -43,13 +43,7 @@ void QualifyFunctionSchemaName(ObjectWithArgs *func, ObjectType type); void QualifyAlterFunctionStmt(AlterFunctionStmt *stmt) { - ObjectType objtype = OBJECT_FUNCTION; - -#if (PG_VERSION_NUM >= 110000) - objtype = stmt->objtype; -#endif - - QualifyFunction(stmt->func, objtype); + QualifyFunction(stmt->func, stmt->objtype); } @@ -61,11 +55,7 @@ QualifyAlterFunctionStmt(AlterFunctionStmt *stmt) void QualifyRenameFunctionStmt(RenameStmt *stmt) { -#if (PG_VERSION_NUM < 110000) - Assert(stmt->renameType == OBJECT_FUNCTION); -#else Assert(stmt->renameType == OBJECT_FUNCTION || stmt->renameType == OBJECT_PROCEDURE); -#endif QualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->renameType); } @@ -79,11 +69,7 @@ QualifyRenameFunctionStmt(RenameStmt *stmt) void QualifyAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt) { -#if (PG_VERSION_NUM < 110000) - Assert(stmt->objectType == OBJECT_FUNCTION); -#else Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE); -#endif QualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType); } @@ -97,11 +83,7 @@ QualifyAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt) void QualifyAlterFunctionOwnerStmt(AlterOwnerStmt *stmt) { -#if (PG_VERSION_NUM < 110000) - Assert(stmt->objectType == OBJECT_FUNCTION); -#else Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE); -#endif QualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType); } @@ -115,11 +97,7 @@ QualifyAlterFunctionOwnerStmt(AlterOwnerStmt *stmt) void QualifyAlterFunctionDependsStmt(AlterObjectDependsStmt *stmt) { -#if (PG_VERSION_NUM < 110000) - Assert(stmt->objectType == OBJECT_FUNCTION); -#else Assert(stmt->objectType == OBJECT_FUNCTION || stmt->objectType == OBJECT_PROCEDURE); -#endif QualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType); } @@ -156,7 +134,7 @@ QualifyFunctionSchemaName(ObjectWithArgs *func, ObjectType type) Oid funcid = InvalidOid; HeapTuple proctup; - funcid = LookupFuncWithArgsCompat(type, func, true); + funcid = LookupFuncWithArgs(type, func, true); proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); /* diff --git a/src/backend/distributed/executor/multi_router_executor.c b/src/backend/distributed/executor/multi_router_executor.c index a7d5c8215..8cc2b46b0 100644 --- a/src/backend/distributed/executor/multi_router_executor.c +++ b/src/backend/distributed/executor/multi_router_executor.c @@ -684,15 +684,9 @@ SortTupleStore(CitusScanState *scanState) sortKeyIndex++; } -#if (PG_VERSION_NUM >= 110000) tuplesortstate = tuplesort_begin_heap(tupleDescriptor, numberOfSortKeys, sortColIdx, sortOperators, collations, nullsFirst, work_mem, NULL, false); -#else - tuplesortstate = - tuplesort_begin_heap(tupleDescriptor, numberOfSortKeys, sortColIdx, sortOperators, - collations, nullsFirst, work_mem, false); -#endif while (true) { diff --git a/src/backend/distributed/master/master_metadata_utility.c b/src/backend/distributed/master/master_metadata_utility.c index f8e2f71a3..5d0052bb2 100644 --- a/src/backend/distributed/master/master_metadata_utility.c +++ b/src/backend/distributed/master/master_metadata_utility.c @@ -1362,7 +1362,7 @@ EnsureTablePermissions(Oid relationId, AclMode mode) if (aclresult != ACLCHECK_OK) { - aclcheck_error(aclresult, ACLCHECK_OBJECT_TABLE, get_rel_name(relationId)); + aclcheck_error(aclresult, OBJECT_TABLE, get_rel_name(relationId)); } } @@ -1376,7 +1376,7 @@ EnsureTableOwner(Oid relationId) { if (!pg_class_ownercheck(relationId, GetUserId())) { - aclcheck_error(ACLCHECK_NOT_OWNER, ACLCHECK_OBJECT_TABLE, + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TABLE, get_rel_name(relationId)); } } @@ -1391,7 +1391,7 @@ EnsureSchemaOwner(Oid schemaId) { if (!pg_namespace_ownercheck(schemaId, GetUserId())) { - aclcheck_error(ACLCHECK_NOT_OWNER, ACLCHECK_OBJECT_SCHEMA, + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA, get_namespace_name(schemaId)); } } @@ -1406,7 +1406,7 @@ EnsureSequenceOwner(Oid sequenceOid) { if (!pg_class_ownercheck(sequenceOid, GetUserId())) { - aclcheck_error(ACLCHECK_NOT_OWNER, ACLCHECK_OBJECT_SEQUENCE, + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE, get_rel_name(sequenceOid)); } } diff --git a/src/backend/distributed/master/master_node_protocol.c b/src/backend/distributed/master/master_node_protocol.c index 245af72f3..e2b9564a1 100644 --- a/src/backend/distributed/master/master_node_protocol.c +++ b/src/backend/distributed/master/master_node_protocol.c @@ -33,9 +33,6 @@ #include "catalog/namespace.h" #include "catalog/pg_class.h" #include "catalog/pg_constraint.h" -#if (PG_VERSION_NUM < 110000) -#include "catalog/pg_constraint_fn.h" -#endif #include "catalog/pg_index.h" #include "catalog/pg_type.h" #include "catalog/pg_namespace.h" diff --git a/src/backend/distributed/master/master_stage_protocol.c b/src/backend/distributed/master/master_stage_protocol.c index a809ceccb..9015ee7f8 100644 --- a/src/backend/distributed/master/master_stage_protocol.c +++ b/src/backend/distributed/master/master_stage_protocol.c @@ -1108,8 +1108,7 @@ WorkerShardStats(ShardPlacement *placement, Oid relationId, char *shardName, /* fill in the partition column name and shard name in the query. */ partitionColumn = PartitionColumn(relationId, unusedTableId); - partitionColumnName = get_attname_internal(relationId, partitionColumn->varattno, - false); + partitionColumnName = get_attname(relationId, partitionColumn->varattno, false); appendStringInfo(partitionValueQuery, SHARD_RANGE_QUERY, partitionColumnName, partitionColumnName, shardName); diff --git a/src/backend/distributed/metadata/distobject.c b/src/backend/distributed/metadata/distobject.c index ca1873364..fc029b622 100644 --- a/src/backend/distributed/metadata/distobject.c +++ b/src/backend/distributed/metadata/distobject.c @@ -276,9 +276,7 @@ ClusterHasDistributedFunctionWithDistArgument(void) Relation pgDistObjectRel = heap_open(DistObjectRelationId(), AccessShareLock); -#if (PG_VERSION_NUM >= 110000) TupleDesc tupleDescriptor = RelationGetDescr(pgDistObjectRel); -#endif pgDistObjectScan = systable_beginscan(pgDistObjectRel, InvalidOid, false, NULL, 0, NULL); @@ -290,16 +288,10 @@ ClusterHasDistributedFunctionWithDistArgument(void) if (pg_dist_object->classid == ProcedureRelationId) { bool distArgumentIsNull = false; -#if (PG_VERSION_NUM >= 110000) distArgumentIsNull = heap_attisnull(pgDistObjectTup, Anum_pg_dist_object_distribution_argument_index, tupleDescriptor); -#else - distArgumentIsNull = - heap_attisnull(pgDistObjectTup, - Anum_pg_dist_object_distribution_argument_index); -#endif /* we found one distributed function that has an distribution argument */ if (!distArgumentIsNull) diff --git a/src/backend/distributed/planner/distributed_planner.c b/src/backend/distributed/planner/distributed_planner.c index 7fa816bbf..764c65977 100644 --- a/src/backend/distributed/planner/distributed_planner.c +++ b/src/backend/distributed/planner/distributed_planner.c @@ -1740,17 +1740,9 @@ HasUnresolvedExternParamsWalker(Node *expression, ParamListInfo boundParams) /* give hook a chance in case parameter is dynamic */ if (boundParams->paramFetch != NULL) { -#if (PG_VERSION_NUM >= 110000) ParamExternData externParamPlaceholder; externParam = (*boundParams->paramFetch)(boundParams, paramId, false, &externParamPlaceholder); -#else - externParam = &boundParams->params[paramId - 1]; - if (!OidIsValid(externParam->ptype)) - { - (*boundParams->paramFetch)(boundParams, paramId); - } -#endif } else { diff --git a/src/backend/distributed/planner/multi_explain.c b/src/backend/distributed/planner/multi_explain.c index 4b42b6280..3a9f9a72e 100644 --- a/src/backend/distributed/planner/multi_explain.c +++ b/src/backend/distributed/planner/multi_explain.c @@ -91,15 +91,6 @@ static void ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv); -#if (PG_VERSION_NUM < 110000) -static void ExplainOpenGroup(const char *objtype, const char *labelname, - bool labeled, ExplainState *es); -static void ExplainCloseGroup(const char *objtype, const char *labelname, - bool labeled, ExplainState *es); -static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es); -static void ExplainJSONLineEnding(ExplainState *es); -static void ExplainYAMLLineStarting(ExplainState *es); -#endif /* @@ -230,7 +221,7 @@ ExplainJob(Job *job, ExplainState *es) ExplainOpenGroup("Job", "Job", true, es); - ExplainPropertyIntegerInternal("Task Count", NULL, taskCount, es); + ExplainPropertyInteger("Task Count", NULL, taskCount, es); if (dependedJobCount > 0) { @@ -306,8 +297,8 @@ ExplainMapMergeJob(MapMergeJob *mapMergeJob, ExplainState *es) } ExplainOpenGroup("MapMergeJob", NULL, true, es); - ExplainPropertyIntegerInternal("Map Task Count", NULL, mapTaskCount, es); - ExplainPropertyIntegerInternal("Merge Task Count", NULL, mergeTaskCount, es); + ExplainPropertyInteger("Map Task Count", NULL, mapTaskCount, es); + ExplainPropertyInteger("Merge Task Count", NULL, mergeTaskCount, es); if (dependedJobCount > 0) { @@ -649,13 +640,10 @@ ExplainOneQuery(Query *query, int cursorOptions, { /* if an advisor plugin is present, let it manage things */ if (ExplainOneQuery_hook) -#if (PG_VERSION_NUM >= 110000) + { (*ExplainOneQuery_hook) (query, cursorOptions, into, es, queryString, params, queryEnv); -#elif (PG_VERSION_NUM >= 100000) - (*ExplainOneQuery_hook) (query, cursorOptions, into, es, - queryString, params); -#endif + } else { PlannedStmt *plan; @@ -675,182 +663,3 @@ ExplainOneQuery(Query *query, int cursorOptions, &planduration); } } - -#if (PG_VERSION_NUM < 110000) -/* - * Open a group of related objects. - * - * objtype is the type of the group object, labelname is its label within - * a containing object (if any). - * - * If labeled is true, the group members will be labeled properties, - * while if it's false, they'll be unlabeled objects. - */ -static void -ExplainOpenGroup(const char *objtype, const char *labelname, - bool labeled, ExplainState *es) -{ - switch (es->format) - { - case EXPLAIN_FORMAT_TEXT: - /* nothing to do */ - break; - - case EXPLAIN_FORMAT_XML: - ExplainXMLTag(objtype, X_OPENING, es); - es->indent++; - break; - - case EXPLAIN_FORMAT_JSON: - ExplainJSONLineEnding(es); - appendStringInfoSpaces(es->str, 2 * es->indent); - if (labelname) - { - escape_json(es->str, labelname); - appendStringInfoString(es->str, ": "); - } - appendStringInfoChar(es->str, labeled ? '{' : '['); - - /* - * In JSON format, the grouping_stack is an integer list. 0 means - * we've emitted nothing at this grouping level, 1 means we've - * emitted something (and so the next item needs a comma). See - * ExplainJSONLineEnding(). - */ - es->grouping_stack = lcons_int(0, es->grouping_stack); - es->indent++; - break; - - case EXPLAIN_FORMAT_YAML: - - /* - * In YAML format, the grouping stack is an integer list. 0 means - * we've emitted nothing at this grouping level AND this grouping - * level is unlabelled and must be marked with "- ". See - * ExplainYAMLLineStarting(). - */ - ExplainYAMLLineStarting(es); - if (labelname) - { - appendStringInfo(es->str, "%s: ", labelname); - es->grouping_stack = lcons_int(1, es->grouping_stack); - } - else - { - appendStringInfoString(es->str, "- "); - es->grouping_stack = lcons_int(0, es->grouping_stack); - } - es->indent++; - break; - } -} - - -/* - * Close a group of related objects. - * Parameters must match the corresponding ExplainOpenGroup call. - */ -static void -ExplainCloseGroup(const char *objtype, const char *labelname, - bool labeled, ExplainState *es) -{ - switch (es->format) - { - case EXPLAIN_FORMAT_TEXT: - /* nothing to do */ - break; - - case EXPLAIN_FORMAT_XML: - es->indent--; - ExplainXMLTag(objtype, X_CLOSING, es); - break; - - case EXPLAIN_FORMAT_JSON: - es->indent--; - appendStringInfoChar(es->str, '\n'); - appendStringInfoSpaces(es->str, 2 * es->indent); - appendStringInfoChar(es->str, labeled ? '}' : ']'); - es->grouping_stack = list_delete_first(es->grouping_stack); - break; - - case EXPLAIN_FORMAT_YAML: - es->indent--; - es->grouping_stack = list_delete_first(es->grouping_stack); - break; - } -} - - -/* - * Emit opening or closing XML tag. - * - * "flags" must contain X_OPENING, X_CLOSING, or X_CLOSE_IMMEDIATE. - * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally - * add. - * - * XML tag names can't contain white space, so we replace any spaces in - * "tagname" with dashes. - */ -static void -ExplainXMLTag(const char *tagname, int flags, ExplainState *es) -{ - const char *s; - - if ((flags & X_NOWHITESPACE) == 0) - appendStringInfoSpaces(es->str, 2 * es->indent); - appendStringInfoCharMacro(es->str, '<'); - if ((flags & X_CLOSING) != 0) - appendStringInfoCharMacro(es->str, '/'); - for (s = tagname; *s; s++) - appendStringInfoCharMacro(es->str, (*s == ' ') ? '-' : *s); - if ((flags & X_CLOSE_IMMEDIATE) != 0) - appendStringInfoString(es->str, " /"); - appendStringInfoCharMacro(es->str, '>'); - if ((flags & X_NOWHITESPACE) == 0) - appendStringInfoCharMacro(es->str, '\n'); -} - - -/* - * Emit a JSON line ending. - * - * JSON requires a comma after each property but the last. To facilitate this, - * in JSON format, the text emitted for each property begins just prior to the - * preceding line-break (and comma, if applicable). - */ -static void -ExplainJSONLineEnding(ExplainState *es) -{ - Assert(es->format == EXPLAIN_FORMAT_JSON); - if (linitial_int(es->grouping_stack) != 0) - appendStringInfoChar(es->str, ','); - else - linitial_int(es->grouping_stack) = 1; - appendStringInfoChar(es->str, '\n'); -} - - -/* - * Indent a YAML line. - * - * YAML lines are ordinarily indented by two spaces per indentation level. - * The text emitted for each property begins just prior to the preceding - * line-break, except for the first property in an unlabelled group, for which - * it begins immediately after the "- " that introduces the group. The first - * property of the group appears on the same line as the opening "- ". - */ -static void -ExplainYAMLLineStarting(ExplainState *es) -{ - Assert(es->format == EXPLAIN_FORMAT_YAML); - if (linitial_int(es->grouping_stack) == 0) - { - linitial_int(es->grouping_stack) = 1; - } - else - { - appendStringInfoChar(es->str, '\n'); - appendStringInfoSpaces(es->str, es->indent * 2); - } -} -#endif diff --git a/src/backend/distributed/planner/multi_logical_planner.c b/src/backend/distributed/planner/multi_logical_planner.c index e04fc4dc8..d464f5009 100644 --- a/src/backend/distributed/planner/multi_logical_planner.c +++ b/src/backend/distributed/planner/multi_logical_planner.c @@ -1358,8 +1358,7 @@ ExtractFromExpressionWalker(Node *node, QualifierWalkerContext *walkerContext) { /* this part of code only run for subqueries */ Node *joinClause = eval_const_expressions(NULL, joinQualifiersNode); - joinClause = (Node *) canonicalize_qual_compat((Expr *) joinClause, - false); + joinClause = (Node *) canonicalize_qual((Expr *) joinClause, false); joinQualifierList = make_ands_implicit((Expr *) joinClause); } } @@ -1392,8 +1391,7 @@ ExtractFromExpressionWalker(Node *node, QualifierWalkerContext *walkerContext) { /* this part of code only run for subqueries */ Node *fromClause = eval_const_expressions(NULL, fromQualifiersNode); - fromClause = (Node *) canonicalize_qual_compat((Expr *) fromClause, - false); + fromClause = (Node *) canonicalize_qual((Expr *) fromClause, false); fromQualifierList = make_ands_implicit((Expr *) fromClause); } diff --git a/src/backend/distributed/planner/multi_physical_planner.c b/src/backend/distributed/planner/multi_physical_planner.c index 40bd3cd11..4d64e2e83 100644 --- a/src/backend/distributed/planner/multi_physical_planner.c +++ b/src/backend/distributed/planner/multi_physical_planner.c @@ -4375,7 +4375,7 @@ ColumnName(Var *column, List *rangeTableList) else if (rangeTableKind == CITUS_RTE_RELATION) { Oid relationId = rangeTableEntry->relid; - columnName = get_attname_internal(relationId, columnNumber, false); + columnName = get_attname(relationId, columnNumber, false); } Assert(columnName != NULL); diff --git a/src/backend/distributed/relay/relay_event_utility.c b/src/backend/distributed/relay/relay_event_utility.c index 623c64ece..6764786f2 100644 --- a/src/backend/distributed/relay/relay_event_utility.c +++ b/src/backend/distributed/relay/relay_event_utility.c @@ -29,9 +29,6 @@ #include "catalog/namespace.h" #include "catalog/pg_class.h" #include "catalog/pg_constraint.h" -#if (PG_VERSION_NUM < 110000) -#include "catalog/pg_constraint_fn.h" -#endif #include "distributed/commands.h" #include "distributed/metadata_cache.h" #include "distributed/relay_utility.h" @@ -318,7 +315,7 @@ RelayEventExtendNames(Node *parseTree, char *schemaName, uint64 shardId) { GrantStmt *grantStmt = (GrantStmt *) parseTree; if (grantStmt->targtype == ACL_TARGET_OBJECT && - grantStmt->objtype == RELATION_OBJECT_TYPE) + grantStmt->objtype == OBJECT_TABLE) { ListCell *lc; diff --git a/src/backend/distributed/utils/acquire_lock.c b/src/backend/distributed/utils/acquire_lock.c index 59ea9fd20..3a954f433 100644 --- a/src/backend/distributed/utils/acquire_lock.c +++ b/src/backend/distributed/utils/acquire_lock.c @@ -78,9 +78,7 @@ StartLockAcquireHelperBackgroundWorker(int backendToHelp, int32 lock_cooldown) snprintf(worker.bgw_name, BGW_MAXLEN, "Citus Lock Acquire Helper: %d/%u", backendToHelp, MyDatabaseId); -#if PG_VERSION_NUM >= 110000 snprintf(worker.bgw_type, BGW_MAXLEN, "citus_lock_aqcuire"); -#endif worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; diff --git a/src/backend/distributed/utils/citus_ruleutils.c b/src/backend/distributed/utils/citus_ruleutils.c index 54ea1f106..72b682172 100644 --- a/src/backend/distributed/utils/citus_ruleutils.c +++ b/src/backend/distributed/utils/citus_ruleutils.c @@ -694,8 +694,6 @@ deparse_shard_index_statement(IndexStmt *origStmt, Oid distrelid, int64 shardid, deparse_index_columns(buffer, indexStmt->indexParams, deparseContext); appendStringInfoString(buffer, ") "); -#if PG_VERSION_NUM >= 110000 - /* column/expressions for INCLUDE list */ if (indexStmt->indexIncludingParams != NIL) { @@ -703,7 +701,6 @@ deparse_shard_index_statement(IndexStmt *origStmt, Oid distrelid, int64 shardid, deparse_index_columns(buffer, indexStmt->indexIncludingParams, deparseContext); appendStringInfoChar(buffer, ')'); } -#endif AppendStorageParametersToString(buffer, indexStmt->options); diff --git a/src/backend/distributed/utils/distribution_column.c b/src/backend/distributed/utils/distribution_column.c index 8d642cfb3..a565f7c64 100644 --- a/src/backend/distributed/utils/distribution_column.c +++ b/src/backend/distributed/utils/distribution_column.c @@ -206,7 +206,7 @@ ColumnNameToColumn(Oid relationId, char *columnNodeString) columnNumber, relationName))); } - columnName = get_attname_internal(relationId, column->varattno, false); + columnName = get_attname(relationId, column->varattno, false); if (columnName == NULL) { char *relationName = get_rel_name(relationId); diff --git a/src/backend/distributed/utils/multi_partitioning_utils.c b/src/backend/distributed/utils/multi_partitioning_utils.c index 41cc08965..0c42e3125 100644 --- a/src/backend/distributed/utils/multi_partitioning_utils.c +++ b/src/backend/distributed/utils/multi_partitioning_utils.c @@ -13,9 +13,6 @@ #include "catalog/partition.h" #include "catalog/pg_class.h" #include "catalog/pg_inherits.h" -#if (PG_VERSION_NUM < 110000) -#include "catalog/pg_constraint_fn.h" -#endif #include "distributed/citus_ruleutils.h" #include "distributed/colocation_utils.h" #include "distributed/master_metadata_utility.h" diff --git a/src/backend/distributed/utils/resource_lock.c b/src/backend/distributed/utils/resource_lock.c index a6f86281b..34cef18a8 100644 --- a/src/backend/distributed/utils/resource_lock.c +++ b/src/backend/distributed/utils/resource_lock.c @@ -839,7 +839,7 @@ lock_relation_if_exists(PG_FUNCTION_ARGS) relation = makeRangeVarFromNameList(relationNameList); /* lock the relation with the lock mode */ - relationId = RangeVarGetRelidInternal(relation, lockMode, RVR_MISSING_OK, + relationId = RangeVarGetRelidExtended(relation, lockMode, RVR_MISSING_OK, CitusRangeVarCallbackForLockTable, (void *) &lockMode); relationExists = OidIsValid(relationId); @@ -879,13 +879,8 @@ CitusRangeVarCallbackForLockTable(const RangeVar *rangeVar, Oid relationId, aclResult = CitusLockTableAclCheck(relationId, lockmode, GetUserId()); if (aclResult != ACLCHECK_OK) { -#if (PG_VERSION_NUM >= 110000) aclcheck_error(aclResult, get_relkind_objtype(get_rel_relkind(relationId)), rangeVar->relname); -#else - - aclcheck_error(aclResult, ACL_KIND_CLASS, rangeVar->relname); -#endif } } diff --git a/src/backend/distributed/utils/ruleutils_10.c b/src/backend/distributed/utils/ruleutils_10.c deleted file mode 100644 index 31d6ab509..000000000 --- a/src/backend/distributed/utils/ruleutils_10.c +++ /dev/null @@ -1,7937 +0,0 @@ -/*------------------------------------------------------------------------- - * - * ruleutils_10.c - * Functions to convert stored expressions/querytrees back to - * source text - * - * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/backend/distributed/utils/ruleutils_10.c - * - * This needs to be closely in sync with the core code. - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#if (PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 110000) - -#include -#include -#include - -#include "access/amapi.h" -#include "access/htup_details.h" -#include "access/sysattr.h" -#include "catalog/dependency.h" -#include "catalog/indexing.h" -#include "catalog/pg_aggregate.h" -#include "catalog/pg_am.h" -#include "catalog/pg_authid.h" -#include "catalog/pg_collation.h" -#include "catalog/pg_constraint.h" -#include "catalog/pg_depend.h" -#include "catalog/pg_extension.h" -#include "catalog/pg_foreign_data_wrapper.h" -#include "catalog/pg_language.h" -#include "catalog/pg_opclass.h" -#include "catalog/pg_operator.h" -#include "catalog/pg_partitioned_table.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_statistic_ext.h" -#include "catalog/pg_trigger.h" -#include "catalog/pg_type.h" -#include "commands/defrem.h" -#include "commands/extension.h" -#include "commands/tablespace.h" -#include "common/keywords.h" -#include "distributed/citus_nodefuncs.h" -#include "distributed/citus_ruleutils.h" -#include "executor/spi.h" -#include "foreign/foreign.h" -#include "funcapi.h" -#include "mb/pg_wchar.h" -#include "miscadmin.h" -#include "nodes/makefuncs.h" -#include "nodes/nodeFuncs.h" -#include "optimizer/tlist.h" -#include "parser/parse_node.h" -#include "parser/parse_agg.h" -#include "parser/parse_func.h" -#include "parser/parse_node.h" -#include "parser/parse_oper.h" -#include "parser/parser.h" -#include "parser/parsetree.h" -#include "rewrite/rewriteHandler.h" -#include "rewrite/rewriteManip.h" -#include "rewrite/rewriteSupport.h" -#include "utils/array.h" -#include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/hsearch.h" -#include "utils/lsyscache.h" -#include "utils/rel.h" -#include "utils/ruleutils.h" -#include "utils/snapmgr.h" -#include "utils/syscache.h" -#include "utils/tqual.h" -#include "utils/typcache.h" -#include "utils/varlena.h" -#include "utils/xml.h" - - -/* ---------- - * Pretty formatting constants - * ---------- - */ - -/* Indent counts */ -#define PRETTYINDENT_STD 8 -#define PRETTYINDENT_JOIN 4 -#define PRETTYINDENT_VAR 4 - -#define PRETTYINDENT_LIMIT 40 /* wrap limit */ - -/* Pretty flags */ -#define PRETTYFLAG_PAREN 1 -#define PRETTYFLAG_INDENT 2 - -/* Default line length for pretty-print wrapping: 0 means wrap always */ -#define WRAP_COLUMN_DEFAULT 0 - -/* macro to test if pretty action needed */ -#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN) -#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT) - - -/* ---------- - * Local data types - * ---------- - */ - -/* Context info needed for invoking a recursive querytree display routine */ -typedef struct -{ - StringInfo buf; /* output buffer to append to */ - List *namespaces; /* List of deparse_namespace nodes */ - List *windowClause; /* Current query level's WINDOW clause */ - List *windowTList; /* targetlist for resolving WINDOW clause */ - int prettyFlags; /* enabling of pretty-print functions */ - int wrapColumn; /* max line length, or -1 for no limit */ - int indentLevel; /* current indent level for prettyprint */ - bool varprefix; /* TRUE to print prefixes on Vars */ - Oid distrelid; /* the distributed table being modified, if valid */ - int64 shardid; /* a distributed table's shardid, if positive */ - ParseExprKind special_exprkind; /* set only for exprkinds needing special - * handling */ -} deparse_context; - -/* - * Each level of query context around a subtree needs a level of Var namespace. - * A Var having varlevelsup=N refers to the N'th item (counting from 0) in - * the current context's namespaces list. - * - * The rangetable is the list of actual RTEs from the query tree, and the - * cte list is the list of actual CTEs. - * - * rtable_names holds the alias name to be used for each RTE (either a C - * string, or NULL for nameless RTEs such as unnamed joins). - * rtable_columns holds the column alias names to be used for each RTE. - * - * In some cases we need to make names of merged JOIN USING columns unique - * across the whole query, not only per-RTE. If so, unique_using is TRUE - * and using_names is a list of C strings representing names already assigned - * to USING columns. - * - * When deparsing plan trees, there is always just a single item in the - * deparse_namespace list (since a plan tree never contains Vars with - * varlevelsup > 0). We store the PlanState node that is the immediate - * parent of the expression to be deparsed, as well as a list of that - * PlanState's ancestors. In addition, we store its outer and inner subplan - * state nodes, as well as their plan nodes' targetlists, and the index tlist - * if the current plan node might contain INDEX_VAR Vars. (These fields could - * be derived on-the-fly from the current PlanState, but it seems notationally - * clearer to set them up as separate fields.) - */ -typedef struct -{ - List *rtable; /* List of RangeTblEntry nodes */ - List *rtable_names; /* Parallel list of names for RTEs */ - List *rtable_columns; /* Parallel list of deparse_columns structs */ - List *ctes; /* List of CommonTableExpr nodes */ - /* Workspace for column alias assignment: */ - bool unique_using; /* Are we making USING names globally unique */ - List *using_names; /* List of assigned names for USING columns */ - /* Remaining fields are used only when deparsing a Plan tree: */ - PlanState *planstate; /* immediate parent of current expression */ - List *ancestors; /* ancestors of planstate */ - PlanState *outer_planstate; /* outer subplan state, or NULL if none */ - PlanState *inner_planstate; /* inner subplan state, or NULL if none */ - List *outer_tlist; /* referent for OUTER_VAR Vars */ - List *inner_tlist; /* referent for INNER_VAR Vars */ - List *index_tlist; /* referent for INDEX_VAR Vars */ -} deparse_namespace; - -/* - * Per-relation data about column alias names. - * - * Selecting aliases is unreasonably complicated because of the need to dump - * rules/views whose underlying tables may have had columns added, deleted, or - * renamed since the query was parsed. We must nonetheless print the rule/view - * in a form that can be reloaded and will produce the same results as before. - * - * For each RTE used in the query, we must assign column aliases that are - * unique within that RTE. SQL does not require this of the original query, - * but due to factors such as *-expansion we need to be able to uniquely - * reference every column in a decompiled query. As long as we qualify all - * column references, per-RTE uniqueness is sufficient for that. - * - * However, we can't ensure per-column name uniqueness for unnamed join RTEs, - * since they just inherit column names from their input RTEs, and we can't - * rename the columns at the join level. Most of the time this isn't an issue - * because we don't need to reference the join's output columns as such; we - * can reference the input columns instead. That approach can fail for merged - * JOIN USING columns, however, so when we have one of those in an unnamed - * join, we have to make that column's alias globally unique across the whole - * query to ensure it can be referenced unambiguously. - * - * Another problem is that a JOIN USING clause requires the columns to be - * merged to have the same aliases in both input RTEs, and that no other - * columns in those RTEs or their children conflict with the USING names. - * To handle that, we do USING-column alias assignment in a recursive - * traversal of the query's jointree. When descending through a JOIN with - * USING, we preassign the USING column names to the child columns, overriding - * other rules for column alias assignment. We also mark each RTE with a list - * of all USING column names selected for joins containing that RTE, so that - * when we assign other columns' aliases later, we can avoid conflicts. - * - * Another problem is that if a JOIN's input tables have had columns added or - * deleted since the query was parsed, we must generate a column alias list - * for the join that matches the current set of input columns --- otherwise, a - * change in the number of columns in the left input would throw off matching - * of aliases to columns of the right input. Thus, positions in the printable - * column alias list are not necessarily one-for-one with varattnos of the - * JOIN, so we need a separate new_colnames[] array for printing purposes. - */ -typedef struct -{ - /* - * colnames is an array containing column aliases to use for columns that - * existed when the query was parsed. Dropped columns have NULL entries. - * This array can be directly indexed by varattno to get a Var's name. - * - * Non-NULL entries are guaranteed unique within the RTE, *except* when - * this is for an unnamed JOIN RTE. In that case we merely copy up names - * from the two input RTEs. - * - * During the recursive descent in set_using_names(), forcible assignment - * of a child RTE's column name is represented by pre-setting that element - * of the child's colnames array. So at that stage, NULL entries in this - * array just mean that no name has been preassigned, not necessarily that - * the column is dropped. - */ - int num_cols; /* length of colnames[] array */ - char **colnames; /* array of C strings and NULLs */ - - /* - * new_colnames is an array containing column aliases to use for columns - * that would exist if the query was re-parsed against the current - * definitions of its base tables. This is what to print as the column - * alias list for the RTE. This array does not include dropped columns, - * but it will include columns added since original parsing. Indexes in - * it therefore have little to do with current varattno values. As above, - * entries are unique unless this is for an unnamed JOIN RTE. (In such an - * RTE, we never actually print this array, but we must compute it anyway - * for possible use in computing column names of upper joins.) The - * parallel array is_new_col marks which of these columns are new since - * original parsing. Entries with is_new_col false must match the - * non-NULL colnames entries one-for-one. - */ - int num_new_cols; /* length of new_colnames[] array */ - char **new_colnames; /* array of C strings */ - bool *is_new_col; /* array of bool flags */ - - /* This flag tells whether we should actually print a column alias list */ - bool printaliases; - - /* This list has all names used as USING names in joins above this RTE */ - List *parentUsing; /* names assigned to parent merged columns */ - - /* - * If this struct is for a JOIN RTE, we fill these fields during the - * set_using_names() pass to describe its relationship to its child RTEs. - * - * leftattnos and rightattnos are arrays with one entry per existing - * output column of the join (hence, indexable by join varattno). For a - * simple reference to a column of the left child, leftattnos[i] is the - * child RTE's attno and rightattnos[i] is zero; and conversely for a - * column of the right child. But for merged columns produced by JOIN - * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero. - * Also, if the column has been dropped, both are zero. - * - * If it's a JOIN USING, usingNames holds the alias names selected for the - * merged columns (these might be different from the original USING list, - * if we had to modify names to achieve uniqueness). - */ - int leftrti; /* rangetable index of left child */ - int rightrti; /* rangetable index of right child */ - int *leftattnos; /* left-child varattnos of join cols, or 0 */ - int *rightattnos; /* right-child varattnos of join cols, or 0 */ - List *usingNames; /* names assigned to merged columns */ -} deparse_columns; - -/* This macro is analogous to rt_fetch(), but for deparse_columns structs */ -#define deparse_columns_fetch(rangetable_index, dpns) \ - ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1)) - -/* - * Entry in set_rtable_names' hash table - */ -typedef struct -{ - char name[NAMEDATALEN]; /* Hash key --- must be first */ - int counter; /* Largest addition used so far for name */ -} NameHashEntry; - - -/* ---------- - * Local functions - * - * Most of these functions used to use fixed-size buffers to build their - * results. Now, they take an (already initialized) StringInfo object - * as a parameter, and append their text output to its contents. - * ---------- - */ -static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, - Bitmapset *rels_used); -static void set_deparse_for_query(deparse_namespace *dpns, Query *query, - List *parent_namespaces); -static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode); -static void set_using_names(deparse_namespace *dpns, Node *jtnode, - List *parentUsing); -static void set_relation_column_names(deparse_namespace *dpns, - RangeTblEntry *rte, - deparse_columns *colinfo); -static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte, - deparse_columns *colinfo); -static bool colname_is_unique(char *colname, deparse_namespace *dpns, - deparse_columns *colinfo); -static char *make_colname_unique(char *colname, deparse_namespace *dpns, - deparse_columns *colinfo); -static void expand_colnames_array_to(deparse_columns *colinfo, int n); -static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte, - deparse_columns *colinfo); -static void flatten_join_using_qual(Node *qual, - List **leftvars, List **rightvars); -static char *get_rtable_name(int rtindex, deparse_context *context); -static void set_deparse_planstate(deparse_namespace *dpns, PlanState *ps); -static void push_child_plan(deparse_namespace *dpns, PlanState *ps, - deparse_namespace *save_dpns); -static void pop_child_plan(deparse_namespace *dpns, - deparse_namespace *save_dpns); -static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, - deparse_namespace *save_dpns); -static void pop_ancestor_plan(deparse_namespace *dpns, - deparse_namespace *save_dpns); -static void get_query_def(Query *query, StringInfo buf, List *parentnamespace, - TupleDesc resultDesc, - int prettyFlags, int wrapColumn, int startIndent); -static void get_query_def_extended(Query *query, StringInfo buf, - List *parentnamespace, Oid distrelid, int64 shardid, - TupleDesc resultDesc, int prettyFlags, int wrapColumn, - int startIndent); -static void get_values_def(List *values_lists, deparse_context *context); -static void get_with_clause(Query *query, deparse_context *context); -static void get_select_query_def(Query *query, deparse_context *context, - TupleDesc resultDesc); -static void get_insert_query_def(Query *query, deparse_context *context); -static void get_update_query_def(Query *query, deparse_context *context); -static void get_update_query_targetlist_def(Query *query, List *targetList, - deparse_context *context, - RangeTblEntry *rte); -static void get_delete_query_def(Query *query, deparse_context *context); -static void get_utility_query_def(Query *query, deparse_context *context); -static void get_basic_select_query(Query *query, deparse_context *context, - TupleDesc resultDesc); -static void get_target_list(List *targetList, deparse_context *context, - TupleDesc resultDesc); -static void get_setop_query(Node *setOp, Query *query, - deparse_context *context, - TupleDesc resultDesc); -static Node *get_rule_sortgroupclause(Index ref, List *tlist, - bool force_colno, - deparse_context *context); -static void get_rule_groupingset(GroupingSet *gset, List *targetlist, - bool omit_parens, deparse_context *context); -static void get_rule_orderby(List *orderList, List *targetList, - bool force_colno, deparse_context *context); -static void get_rule_windowclause(Query *query, deparse_context *context); -static void get_rule_windowspec(WindowClause *wc, List *targetList, - deparse_context *context); -static char *get_variable(Var *var, int levelsup, bool istoplevel, - deparse_context *context); -static void get_special_variable(Node *node, deparse_context *context, - void *private); -static void resolve_special_varno(Node *node, deparse_context *context, - void *private, - void (*callback) (Node *, deparse_context *, void *)); -static Node *find_param_referent(Param *param, deparse_context *context, - deparse_namespace **dpns_p, ListCell **ancestor_cell_p); -static void get_parameter(Param *param, deparse_context *context); -static const char *get_simple_binary_op_name(OpExpr *expr); -static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags); -static void appendContextKeyword(deparse_context *context, const char *str, - int indentBefore, int indentAfter, int indentPlus); -static void removeStringInfoSpaces(StringInfo str); -static void get_rule_expr(Node *node, deparse_context *context, - bool showimplicit); -static void get_rule_expr_toplevel(Node *node, deparse_context *context, - bool showimplicit); -static void get_rule_expr_funccall(Node *node, deparse_context *context, - bool showimplicit); -static bool looks_like_function(Node *node); -static void get_oper_expr(OpExpr *expr, deparse_context *context); -static void get_func_expr(FuncExpr *expr, deparse_context *context, - bool showimplicit); -static void get_agg_expr(Aggref *aggref, deparse_context *context, - Aggref *original_aggref); -static void get_agg_combine_expr(Node *node, deparse_context *context, - void *private); -static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context); -static void get_coercion_expr(Node *arg, deparse_context *context, - Oid resulttype, int32 resulttypmod, - Node *parentNode); -static void get_const_expr(Const *constval, deparse_context *context, - int showtype); -static void get_const_collation(Const *constval, deparse_context *context); -static void simple_quote_literal(StringInfo buf, const char *val); -static void get_sublink_expr(SubLink *sublink, deparse_context *context); -static void get_tablefunc(TableFunc *tf, deparse_context *context, - bool showimplicit); -static void get_from_clause(Query *query, const char *prefix, - deparse_context *context); -static void get_from_clause_item(Node *jtnode, Query *query, - deparse_context *context); -static void get_column_alias_list(deparse_columns *colinfo, - deparse_context *context); -static void get_from_clause_coldeflist(RangeTblFunction *rtfunc, - deparse_columns *colinfo, - deparse_context *context); -static void get_tablesample_def(TableSampleClause *tablesample, - deparse_context *context); -static void get_opclass_name(Oid opclass, Oid actual_datatype, - StringInfo buf); -static Node *processIndirection(Node *node, deparse_context *context); -static void printSubscripts(ArrayRef *aref, deparse_context *context); -static char *get_relation_name(Oid relid); -static char *generate_relation_or_shard_name(Oid relid, Oid distrelid, - int64 shardid, List *namespaces); -static char *generate_fragment_name(char *schemaName, char *tableName); -static char *generate_function_name(Oid funcid, int nargs, - List *argnames, Oid *argtypes, - bool has_variadic, bool *use_variadic_p, - ParseExprKind special_exprkind); -static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); - -#define only_marker(rte) ((rte)->inh ? "" : "ONLY ") - - - -/* - * pg_get_query_def parses back one query tree, and outputs the resulting query - * string into given buffer. - */ -void -pg_get_query_def(Query *query, StringInfo buffer) -{ - get_query_def(query, buffer, NIL, NULL, 0, WRAP_COLUMN_DEFAULT, 0); -} - - -/* - * pg_get_rule_expr deparses an expression and returns the result as a string. - */ -char * -pg_get_rule_expr(Node *expression) -{ - bool showImplicitCasts = true; - deparse_context context; - OverrideSearchPath *overridePath = NULL; - StringInfo buffer = makeStringInfo(); - - /* - * Set search_path to NIL so that all objects outside of pg_catalog will be - * schema-prefixed. pg_catalog will be added automatically when we call - * PushOverrideSearchPath(), since we set addCatalog to true; - */ - overridePath = GetOverrideSearchPath(CurrentMemoryContext); - overridePath->schemas = NIL; - overridePath->addCatalog = true; - PushOverrideSearchPath(overridePath); - - context.buf = buffer; - context.namespaces = NIL; - context.windowClause = NIL; - context.windowTList = NIL; - context.varprefix = false; - context.prettyFlags = 0; - context.wrapColumn = WRAP_COLUMN_DEFAULT; - context.indentLevel = 0; - context.special_exprkind = EXPR_KIND_NONE; - context.distrelid = InvalidOid; - context.shardid = INVALID_SHARD_ID; - - get_rule_expr(expression, &context, showImplicitCasts); - - /* revert back to original search_path */ - PopOverrideSearchPath(); - - return buffer->data; -} - - -/* - * set_rtable_names: select RTE aliases to be used in printing a query - * - * We fill in dpns->rtable_names with a list of names that is one-for-one with - * the already-filled dpns->rtable list. Each RTE name is unique among those - * in the new namespace plus any ancestor namespaces listed in - * parent_namespaces. - * - * If rels_used isn't NULL, only RTE indexes listed in it are given aliases. - * - * Note that this function is only concerned with relation names, not column - * names. - */ -static void -set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, - Bitmapset *rels_used) -{ - HASHCTL hash_ctl; - HTAB *names_hash; - NameHashEntry *hentry; - bool found; - int rtindex; - ListCell *lc; - - dpns->rtable_names = NIL; - /* nothing more to do if empty rtable */ - if (dpns->rtable == NIL) - return; - - /* - * We use a hash table to hold known names, so that this process is O(N) - * not O(N^2) for N names. - */ - MemSet(&hash_ctl, 0, sizeof(hash_ctl)); - hash_ctl.keysize = NAMEDATALEN; - hash_ctl.entrysize = sizeof(NameHashEntry); - hash_ctl.hcxt = CurrentMemoryContext; - names_hash = hash_create("set_rtable_names names", - list_length(dpns->rtable), - &hash_ctl, - HASH_ELEM | HASH_CONTEXT); - /* Preload the hash table with names appearing in parent_namespaces */ - foreach(lc, parent_namespaces) - { - deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc); - ListCell *lc2; - - foreach(lc2, olddpns->rtable_names) - { - char *oldname = (char *) lfirst(lc2); - - if (oldname == NULL) - continue; - hentry = (NameHashEntry *) hash_search(names_hash, - oldname, - HASH_ENTER, - &found); - /* we do not complain about duplicate names in parent namespaces */ - hentry->counter = 0; - } - } - - /* Now we can scan the rtable */ - rtindex = 1; - foreach(lc, dpns->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - char *refname; - - /* Just in case this takes an unreasonable amount of time ... */ - CHECK_FOR_INTERRUPTS(); - - if (rels_used && !bms_is_member(rtindex, rels_used)) - { - /* Ignore unreferenced RTE */ - refname = NULL; - } - else if (rte->alias) - { - /* If RTE has a user-defined alias, prefer that */ - refname = rte->alias->aliasname; - } - else if (rte->rtekind == RTE_RELATION) - { - /* Use the current actual name of the relation */ - refname = get_rel_name(rte->relid); - } - else if (rte->rtekind == RTE_JOIN) - { - /* Unnamed join has no refname */ - refname = NULL; - } - else - { - /* Otherwise use whatever the parser assigned */ - refname = rte->eref->aliasname; - } - - /* - * If the selected name isn't unique, append digits to make it so, and - * make a new hash entry for it once we've got a unique name. For a - * very long input name, we might have to truncate to stay within - * NAMEDATALEN. - */ - if (refname) - { - hentry = (NameHashEntry *) hash_search(names_hash, - refname, - HASH_ENTER, - &found); - if (found) - { - /* Name already in use, must choose a new one */ - int refnamelen = strlen(refname); - char *modname = (char *) palloc(refnamelen + 16); - NameHashEntry *hentry2; - - do - { - hentry->counter++; - for (;;) - { - /* - * We avoid using %.*s here because it can misbehave - * if the data is not valid in what libc thinks is the - * prevailing encoding. - */ - memcpy(modname, refname, refnamelen); - sprintf(modname + refnamelen, "_%d", hentry->counter); - if (strlen(modname) < NAMEDATALEN) - break; - /* drop chars from refname to keep all the digits */ - refnamelen = pg_mbcliplen(refname, refnamelen, - refnamelen - 1); - } - hentry2 = (NameHashEntry *) hash_search(names_hash, - modname, - HASH_ENTER, - &found); - } while (found); - hentry2->counter = 0; /* init new hash entry */ - refname = modname; - } - else - { - /* Name not previously used, need only initialize hentry */ - hentry->counter = 0; - } - } - - dpns->rtable_names = lappend(dpns->rtable_names, refname); - rtindex++; - } - - hash_destroy(names_hash); -} - -/* - * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree - * - * For convenience, this is defined to initialize the deparse_namespace struct - * from scratch. - */ -static void -set_deparse_for_query(deparse_namespace *dpns, Query *query, - List *parent_namespaces) -{ - ListCell *lc; - ListCell *lc2; - - /* Initialize *dpns and fill rtable/ctes links */ - memset(dpns, 0, sizeof(deparse_namespace)); - dpns->rtable = query->rtable; - dpns->ctes = query->cteList; - - /* Assign a unique relation alias to each RTE */ - set_rtable_names(dpns, parent_namespaces, NULL); - - /* Initialize dpns->rtable_columns to contain zeroed structs */ - dpns->rtable_columns = NIL; - while (list_length(dpns->rtable_columns) < list_length(dpns->rtable)) - dpns->rtable_columns = lappend(dpns->rtable_columns, - palloc0(sizeof(deparse_columns))); - - /* If it's a utility query, it won't have a jointree */ - if (query->jointree) - { - /* Detect whether global uniqueness of USING names is needed */ - dpns->unique_using = - has_dangerous_join_using(dpns, (Node *) query->jointree); - - /* - * Select names for columns merged by USING, via a recursive pass over - * the query jointree. - */ - set_using_names(dpns, (Node *) query->jointree, NIL); - } - - /* - * Now assign remaining column aliases for each RTE. We do this in a - * linear scan of the rtable, so as to process RTEs whether or not they - * are in the jointree (we mustn't miss NEW.*, INSERT target relations, - * etc). JOIN RTEs must be processed after their children, but this is - * okay because they appear later in the rtable list than their children - * (cf Asserts in identify_join_columns()). - */ - forboth(lc, dpns->rtable, lc2, dpns->rtable_columns) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - deparse_columns *colinfo = (deparse_columns *) lfirst(lc2); - - if (rte->rtekind == RTE_JOIN) - set_join_column_names(dpns, rte, colinfo); - else - set_relation_column_names(dpns, rte, colinfo); - } -} - -/* - * has_dangerous_join_using: search jointree for unnamed JOIN USING - * - * Merged columns of a JOIN USING may act differently from either of the input - * columns, either because they are merged with COALESCE (in a FULL JOIN) or - * because an implicit coercion of the underlying input column is required. - * In such a case the column must be referenced as a column of the JOIN not as - * a column of either input. And this is problematic if the join is unnamed - * (alias-less): we cannot qualify the column's name with an RTE name, since - * there is none. (Forcibly assigning an alias to the join is not a solution, - * since that will prevent legal references to tables below the join.) - * To ensure that every column in the query is unambiguously referenceable, - * we must assign such merged columns names that are globally unique across - * the whole query, aliasing other columns out of the way as necessary. - * - * Because the ensuing re-aliasing is fairly damaging to the readability of - * the query, we don't do this unless we have to. So, we must pre-scan - * the join tree to see if we have to, before starting set_using_names(). - */ -static bool -has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode) -{ - if (IsA(jtnode, RangeTblRef)) - { - /* nothing to do here */ - } - else if (IsA(jtnode, FromExpr)) - { - FromExpr *f = (FromExpr *) jtnode; - ListCell *lc; - - foreach(lc, f->fromlist) - { - if (has_dangerous_join_using(dpns, (Node *) lfirst(lc))) - return true; - } - } - else if (IsA(jtnode, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) jtnode; - - /* Is it an unnamed JOIN with USING? */ - if (j->alias == NULL && j->usingClause) - { - /* - * Yes, so check each join alias var to see if any of them are not - * simple references to underlying columns. If so, we have a - * dangerous situation and must pick unique aliases. - */ - RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable); - ListCell *lc; - - foreach(lc, jrte->joinaliasvars) - { - Var *aliasvar = (Var *) lfirst(lc); - - if (aliasvar != NULL && !IsA(aliasvar, Var)) - return true; - } - } - - /* Nope, but inspect children */ - if (has_dangerous_join_using(dpns, j->larg)) - return true; - if (has_dangerous_join_using(dpns, j->rarg)) - return true; - } - else - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(jtnode)); - return false; -} - -/* - * set_using_names: select column aliases to be used for merged USING columns - * - * We do this during a recursive descent of the query jointree. - * dpns->unique_using must already be set to determine the global strategy. - * - * Column alias info is saved in the dpns->rtable_columns list, which is - * assumed to be filled with pre-zeroed deparse_columns structs. - * - * parentUsing is a list of all USING aliases assigned in parent joins of - * the current jointree node. (The passed-in list must not be modified.) - */ -static void -set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing) -{ - if (IsA(jtnode, RangeTblRef)) - { - /* nothing to do now */ - } - else if (IsA(jtnode, FromExpr)) - { - FromExpr *f = (FromExpr *) jtnode; - ListCell *lc; - - foreach(lc, f->fromlist) - set_using_names(dpns, (Node *) lfirst(lc), parentUsing); - } - else if (IsA(jtnode, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) jtnode; - RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable); - deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns); - int *leftattnos; - int *rightattnos; - deparse_columns *leftcolinfo; - deparse_columns *rightcolinfo; - int i; - ListCell *lc; - - /* Get info about the shape of the join */ - identify_join_columns(j, rte, colinfo); - leftattnos = colinfo->leftattnos; - rightattnos = colinfo->rightattnos; - - /* Look up the not-yet-filled-in child deparse_columns structs */ - leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns); - rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns); - - /* - * If this join is unnamed, then we cannot substitute new aliases at - * this level, so any name requirements pushed down to here must be - * pushed down again to the children. - */ - if (rte->alias == NULL) - { - for (i = 0; i < colinfo->num_cols; i++) - { - char *colname = colinfo->colnames[i]; - - if (colname == NULL) - continue; - - /* Push down to left column, unless it's a system column */ - if (leftattnos[i] > 0) - { - expand_colnames_array_to(leftcolinfo, leftattnos[i]); - leftcolinfo->colnames[leftattnos[i] - 1] = colname; - } - - /* Same on the righthand side */ - if (rightattnos[i] > 0) - { - expand_colnames_array_to(rightcolinfo, rightattnos[i]); - rightcolinfo->colnames[rightattnos[i] - 1] = colname; - } - } - } - - /* - * If there's a USING clause, select the USING column names and push - * those names down to the children. We have two strategies: - * - * If dpns->unique_using is TRUE, we force all USING names to be - * unique across the whole query level. In principle we'd only need - * the names of dangerous USING columns to be globally unique, but to - * safely assign all USING names in a single pass, we have to enforce - * the same uniqueness rule for all of them. However, if a USING - * column's name has been pushed down from the parent, we should use - * it as-is rather than making a uniqueness adjustment. This is - * necessary when we're at an unnamed join, and it creates no risk of - * ambiguity. Also, if there's a user-written output alias for a - * merged column, we prefer to use that rather than the input name; - * this simplifies the logic and seems likely to lead to less aliasing - * overall. - * - * If dpns->unique_using is FALSE, we only need USING names to be - * unique within their own join RTE. We still need to honor - * pushed-down names, though. - * - * Though significantly different in results, these two strategies are - * implemented by the same code, with only the difference of whether - * to put assigned names into dpns->using_names. - */ - if (j->usingClause) - { - /* Copy the input parentUsing list so we don't modify it */ - parentUsing = list_copy(parentUsing); - - /* USING names must correspond to the first join output columns */ - expand_colnames_array_to(colinfo, list_length(j->usingClause)); - i = 0; - foreach(lc, j->usingClause) - { - char *colname = strVal(lfirst(lc)); - - /* Assert it's a merged column */ - Assert(leftattnos[i] != 0 && rightattnos[i] != 0); - - /* Adopt passed-down name if any, else select unique name */ - if (colinfo->colnames[i] != NULL) - colname = colinfo->colnames[i]; - else - { - /* Prefer user-written output alias if any */ - if (rte->alias && i < list_length(rte->alias->colnames)) - colname = strVal(list_nth(rte->alias->colnames, i)); - /* Make it appropriately unique */ - colname = make_colname_unique(colname, dpns, colinfo); - if (dpns->unique_using) - dpns->using_names = lappend(dpns->using_names, - colname); - /* Save it as output column name, too */ - colinfo->colnames[i] = colname; - } - - /* Remember selected names for use later */ - colinfo->usingNames = lappend(colinfo->usingNames, colname); - parentUsing = lappend(parentUsing, colname); - - /* Push down to left column, unless it's a system column */ - if (leftattnos[i] > 0) - { - expand_colnames_array_to(leftcolinfo, leftattnos[i]); - leftcolinfo->colnames[leftattnos[i] - 1] = colname; - } - - /* Same on the righthand side */ - if (rightattnos[i] > 0) - { - expand_colnames_array_to(rightcolinfo, rightattnos[i]); - rightcolinfo->colnames[rightattnos[i] - 1] = colname; - } - - i++; - } - } - - /* Mark child deparse_columns structs with correct parentUsing info */ - leftcolinfo->parentUsing = parentUsing; - rightcolinfo->parentUsing = parentUsing; - - /* Now recursively assign USING column names in children */ - set_using_names(dpns, j->larg, parentUsing); - set_using_names(dpns, j->rarg, parentUsing); - } - else - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(jtnode)); -} - -/* - * set_relation_column_names: select column aliases for a non-join RTE - * - * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed. - * If any colnames entries are already filled in, those override local - * choices. - */ -static void -set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte, - deparse_columns *colinfo) -{ - int ncolumns; - char **real_colnames; - bool changed_any; - int noldcolumns; - int i; - int j; - - /* - * Extract the RTE's "real" column names. This is comparable to - * get_rte_attribute_name, except that it's important to disregard dropped - * columns. We put NULL into the array for a dropped column. - */ - if (rte->rtekind == RTE_RELATION) - { - /* Relation --- look to the system catalogs for up-to-date info */ - Relation rel; - TupleDesc tupdesc; - - rel = relation_open(rte->relid, AccessShareLock); - tupdesc = RelationGetDescr(rel); - - ncolumns = tupdesc->natts; - real_colnames = (char **) palloc(ncolumns * sizeof(char *)); - - for (i = 0; i < ncolumns; i++) - { - if (tupdesc->attrs[i]->attisdropped) - real_colnames[i] = NULL; - else - real_colnames[i] = pstrdup(NameStr(tupdesc->attrs[i]->attname)); - } - relation_close(rel, AccessShareLock); - } - else - { - /* Otherwise use the column names from eref */ - ListCell *lc; - - ncolumns = list_length(rte->eref->colnames); - real_colnames = (char **) palloc(ncolumns * sizeof(char *)); - - i = 0; - foreach(lc, rte->eref->colnames) - { - /* - * If the column name shown in eref is an empty string, then it's - * a column that was dropped at the time of parsing the query, so - * treat it as dropped. - */ - char *cname = strVal(lfirst(lc)); - - if (cname[0] == '\0') - cname = NULL; - real_colnames[i] = cname; - i++; - } - } - - /* - * Ensure colinfo->colnames has a slot for each column. (It could be long - * enough already, if we pushed down a name for the last column.) Note: - * it's possible that there are now more columns than there were when the - * query was parsed, ie colnames could be longer than rte->eref->colnames. - * We must assign unique aliases to the new columns too, else there could - * be unresolved conflicts when the view/rule is reloaded. - */ - expand_colnames_array_to(colinfo, ncolumns); - Assert(colinfo->num_cols == ncolumns); - - /* - * Make sufficiently large new_colnames and is_new_col arrays, too. - * - * Note: because we leave colinfo->num_new_cols zero until after the loop, - * colname_is_unique will not consult that array, which is fine because it - * would only be duplicate effort. - */ - colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *)); - colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool)); - - /* - * Scan the columns, select a unique alias for each one, and store it in - * colinfo->colnames and colinfo->new_colnames. The former array has NULL - * entries for dropped columns, the latter omits them. Also mark - * new_colnames entries as to whether they are new since parse time; this - * is the case for entries beyond the length of rte->eref->colnames. - */ - noldcolumns = list_length(rte->eref->colnames); - changed_any = false; - j = 0; - for (i = 0; i < ncolumns; i++) - { - char *real_colname = real_colnames[i]; - char *colname = colinfo->colnames[i]; - - /* Skip dropped columns */ - if (real_colname == NULL) - { - Assert(colname == NULL); /* colnames[i] is already NULL */ - continue; - } - - /* If alias already assigned, that's what to use */ - if (colname == NULL) - { - /* If user wrote an alias, prefer that over real column name */ - if (rte->alias && i < list_length(rte->alias->colnames)) - colname = strVal(list_nth(rte->alias->colnames, i)); - else - colname = real_colname; - - /* Unique-ify and insert into colinfo */ - colname = make_colname_unique(colname, dpns, colinfo); - - colinfo->colnames[i] = colname; - } - - /* Put names of non-dropped columns in new_colnames[] too */ - colinfo->new_colnames[j] = colname; - /* And mark them as new or not */ - colinfo->is_new_col[j] = (i >= noldcolumns); - j++; - - /* Remember if any assigned aliases differ from "real" name */ - if (!changed_any && strcmp(colname, real_colname) != 0) - changed_any = true; - } - - /* - * Set correct length for new_colnames[] array. (Note: if columns have - * been added, colinfo->num_cols includes them, which is not really quite - * right but is harmless, since any new columns must be at the end where - * they won't affect varattnos of pre-existing columns.) - */ - colinfo->num_new_cols = j; - - /* - * For a relation RTE, we need only print the alias column names if any - * are different from the underlying "real" names. For a function RTE, - * always emit a complete column alias list; this is to protect against - * possible instability of the default column names (eg, from altering - * parameter names). For tablefunc RTEs, we never print aliases, because - * the column names are part of the clause itself. For other RTE types, - * print if we changed anything OR if there were user-written column - * aliases (since the latter would be part of the underlying "reality"). - */ - if (rte->rtekind == RTE_RELATION) - colinfo->printaliases = changed_any; - else if (rte->rtekind == RTE_FUNCTION) - colinfo->printaliases = true; - else if (rte->rtekind == RTE_TABLEFUNC) - colinfo->printaliases = false; - else if (rte->alias && rte->alias->colnames != NIL) - colinfo->printaliases = true; - else - colinfo->printaliases = changed_any; -} - -/* - * set_join_column_names: select column aliases for a join RTE - * - * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed. - * If any colnames entries are already filled in, those override local - * choices. Also, names for USING columns were already chosen by - * set_using_names(). We further expect that column alias selection has been - * completed for both input RTEs. - */ -static void -set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte, - deparse_columns *colinfo) -{ - deparse_columns *leftcolinfo; - deparse_columns *rightcolinfo; - bool changed_any; - int noldcolumns; - int nnewcolumns; - Bitmapset *leftmerged = NULL; - Bitmapset *rightmerged = NULL; - int i; - int j; - int ic; - int jc; - - /* Look up the previously-filled-in child deparse_columns structs */ - leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns); - rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns); - - /* - * Ensure colinfo->colnames has a slot for each column. (It could be long - * enough already, if we pushed down a name for the last column.) Note: - * it's possible that one or both inputs now have more columns than there - * were when the query was parsed, but we'll deal with that below. We - * only need entries in colnames for pre-existing columns. - */ - noldcolumns = list_length(rte->eref->colnames); - expand_colnames_array_to(colinfo, noldcolumns); - Assert(colinfo->num_cols == noldcolumns); - - /* - * Scan the join output columns, select an alias for each one, and store - * it in colinfo->colnames. If there are USING columns, set_using_names() - * already selected their names, so we can start the loop at the first - * non-merged column. - */ - changed_any = false; - for (i = list_length(colinfo->usingNames); i < noldcolumns; i++) - { - char *colname = colinfo->colnames[i]; - char *real_colname; - - /* Ignore dropped column (only possible for non-merged column) */ - if (colinfo->leftattnos[i] == 0 && colinfo->rightattnos[i] == 0) - { - Assert(colname == NULL); - continue; - } - - /* Get the child column name */ - if (colinfo->leftattnos[i] > 0) - real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1]; - else if (colinfo->rightattnos[i] > 0) - real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1]; - else - { - /* We're joining system columns --- use eref name */ - real_colname = strVal(list_nth(rte->eref->colnames, i)); - } - Assert(real_colname != NULL); - - /* In an unnamed join, just report child column names as-is */ - if (rte->alias == NULL) - { - colinfo->colnames[i] = real_colname; - continue; - } - - /* If alias already assigned, that's what to use */ - if (colname == NULL) - { - /* If user wrote an alias, prefer that over real column name */ - if (rte->alias && i < list_length(rte->alias->colnames)) - colname = strVal(list_nth(rte->alias->colnames, i)); - else - colname = real_colname; - - /* Unique-ify and insert into colinfo */ - colname = make_colname_unique(colname, dpns, colinfo); - - colinfo->colnames[i] = colname; - } - - /* Remember if any assigned aliases differ from "real" name */ - if (!changed_any && strcmp(colname, real_colname) != 0) - changed_any = true; - } - - /* - * Calculate number of columns the join would have if it were re-parsed - * now, and create storage for the new_colnames and is_new_col arrays. - * - * Note: colname_is_unique will be consulting new_colnames[] during the - * loops below, so its not-yet-filled entries must be zeroes. - */ - nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols - - list_length(colinfo->usingNames); - colinfo->num_new_cols = nnewcolumns; - colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *)); - colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool)); - - /* - * Generating the new_colnames array is a bit tricky since any new columns - * added since parse time must be inserted in the right places. This code - * must match the parser, which will order a join's columns as merged - * columns first (in USING-clause order), then non-merged columns from the - * left input (in attnum order), then non-merged columns from the right - * input (ditto). If one of the inputs is itself a join, its columns will - * be ordered according to the same rule, which means newly-added columns - * might not be at the end. We can figure out what's what by consulting - * the leftattnos and rightattnos arrays plus the input is_new_col arrays. - * - * In these loops, i indexes leftattnos/rightattnos (so it's join varattno - * less one), j indexes new_colnames/is_new_col, and ic/jc have similar - * meanings for the current child RTE. - */ - - /* Handle merged columns; they are first and can't be new */ - i = j = 0; - while (i < noldcolumns && - colinfo->leftattnos[i] != 0 && - colinfo->rightattnos[i] != 0) - { - /* column name is already determined and known unique */ - colinfo->new_colnames[j] = colinfo->colnames[i]; - colinfo->is_new_col[j] = false; - - /* build bitmapsets of child attnums of merged columns */ - if (colinfo->leftattnos[i] > 0) - leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]); - if (colinfo->rightattnos[i] > 0) - rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]); - - i++, j++; - } - - /* Handle non-merged left-child columns */ - ic = 0; - for (jc = 0; jc < leftcolinfo->num_new_cols; jc++) - { - char *child_colname = leftcolinfo->new_colnames[jc]; - - if (!leftcolinfo->is_new_col[jc]) - { - /* Advance ic to next non-dropped old column of left child */ - while (ic < leftcolinfo->num_cols && - leftcolinfo->colnames[ic] == NULL) - ic++; - Assert(ic < leftcolinfo->num_cols); - ic++; - /* If it is a merged column, we already processed it */ - if (bms_is_member(ic, leftmerged)) - continue; - /* Else, advance i to the corresponding existing join column */ - while (i < colinfo->num_cols && - colinfo->colnames[i] == NULL) - i++; - Assert(i < colinfo->num_cols); - Assert(ic == colinfo->leftattnos[i]); - /* Use the already-assigned name of this column */ - colinfo->new_colnames[j] = colinfo->colnames[i]; - i++; - } - else - { - /* - * Unique-ify the new child column name and assign, unless we're - * in an unnamed join, in which case just copy - */ - if (rte->alias != NULL) - { - colinfo->new_colnames[j] = - make_colname_unique(child_colname, dpns, colinfo); - if (!changed_any && - strcmp(colinfo->new_colnames[j], child_colname) != 0) - changed_any = true; - } - else - colinfo->new_colnames[j] = child_colname; - } - - colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc]; - j++; - } - - /* Handle non-merged right-child columns in exactly the same way */ - ic = 0; - for (jc = 0; jc < rightcolinfo->num_new_cols; jc++) - { - char *child_colname = rightcolinfo->new_colnames[jc]; - - if (!rightcolinfo->is_new_col[jc]) - { - /* Advance ic to next non-dropped old column of right child */ - while (ic < rightcolinfo->num_cols && - rightcolinfo->colnames[ic] == NULL) - ic++; - Assert(ic < rightcolinfo->num_cols); - ic++; - /* If it is a merged column, we already processed it */ - if (bms_is_member(ic, rightmerged)) - continue; - /* Else, advance i to the corresponding existing join column */ - while (i < colinfo->num_cols && - colinfo->colnames[i] == NULL) - i++; - Assert(i < colinfo->num_cols); - Assert(ic == colinfo->rightattnos[i]); - /* Use the already-assigned name of this column */ - colinfo->new_colnames[j] = colinfo->colnames[i]; - i++; - } - else - { - /* - * Unique-ify the new child column name and assign, unless we're - * in an unnamed join, in which case just copy - */ - if (rte->alias != NULL) - { - colinfo->new_colnames[j] = - make_colname_unique(child_colname, dpns, colinfo); - if (!changed_any && - strcmp(colinfo->new_colnames[j], child_colname) != 0) - changed_any = true; - } - else - colinfo->new_colnames[j] = child_colname; - } - - colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc]; - j++; - } - - /* Assert we processed the right number of columns */ -#ifdef USE_ASSERT_CHECKING - while (i < colinfo->num_cols && colinfo->colnames[i] == NULL) - i++; - Assert(i == colinfo->num_cols); - Assert(j == nnewcolumns); -#endif - - /* - * For a named join, print column aliases if we changed any from the child - * names. Unnamed joins cannot print aliases. - */ - if (rte->alias != NULL) - colinfo->printaliases = changed_any; - else - colinfo->printaliases = false; -} - -/* - * colname_is_unique: is colname distinct from already-chosen column names? - * - * dpns is query-wide info, colinfo is for the column's RTE - */ -static bool -colname_is_unique(char *colname, deparse_namespace *dpns, - deparse_columns *colinfo) -{ - int i; - ListCell *lc; - - /* Check against already-assigned column aliases within RTE */ - for (i = 0; i < colinfo->num_cols; i++) - { - char *oldname = colinfo->colnames[i]; - - if (oldname && strcmp(oldname, colname) == 0) - return false; - } - - /* - * If we're building a new_colnames array, check that too (this will be - * partially but not completely redundant with the previous checks) - */ - for (i = 0; i < colinfo->num_new_cols; i++) - { - char *oldname = colinfo->new_colnames[i]; - - if (oldname && strcmp(oldname, colname) == 0) - return false; - } - - /* Also check against USING-column names that must be globally unique */ - foreach(lc, dpns->using_names) - { - char *oldname = (char *) lfirst(lc); - - if (strcmp(oldname, colname) == 0) - return false; - } - - /* Also check against names already assigned for parent-join USING cols */ - foreach(lc, colinfo->parentUsing) - { - char *oldname = (char *) lfirst(lc); - - if (strcmp(oldname, colname) == 0) - return false; - } - - return true; -} - -/* - * make_colname_unique: modify colname if necessary to make it unique - * - * dpns is query-wide info, colinfo is for the column's RTE - */ -static char * -make_colname_unique(char *colname, deparse_namespace *dpns, - deparse_columns *colinfo) -{ - /* - * If the selected name isn't unique, append digits to make it so. For a - * very long input name, we might have to truncate to stay within - * NAMEDATALEN. - */ - if (!colname_is_unique(colname, dpns, colinfo)) - { - int colnamelen = strlen(colname); - char *modname = (char *) palloc(colnamelen + 16); - int i = 0; - - do - { - i++; - for (;;) - { - /* - * We avoid using %.*s here because it can misbehave if the - * data is not valid in what libc thinks is the prevailing - * encoding. - */ - memcpy(modname, colname, colnamelen); - sprintf(modname + colnamelen, "_%d", i); - if (strlen(modname) < NAMEDATALEN) - break; - /* drop chars from colname to keep all the digits */ - colnamelen = pg_mbcliplen(colname, colnamelen, - colnamelen - 1); - } - } while (!colname_is_unique(modname, dpns, colinfo)); - colname = modname; - } - return colname; -} - -/* - * expand_colnames_array_to: make colinfo->colnames at least n items long - * - * Any added array entries are initialized to zero. - */ -static void -expand_colnames_array_to(deparse_columns *colinfo, int n) -{ - if (n > colinfo->num_cols) - { - if (colinfo->colnames == NULL) - colinfo->colnames = (char **) palloc0(n * sizeof(char *)); - else - { - colinfo->colnames = (char **) repalloc(colinfo->colnames, - n * sizeof(char *)); - memset(colinfo->colnames + colinfo->num_cols, 0, - (n - colinfo->num_cols) * sizeof(char *)); - } - colinfo->num_cols = n; - } -} - -/* - * identify_join_columns: figure out where columns of a join come from - * - * Fills the join-specific fields of the colinfo struct, except for - * usingNames which is filled later. - */ -static void -identify_join_columns(JoinExpr *j, RangeTblEntry *jrte, - deparse_columns *colinfo) -{ - int numjoincols; - int i; - ListCell *lc; - - /* Extract left/right child RT indexes */ - if (IsA(j->larg, RangeTblRef)) - colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex; - else if (IsA(j->larg, JoinExpr)) - colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex; - else - elog(ERROR, "unrecognized node type in jointree: %d", - (int) nodeTag(j->larg)); - if (IsA(j->rarg, RangeTblRef)) - colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex; - else if (IsA(j->rarg, JoinExpr)) - colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex; - else - elog(ERROR, "unrecognized node type in jointree: %d", - (int) nodeTag(j->rarg)); - - /* Assert children will be processed earlier than join in second pass */ - Assert(colinfo->leftrti < j->rtindex); - Assert(colinfo->rightrti < j->rtindex); - - /* Initialize result arrays with zeroes */ - numjoincols = list_length(jrte->joinaliasvars); - Assert(numjoincols == list_length(jrte->eref->colnames)); - colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int)); - colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int)); - - /* Scan the joinaliasvars list to identify simple column references */ - i = 0; - foreach(lc, jrte->joinaliasvars) - { - Var *aliasvar = (Var *) lfirst(lc); - - /* get rid of any implicit coercion above the Var */ - aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar); - - if (aliasvar == NULL) - { - /* It's a dropped column; nothing to do here */ - } - else if (IsA(aliasvar, Var)) - { - Assert(aliasvar->varlevelsup == 0); - Assert(aliasvar->varattno != 0); - if (aliasvar->varno == colinfo->leftrti) - colinfo->leftattnos[i] = aliasvar->varattno; - else if (aliasvar->varno == colinfo->rightrti) - colinfo->rightattnos[i] = aliasvar->varattno; - else - elog(ERROR, "unexpected varno %d in JOIN RTE", - aliasvar->varno); - } - else if (IsA(aliasvar, CoalesceExpr)) - { - /* - * It's a merged column in FULL JOIN USING. Ignore it for now and - * let the code below identify the merged columns. - */ - } - else - elog(ERROR, "unrecognized node type in join alias vars: %d", - (int) nodeTag(aliasvar)); - - i++; - } - - /* - * If there's a USING clause, deconstruct the join quals to identify the - * merged columns. This is a tad painful but if we cannot rely on the - * column names, there is no other representation of which columns were - * joined by USING. (Unless the join type is FULL, we can't tell from the - * joinaliasvars list which columns are merged.) Note: we assume that the - * merged columns are the first output column(s) of the join. - */ - if (j->usingClause) - { - List *leftvars = NIL; - List *rightvars = NIL; - ListCell *lc2; - - /* Extract left- and right-side Vars from the qual expression */ - flatten_join_using_qual(j->quals, &leftvars, &rightvars); - Assert(list_length(leftvars) == list_length(j->usingClause)); - Assert(list_length(rightvars) == list_length(j->usingClause)); - - /* Mark the output columns accordingly */ - i = 0; - forboth(lc, leftvars, lc2, rightvars) - { - Var *leftvar = (Var *) lfirst(lc); - Var *rightvar = (Var *) lfirst(lc2); - - Assert(leftvar->varlevelsup == 0); - Assert(leftvar->varattno != 0); - if (leftvar->varno != colinfo->leftrti) - elog(ERROR, "unexpected varno %d in JOIN USING qual", - leftvar->varno); - colinfo->leftattnos[i] = leftvar->varattno; - - Assert(rightvar->varlevelsup == 0); - Assert(rightvar->varattno != 0); - if (rightvar->varno != colinfo->rightrti) - elog(ERROR, "unexpected varno %d in JOIN USING qual", - rightvar->varno); - colinfo->rightattnos[i] = rightvar->varattno; - - i++; - } - } -} - -/* - * flatten_join_using_qual: extract Vars being joined from a JOIN/USING qual - * - * We assume that transformJoinUsingClause won't have produced anything except - * AND nodes, equality operator nodes, and possibly implicit coercions, and - * that the AND node inputs match left-to-right with the original USING list. - * - * Caller must initialize the result lists to NIL. - */ -static void -flatten_join_using_qual(Node *qual, List **leftvars, List **rightvars) -{ - if (IsA(qual, BoolExpr)) - { - /* Handle AND nodes by recursion */ - BoolExpr *b = (BoolExpr *) qual; - ListCell *lc; - - Assert(b->boolop == AND_EXPR); - foreach(lc, b->args) - { - flatten_join_using_qual((Node *) lfirst(lc), - leftvars, rightvars); - } - } - else if (IsA(qual, OpExpr)) - { - /* Otherwise we should have an equality operator */ - OpExpr *op = (OpExpr *) qual; - Var *var; - - if (list_length(op->args) != 2) - elog(ERROR, "unexpected unary operator in JOIN/USING qual"); - /* Arguments should be Vars with perhaps implicit coercions */ - var = (Var *) strip_implicit_coercions((Node *) linitial(op->args)); - if (!IsA(var, Var)) - elog(ERROR, "unexpected node type in JOIN/USING qual: %d", - (int) nodeTag(var)); - *leftvars = lappend(*leftvars, var); - var = (Var *) strip_implicit_coercions((Node *) lsecond(op->args)); - if (!IsA(var, Var)) - elog(ERROR, "unexpected node type in JOIN/USING qual: %d", - (int) nodeTag(var)); - *rightvars = lappend(*rightvars, var); - } - else - { - /* Perhaps we have an implicit coercion to boolean? */ - Node *q = strip_implicit_coercions(qual); - - if (q != qual) - flatten_join_using_qual(q, leftvars, rightvars); - else - elog(ERROR, "unexpected node type in JOIN/USING qual: %d", - (int) nodeTag(qual)); - } -} - -/* - * get_rtable_name: convenience function to get a previously assigned RTE alias - * - * The RTE must belong to the topmost namespace level in "context". - */ -static char * -get_rtable_name(int rtindex, deparse_context *context) -{ - deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces); - - Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names)); - return (char *) list_nth(dpns->rtable_names, rtindex - 1); -} - -/* - * set_deparse_planstate: set up deparse_namespace to parse subexpressions - * of a given PlanState node - * - * This sets the planstate, outer_planstate, inner_planstate, outer_tlist, - * inner_tlist, and index_tlist fields. Caller is responsible for adjusting - * the ancestors list if necessary. Note that the rtable and ctes fields do - * not need to change when shifting attention to different plan nodes in a - * single plan tree. - */ -static void -set_deparse_planstate(deparse_namespace *dpns, PlanState *ps) -{ - dpns->planstate = ps; - - /* - * We special-case Append and MergeAppend to pretend that the first child - * plan is the OUTER referent; we have to interpret OUTER Vars in their - * tlists according to one of the children, and the first one is the most - * natural choice. Likewise special-case ModifyTable to pretend that the - * first child plan is the OUTER referent; this is to support RETURNING - * lists containing references to non-target relations. - */ - if (IsA(ps, AppendState)) - dpns->outer_planstate = ((AppendState *) ps)->appendplans[0]; - else if (IsA(ps, MergeAppendState)) - dpns->outer_planstate = ((MergeAppendState *) ps)->mergeplans[0]; - else if (IsA(ps, ModifyTableState)) - dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0]; - else - dpns->outer_planstate = outerPlanState(ps); - - if (dpns->outer_planstate) - dpns->outer_tlist = dpns->outer_planstate->plan->targetlist; - else - dpns->outer_tlist = NIL; - - /* - * For a SubqueryScan, pretend the subplan is INNER referent. (We don't - * use OUTER because that could someday conflict with the normal meaning.) - * Likewise, for a CteScan, pretend the subquery's plan is INNER referent. - * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the - * excluded expression's tlist. (Similar to the SubqueryScan we don't want - * to reuse OUTER, it's used for RETURNING in some modify table cases, - * although not INSERT .. CONFLICT). - */ - if (IsA(ps, SubqueryScanState)) - dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan; - else if (IsA(ps, CteScanState)) - dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate; - else if (IsA(ps, ModifyTableState)) - dpns->inner_planstate = ps; - else - dpns->inner_planstate = innerPlanState(ps); - - if (IsA(ps, ModifyTableState)) - dpns->inner_tlist = ((ModifyTableState *) ps)->mt_excludedtlist; - else if (dpns->inner_planstate) - dpns->inner_tlist = dpns->inner_planstate->plan->targetlist; - else - dpns->inner_tlist = NIL; - - /* Set up referent for INDEX_VAR Vars, if needed */ - if (IsA(ps->plan, IndexOnlyScan)) - dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist; - else if (IsA(ps->plan, ForeignScan)) - dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_scan_tlist; - else if (IsA(ps->plan, CustomScan)) - dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist; - else - dpns->index_tlist = NIL; -} - -/* - * push_child_plan: temporarily transfer deparsing attention to a child plan - * - * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the - * deparse context in case the referenced expression itself uses - * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid - * affecting levelsup issues (although in a Plan tree there really shouldn't - * be any). - * - * Caller must provide a local deparse_namespace variable to save the - * previous state for pop_child_plan. - */ -static void -push_child_plan(deparse_namespace *dpns, PlanState *ps, - deparse_namespace *save_dpns) -{ - /* Save state for restoration later */ - *save_dpns = *dpns; - - /* Link current plan node into ancestors list */ - dpns->ancestors = lcons(dpns->planstate, dpns->ancestors); - - /* Set attention on selected child */ - set_deparse_planstate(dpns, ps); -} - -/* - * pop_child_plan: undo the effects of push_child_plan - */ -static void -pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns) -{ - List *ancestors; - - /* Get rid of ancestors list cell added by push_child_plan */ - ancestors = list_delete_first(dpns->ancestors); - - /* Restore fields changed by push_child_plan */ - *dpns = *save_dpns; - - /* Make sure dpns->ancestors is right (may be unnecessary) */ - dpns->ancestors = ancestors; -} - -/* - * push_ancestor_plan: temporarily transfer deparsing attention to an - * ancestor plan - * - * When expanding a Param reference, we must adjust the deparse context - * to match the plan node that contains the expression being printed; - * otherwise we'd fail if that expression itself contains a Param or - * OUTER_VAR/INNER_VAR/INDEX_VAR variable. - * - * The target ancestor is conveniently identified by the ListCell holding it - * in dpns->ancestors. - * - * Caller must provide a local deparse_namespace variable to save the - * previous state for pop_ancestor_plan. - */ -static void -push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, - deparse_namespace *save_dpns) -{ - PlanState *ps = (PlanState *) lfirst(ancestor_cell); - List *ancestors; - - /* Save state for restoration later */ - *save_dpns = *dpns; - - /* Build a new ancestor list with just this node's ancestors */ - ancestors = NIL; - while ((ancestor_cell = lnext(ancestor_cell)) != NULL) - ancestors = lappend(ancestors, lfirst(ancestor_cell)); - dpns->ancestors = ancestors; - - /* Set attention on selected ancestor */ - set_deparse_planstate(dpns, ps); -} - -/* - * pop_ancestor_plan: undo the effects of push_ancestor_plan - */ -static void -pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns) -{ - /* Free the ancestor list made in push_ancestor_plan */ - list_free(dpns->ancestors); - - /* Restore fields changed by push_ancestor_plan */ - *dpns = *save_dpns; -} - - -/* ---------- - * deparse_shard_query - Parse back a query for execution on a shard - * - * Builds an SQL string to perform the provided query on a specific shard and - * places this string into the provided buffer. - * ---------- - */ -void -deparse_shard_query(Query *query, Oid distrelid, int64 shardid, - StringInfo buffer) -{ - get_query_def_extended(query, buffer, NIL, distrelid, shardid, NULL, 0, - WRAP_COLUMN_DEFAULT, 0); -} - - -/* ---------- - * get_query_def - Parse back one query parsetree - * - * If resultDesc is not NULL, then it is the output tuple descriptor for - * the view represented by a SELECT query. - * ---------- - */ -static void -get_query_def(Query *query, StringInfo buf, List *parentnamespace, - TupleDesc resultDesc, - int prettyFlags, int wrapColumn, int startIndent) -{ - get_query_def_extended(query, buf, parentnamespace, InvalidOid, 0, resultDesc, - prettyFlags, wrapColumn, startIndent); -} - - -/* ---------- - * get_query_def_extended - Parse back one query parsetree, optionally - * with extension using a shard identifier. - * - * If distrelid is valid and shardid is positive, the provided shardid is added - * any time the provided relid is deparsed, so that the query may be executed - * on a placement for the given shard. - * ---------- - */ -static void -get_query_def_extended(Query *query, StringInfo buf, List *parentnamespace, - Oid distrelid, int64 shardid, TupleDesc resultDesc, - int prettyFlags, int wrapColumn, int startIndent) -{ - deparse_context context; - deparse_namespace dpns; - - OverrideSearchPath *overridePath = NULL; - - /* Guard against excessively long or deeply-nested queries */ - CHECK_FOR_INTERRUPTS(); - check_stack_depth(); - - /* - * Before we begin to examine the query, acquire locks on referenced - * relations, and fix up deleted columns in JOIN RTEs. This ensures - * consistent results. Note we assume it's OK to scribble on the passed - * querytree! - * - * We are only deparsing the query (we are not about to execute it), so we - * only need AccessShareLock on the relations it mentions. - */ - AcquireRewriteLocks(query, false, false); - - /* - * Set search_path to NIL so that all objects outside of pg_catalog will be - * schema-prefixed. pg_catalog will be added automatically when we call - * PushOverrideSearchPath(), since we set addCatalog to true; - */ - overridePath = GetOverrideSearchPath(CurrentMemoryContext); - overridePath->schemas = NIL; - overridePath->addCatalog = true; - PushOverrideSearchPath(overridePath); - - context.buf = buf; - context.namespaces = lcons(&dpns, list_copy(parentnamespace)); - context.windowClause = NIL; - context.windowTList = NIL; - context.varprefix = (parentnamespace != NIL || - list_length(query->rtable) != 1); - context.prettyFlags = prettyFlags; - context.wrapColumn = wrapColumn; - context.indentLevel = startIndent; - context.special_exprkind = EXPR_KIND_NONE; - context.distrelid = distrelid; - context.shardid = shardid; - - set_deparse_for_query(&dpns, query, parentnamespace); - - switch (query->commandType) - { - case CMD_SELECT: - get_select_query_def(query, &context, resultDesc); - break; - - case CMD_UPDATE: - get_update_query_def(query, &context); - break; - - case CMD_INSERT: - get_insert_query_def(query, &context); - break; - - case CMD_DELETE: - get_delete_query_def(query, &context); - break; - - case CMD_NOTHING: - appendStringInfoString(buf, "NOTHING"); - break; - - case CMD_UTILITY: - get_utility_query_def(query, &context); - break; - - default: - elog(ERROR, "unrecognized query command type: %d", - query->commandType); - break; - } - - /* revert back to original search_path */ - PopOverrideSearchPath(); -} - -/* ---------- - * get_values_def - Parse back a VALUES list - * ---------- - */ -static void -get_values_def(List *values_lists, deparse_context *context) -{ - StringInfo buf = context->buf; - bool first_list = true; - ListCell *vtl; - - appendStringInfoString(buf, "VALUES "); - - foreach(vtl, values_lists) - { - List *sublist = (List *) lfirst(vtl); - bool first_col = true; - ListCell *lc; - - if (first_list) - first_list = false; - else - appendStringInfoString(buf, ", "); - - appendStringInfoChar(buf, '('); - foreach(lc, sublist) - { - Node *col = (Node *) lfirst(lc); - - if (first_col) - first_col = false; - else - appendStringInfoChar(buf, ','); - - /* - * Print the value. Whole-row Vars need special treatment. - */ - get_rule_expr_toplevel(col, context, false); - } - appendStringInfoChar(buf, ')'); - } -} - -/* ---------- - * get_with_clause - Parse back a WITH clause - * ---------- - */ -static void -get_with_clause(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - const char *sep; - ListCell *l; - - if (query->cteList == NIL) - return; - - if (PRETTY_INDENT(context)) - { - context->indentLevel += PRETTYINDENT_STD; - appendStringInfoChar(buf, ' '); - } - - if (query->hasRecursive) - sep = "WITH RECURSIVE "; - else - sep = "WITH "; - foreach(l, query->cteList) - { - CommonTableExpr *cte = (CommonTableExpr *) lfirst(l); - - appendStringInfoString(buf, sep); - appendStringInfoString(buf, quote_identifier(cte->ctename)); - if (cte->aliascolnames) - { - bool first = true; - ListCell *col; - - appendStringInfoChar(buf, '('); - foreach(col, cte->aliascolnames) - { - if (first) - first = false; - else - appendStringInfoString(buf, ", "); - appendStringInfoString(buf, - quote_identifier(strVal(lfirst(col)))); - } - appendStringInfoChar(buf, ')'); - } - appendStringInfoString(buf, " AS ("); - if (PRETTY_INDENT(context)) - appendContextKeyword(context, "", 0, 0, 0); - get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - if (PRETTY_INDENT(context)) - appendContextKeyword(context, "", 0, 0, 0); - appendStringInfoChar(buf, ')'); - sep = ", "; - } - - if (PRETTY_INDENT(context)) - { - context->indentLevel -= PRETTYINDENT_STD; - appendContextKeyword(context, "", 0, 0, 0); - } - else - appendStringInfoChar(buf, ' '); -} - -/* ---------- - * get_select_query_def - Parse back a SELECT parsetree - * ---------- - */ -static void -get_select_query_def(Query *query, deparse_context *context, - TupleDesc resultDesc) -{ - StringInfo buf = context->buf; - List *save_windowclause; - List *save_windowtlist; - bool force_colno; - ListCell *l; - - /* Insert the WITH clause if given */ - get_with_clause(query, context); - - /* Set up context for possible window functions */ - save_windowclause = context->windowClause; - context->windowClause = query->windowClause; - save_windowtlist = context->windowTList; - context->windowTList = query->targetList; - - /* - * If the Query node has a setOperations tree, then it's the top level of - * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT - * fields are interesting in the top query itself. - */ - if (query->setOperations) - { - get_setop_query(query->setOperations, query, context, resultDesc); - /* ORDER BY clauses must be simple in this case */ - force_colno = true; - } - else - { - get_basic_select_query(query, context, resultDesc); - force_colno = false; - } - - /* Add the ORDER BY clause if given */ - if (query->sortClause != NIL) - { - appendContextKeyword(context, " ORDER BY ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_orderby(query->sortClause, query->targetList, - force_colno, context); - } - - /* Add the LIMIT clause if given */ - if (query->limitOffset != NULL) - { - appendContextKeyword(context, " OFFSET ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - get_rule_expr(query->limitOffset, context, false); - } - if (query->limitCount != NULL) - { - appendContextKeyword(context, " LIMIT ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - if (IsA(query->limitCount, Const) && - ((Const *) query->limitCount)->constisnull) - appendStringInfoString(buf, "ALL"); - else - get_rule_expr(query->limitCount, context, false); - } - - /* Add FOR [KEY] UPDATE/SHARE clauses if present */ - if (query->hasForUpdate) - { - foreach(l, query->rowMarks) - { - RowMarkClause *rc = (RowMarkClause *) lfirst(l); - - /* don't print implicit clauses */ - if (rc->pushedDown) - continue; - - switch (rc->strength) - { - case LCS_NONE: - /* we intentionally throw an error for LCS_NONE */ - elog(ERROR, "unrecognized LockClauseStrength %d", - (int) rc->strength); - break; - case LCS_FORKEYSHARE: - appendContextKeyword(context, " FOR KEY SHARE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - break; - case LCS_FORSHARE: - appendContextKeyword(context, " FOR SHARE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - break; - case LCS_FORNOKEYUPDATE: - appendContextKeyword(context, " FOR NO KEY UPDATE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - break; - case LCS_FORUPDATE: - appendContextKeyword(context, " FOR UPDATE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - break; - } - - appendStringInfo(buf, " OF %s", - quote_identifier(get_rtable_name(rc->rti, - context))); - if (rc->waitPolicy == LockWaitError) - appendStringInfoString(buf, " NOWAIT"); - else if (rc->waitPolicy == LockWaitSkip) - appendStringInfoString(buf, " SKIP LOCKED"); - } - } - - context->windowClause = save_windowclause; - context->windowTList = save_windowtlist; -} - -/* - * Detect whether query looks like SELECT ... FROM VALUES(); - * if so, return the VALUES RTE. Otherwise return NULL. - */ -static RangeTblEntry * -get_simple_values_rte(Query *query) -{ - RangeTblEntry *result = NULL; - ListCell *lc; - - /* - * We want to return TRUE even if the Query also contains OLD or NEW rule - * RTEs. So the idea is to scan the rtable and see if there is only one - * inFromCl RTE that is a VALUES RTE. - */ - foreach(lc, query->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (rte->rtekind == RTE_VALUES && rte->inFromCl) - { - if (result) - return NULL; /* multiple VALUES (probably not possible) */ - result = rte; - } - else if (rte->rtekind == RTE_RELATION && !rte->inFromCl) - continue; /* ignore rule entries */ - else - return NULL; /* something else -> not simple VALUES */ - } - - /* - * We don't need to check the targetlist in any great detail, because - * parser/analyze.c will never generate a "bare" VALUES RTE --- they only - * appear inside auto-generated sub-queries with very restricted - * structure. However, DefineView might have modified the tlist by - * injecting new column aliases; so compare tlist resnames against the - * RTE's names to detect that. - */ - if (result) - { - ListCell *lcn; - - if (list_length(query->targetList) != list_length(result->eref->colnames)) - return NULL; /* this probably cannot happen */ - forboth(lc, query->targetList, lcn, result->eref->colnames) - { - TargetEntry *tle = (TargetEntry *) lfirst(lc); - char *cname = strVal(lfirst(lcn)); - - if (tle->resjunk) - return NULL; /* this probably cannot happen */ - if (tle->resname == NULL || strcmp(tle->resname, cname) != 0) - return NULL; /* column name has been changed */ - } - } - - return result; -} - -static void -get_basic_select_query(Query *query, deparse_context *context, - TupleDesc resultDesc) -{ - StringInfo buf = context->buf; - RangeTblEntry *values_rte; - char *sep; - ListCell *l; - - if (PRETTY_INDENT(context)) - { - context->indentLevel += PRETTYINDENT_STD; - appendStringInfoChar(buf, ' '); - } - - /* - * If the query looks like SELECT * FROM (VALUES ...), then print just the - * VALUES part. This reverses what transformValuesClause() did at parse - * time. - */ - values_rte = get_simple_values_rte(query); - if (values_rte) - { - get_values_def(values_rte->values_lists, context); - return; - } - - /* - * Build up the query string - first we say SELECT - */ - appendStringInfoString(buf, "SELECT"); - - /* Add the DISTINCT clause if given */ - if (query->distinctClause != NIL) - { - if (query->hasDistinctOn) - { - appendStringInfoString(buf, " DISTINCT ON ("); - sep = ""; - foreach(l, query->distinctClause) - { - SortGroupClause *srt = (SortGroupClause *) lfirst(l); - - appendStringInfoString(buf, sep); - get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList, - false, context); - sep = ", "; - } - appendStringInfoChar(buf, ')'); - } - else - appendStringInfoString(buf, " DISTINCT"); - } - - /* Then we tell what to select (the targetlist) */ - get_target_list(query->targetList, context, resultDesc); - - /* Add the FROM clause if needed */ - get_from_clause(query, " FROM ", context); - - /* Add the WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(query->jointree->quals, context, false); - } - - /* Add the GROUP BY clause if given */ - if (query->groupClause != NULL || query->groupingSets != NULL) - { - ParseExprKind save_exprkind; - - appendContextKeyword(context, " GROUP BY ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - - save_exprkind = context->special_exprkind; - context->special_exprkind = EXPR_KIND_GROUP_BY; - - if (query->groupingSets == NIL) - { - sep = ""; - foreach(l, query->groupClause) - { - SortGroupClause *grp = (SortGroupClause *) lfirst(l); - - appendStringInfoString(buf, sep); - get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList, - false, context); - sep = ", "; - } - } - else - { - sep = ""; - foreach(l, query->groupingSets) - { - GroupingSet *grp = lfirst(l); - - appendStringInfoString(buf, sep); - get_rule_groupingset(grp, query->targetList, true, context); - sep = ", "; - } - } - - context->special_exprkind = save_exprkind; - } - - /* Add the HAVING clause if given */ - if (query->havingQual != NULL) - { - appendContextKeyword(context, " HAVING ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - get_rule_expr(query->havingQual, context, false); - } - - /* Add the WINDOW clause if needed */ - if (query->windowClause != NIL) - get_rule_windowclause(query, context); -} - -/* ---------- - * get_target_list - Parse back a SELECT target list - * - * This is also used for RETURNING lists in INSERT/UPDATE/DELETE. - * ---------- - */ -static void -get_target_list(List *targetList, deparse_context *context, - TupleDesc resultDesc) -{ - StringInfo buf = context->buf; - StringInfoData targetbuf; - bool last_was_multiline = false; - char *sep; - int colno; - ListCell *l; - - /* we use targetbuf to hold each TLE's text temporarily */ - initStringInfo(&targetbuf); - - sep = " "; - colno = 0; - foreach(l, targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - char *colname; - char *attname; - - if (tle->resjunk) - continue; /* ignore junk entries */ - - appendStringInfoString(buf, sep); - sep = ", "; - colno++; - - /* - * Put the new field text into targetbuf so we can decide after we've - * got it whether or not it needs to go on a new line. - */ - resetStringInfo(&targetbuf); - context->buf = &targetbuf; - - /* - * We special-case Var nodes rather than using get_rule_expr. This is - * needed because get_rule_expr will display a whole-row Var as - * "foo.*", which is the preferred notation in most contexts, but at - * the top level of a SELECT list it's not right (the parser will - * expand that notation into multiple columns, yielding behavior - * different from a whole-row Var). We need to call get_variable - * directly so that we can tell it to do the right thing, and so that - * we can get the attribute name which is the default AS label. - */ - if (tle->expr && (IsA(tle->expr, Var))) - { - attname = get_variable((Var *) tle->expr, 0, true, context); - } - else - { - get_rule_expr((Node *) tle->expr, context, true); - /* We'll show the AS name unless it's this: */ - attname = "?column?"; - } - - /* - * Figure out what the result column should be called. In the context - * of a view, use the view's tuple descriptor (so as to pick up the - * effects of any column RENAME that's been done on the view). - * Otherwise, just use what we can find in the TLE. - */ - if (resultDesc && colno <= resultDesc->natts) - colname = NameStr(resultDesc->attrs[colno - 1]->attname); - else - colname = tle->resname; - - /* Show AS unless the column's name is correct as-is */ - if (colname) /* resname could be NULL */ - { - if (attname == NULL || strcmp(attname, colname) != 0) - appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname)); - } - - /* Restore context's output buffer */ - context->buf = buf; - - /* Consider line-wrapping if enabled */ - if (PRETTY_INDENT(context) && context->wrapColumn >= 0) - { - int leading_nl_pos; - - /* Does the new field start with a new line? */ - if (targetbuf.len > 0 && targetbuf.data[0] == '\n') - leading_nl_pos = 0; - else - leading_nl_pos = -1; - - /* If so, we shouldn't add anything */ - if (leading_nl_pos >= 0) - { - /* instead, remove any trailing spaces currently in buf */ - removeStringInfoSpaces(buf); - } - else - { - char *trailing_nl; - - /* Locate the start of the current line in the output buffer */ - trailing_nl = strrchr(buf->data, '\n'); - if (trailing_nl == NULL) - trailing_nl = buf->data; - else - trailing_nl++; - - /* - * Add a newline, plus some indentation, if the new field is - * not the first and either the new field would cause an - * overflow or the last field used more than one line. - */ - if (colno > 1 && - ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) || - last_was_multiline)) - appendContextKeyword(context, "", -PRETTYINDENT_STD, - PRETTYINDENT_STD, PRETTYINDENT_VAR); - } - - /* Remember this field's multiline status for next iteration */ - last_was_multiline = - (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL); - } - - /* Add the new field */ - appendStringInfoString(buf, targetbuf.data); - } - - /* clean up */ - pfree(targetbuf.data); -} - -static void -get_setop_query(Node *setOp, Query *query, deparse_context *context, - TupleDesc resultDesc) -{ - StringInfo buf = context->buf; - bool need_paren; - - /* Guard against excessively long or deeply-nested queries */ - CHECK_FOR_INTERRUPTS(); - check_stack_depth(); - - if (IsA(setOp, RangeTblRef)) - { - RangeTblRef *rtr = (RangeTblRef *) setOp; - RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); - Query *subquery = rte->subquery; - - Assert(subquery != NULL); - Assert(subquery->setOperations == NULL); - /* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */ - need_paren = (subquery->cteList || - subquery->sortClause || - subquery->rowMarks || - subquery->limitOffset || - subquery->limitCount); - if (need_paren) - appendStringInfoChar(buf, '('); - get_query_def(subquery, buf, context->namespaces, resultDesc, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - if (need_paren) - appendStringInfoChar(buf, ')'); - } - else if (IsA(setOp, SetOperationStmt)) - { - SetOperationStmt *op = (SetOperationStmt *) setOp; - int subindent; - - /* - * We force parens when nesting two SetOperationStmts, except when the - * lefthand input is another setop of the same kind. Syntactically, - * we could omit parens in rather more cases, but it seems best to use - * parens to flag cases where the setop operator changes. If we use - * parens, we also increase the indentation level for the child query. - * - * There are some cases in which parens are needed around a leaf query - * too, but those are more easily handled at the next level down (see - * code above). - */ - if (IsA(op->larg, SetOperationStmt)) - { - SetOperationStmt *lop = (SetOperationStmt *) op->larg; - - if (op->op == lop->op && op->all == lop->all) - need_paren = false; - else - need_paren = true; - } - else - need_paren = false; - - if (need_paren) - { - appendStringInfoChar(buf, '('); - subindent = PRETTYINDENT_STD; - appendContextKeyword(context, "", subindent, 0, 0); - } - else - subindent = 0; - - get_setop_query(op->larg, query, context, resultDesc); - - if (need_paren) - appendContextKeyword(context, ") ", -subindent, 0, 0); - else if (PRETTY_INDENT(context)) - appendContextKeyword(context, "", -subindent, 0, 0); - else - appendStringInfoChar(buf, ' '); - - switch (op->op) - { - case SETOP_UNION: - appendStringInfoString(buf, "UNION "); - break; - case SETOP_INTERSECT: - appendStringInfoString(buf, "INTERSECT "); - break; - case SETOP_EXCEPT: - appendStringInfoString(buf, "EXCEPT "); - break; - default: - elog(ERROR, "unrecognized set op: %d", - (int) op->op); - } - if (op->all) - appendStringInfoString(buf, "ALL "); - - /* Always parenthesize if RHS is another setop */ - need_paren = IsA(op->rarg, SetOperationStmt); - - /* - * The indentation code here is deliberately a bit different from that - * for the lefthand input, because we want the line breaks in - * different places. - */ - if (need_paren) - { - appendStringInfoChar(buf, '('); - subindent = PRETTYINDENT_STD; - } - else - subindent = 0; - appendContextKeyword(context, "", subindent, 0, 0); - - get_setop_query(op->rarg, query, context, resultDesc); - - if (PRETTY_INDENT(context)) - context->indentLevel -= subindent; - if (need_paren) - appendContextKeyword(context, ")", 0, 0, 0); - } - else - { - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(setOp)); - } -} - -/* - * Display a sort/group clause. - * - * Also returns the expression tree, so caller need not find it again. - */ -static Node * -get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno, - deparse_context *context) -{ - StringInfo buf = context->buf; - TargetEntry *tle; - Node *expr; - - tle = get_sortgroupref_tle(ref, tlist); - expr = (Node *) tle->expr; - - /* - * Use column-number form if requested by caller. Otherwise, if - * expression is a constant, force it to be dumped with an explicit cast - * as decoration --- this is because a simple integer constant is - * ambiguous (and will be misinterpreted by findTargetlistEntry()) if we - * dump it without any decoration. If it's anything more complex than a - * simple Var, then force extra parens around it, to ensure it can't be - * misinterpreted as a cube() or rollup() construct. - */ - if (force_colno) - { - Assert(!tle->resjunk); - appendStringInfo(buf, "%d", tle->resno); - } - else if (expr && IsA(expr, Const)) - get_const_expr((Const *) expr, context, 1); - else if (!expr || IsA(expr, Var)) - get_rule_expr(expr, context, true); - else - { - /* - * We must force parens for function-like expressions even if - * PRETTY_PAREN is off, since those are the ones in danger of - * misparsing. For other expressions we need to force them only if - * PRETTY_PAREN is on, since otherwise the expression will output them - * itself. (We can't skip the parens.) - */ - bool need_paren = (PRETTY_PAREN(context) - || IsA(expr, FuncExpr) - ||IsA(expr, Aggref) - ||IsA(expr, WindowFunc)); - - if (need_paren) - appendStringInfoString(context->buf, "("); - get_rule_expr(expr, context, true); - if (need_paren) - appendStringInfoString(context->buf, ")"); - } - - return expr; -} - -/* - * Display a GroupingSet - */ -static void -get_rule_groupingset(GroupingSet *gset, List *targetlist, - bool omit_parens, deparse_context *context) -{ - ListCell *l; - StringInfo buf = context->buf; - bool omit_child_parens = true; - char *sep = ""; - - switch (gset->kind) - { - case GROUPING_SET_EMPTY: - appendStringInfoString(buf, "()"); - return; - - case GROUPING_SET_SIMPLE: - { - if (!omit_parens || list_length(gset->content) != 1) - appendStringInfoString(buf, "("); - - foreach(l, gset->content) - { - Index ref = lfirst_int(l); - - appendStringInfoString(buf, sep); - get_rule_sortgroupclause(ref, targetlist, - false, context); - sep = ", "; - } - - if (!omit_parens || list_length(gset->content) != 1) - appendStringInfoString(buf, ")"); - } - return; - - case GROUPING_SET_ROLLUP: - appendStringInfoString(buf, "ROLLUP("); - break; - case GROUPING_SET_CUBE: - appendStringInfoString(buf, "CUBE("); - break; - case GROUPING_SET_SETS: - appendStringInfoString(buf, "GROUPING SETS ("); - omit_child_parens = false; - break; - } - - foreach(l, gset->content) - { - appendStringInfoString(buf, sep); - get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context); - sep = ", "; - } - - appendStringInfoString(buf, ")"); -} - -/* - * Display an ORDER BY list. - */ -static void -get_rule_orderby(List *orderList, List *targetList, - bool force_colno, deparse_context *context) -{ - StringInfo buf = context->buf; - const char *sep; - ListCell *l; - - sep = ""; - foreach(l, orderList) - { - SortGroupClause *srt = (SortGroupClause *) lfirst(l); - Node *sortexpr; - Oid sortcoltype; - TypeCacheEntry *typentry; - - appendStringInfoString(buf, sep); - sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList, - force_colno, context); - sortcoltype = exprType(sortexpr); - /* See whether operator is default < or > for datatype */ - typentry = lookup_type_cache(sortcoltype, - TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); - if (srt->sortop == typentry->lt_opr) - { - /* ASC is default, so emit nothing for it */ - if (srt->nulls_first) - appendStringInfoString(buf, " NULLS FIRST"); - } - else if (srt->sortop == typentry->gt_opr) - { - appendStringInfoString(buf, " DESC"); - /* DESC defaults to NULLS FIRST */ - if (!srt->nulls_first) - appendStringInfoString(buf, " NULLS LAST"); - } - else - { - appendStringInfo(buf, " USING %s", - generate_operator_name(srt->sortop, - sortcoltype, - sortcoltype)); - /* be specific to eliminate ambiguity */ - if (srt->nulls_first) - appendStringInfoString(buf, " NULLS FIRST"); - else - appendStringInfoString(buf, " NULLS LAST"); - } - sep = ", "; - } -} - -/* - * Display a WINDOW clause. - * - * Note that the windowClause list might contain only anonymous window - * specifications, in which case we should print nothing here. - */ -static void -get_rule_windowclause(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - const char *sep; - ListCell *l; - - sep = NULL; - foreach(l, query->windowClause) - { - WindowClause *wc = (WindowClause *) lfirst(l); - - if (wc->name == NULL) - continue; /* ignore anonymous windows */ - - if (sep == NULL) - appendContextKeyword(context, " WINDOW ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - else - appendStringInfoString(buf, sep); - - appendStringInfo(buf, "%s AS ", quote_identifier(wc->name)); - - get_rule_windowspec(wc, query->targetList, context); - - sep = ", "; - } -} - -/* - * Display a window definition - */ -static void -get_rule_windowspec(WindowClause *wc, List *targetList, - deparse_context *context) -{ - StringInfo buf = context->buf; - bool needspace = false; - const char *sep; - ListCell *l; - - appendStringInfoChar(buf, '('); - if (wc->refname) - { - appendStringInfoString(buf, quote_identifier(wc->refname)); - needspace = true; - } - /* partition clauses are always inherited, so only print if no refname */ - if (wc->partitionClause && !wc->refname) - { - if (needspace) - appendStringInfoChar(buf, ' '); - appendStringInfoString(buf, "PARTITION BY "); - sep = ""; - foreach(l, wc->partitionClause) - { - SortGroupClause *grp = (SortGroupClause *) lfirst(l); - - appendStringInfoString(buf, sep); - get_rule_sortgroupclause(grp->tleSortGroupRef, targetList, - false, context); - sep = ", "; - } - needspace = true; - } - /* print ordering clause only if not inherited */ - if (wc->orderClause && !wc->copiedOrder) - { - if (needspace) - appendStringInfoChar(buf, ' '); - appendStringInfoString(buf, "ORDER BY "); - get_rule_orderby(wc->orderClause, targetList, false, context); - needspace = true; - } - /* framing clause is never inherited, so print unless it's default */ - if (wc->frameOptions & FRAMEOPTION_NONDEFAULT) - { - if (needspace) - appendStringInfoChar(buf, ' '); - if (wc->frameOptions & FRAMEOPTION_RANGE) - appendStringInfoString(buf, "RANGE "); - else if (wc->frameOptions & FRAMEOPTION_ROWS) - appendStringInfoString(buf, "ROWS "); - else - Assert(false); - if (wc->frameOptions & FRAMEOPTION_BETWEEN) - appendStringInfoString(buf, "BETWEEN "); - if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) - appendStringInfoString(buf, "UNBOUNDED PRECEDING "); - else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW) - appendStringInfoString(buf, "CURRENT ROW "); - else if (wc->frameOptions & FRAMEOPTION_START_VALUE) - { - get_rule_expr(wc->startOffset, context, false); - if (wc->frameOptions & FRAMEOPTION_START_VALUE_PRECEDING) - appendStringInfoString(buf, " PRECEDING "); - else if (wc->frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING) - appendStringInfoString(buf, " FOLLOWING "); - else - Assert(false); - } - else - Assert(false); - if (wc->frameOptions & FRAMEOPTION_BETWEEN) - { - appendStringInfoString(buf, "AND "); - if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) - appendStringInfoString(buf, "UNBOUNDED FOLLOWING "); - else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW) - appendStringInfoString(buf, "CURRENT ROW "); - else if (wc->frameOptions & FRAMEOPTION_END_VALUE) - { - get_rule_expr(wc->endOffset, context, false); - if (wc->frameOptions & FRAMEOPTION_END_VALUE_PRECEDING) - appendStringInfoString(buf, " PRECEDING "); - else if (wc->frameOptions & FRAMEOPTION_END_VALUE_FOLLOWING) - appendStringInfoString(buf, " FOLLOWING "); - else - Assert(false); - } - else - Assert(false); - } - /* we will now have a trailing space; remove it */ - buf->len--; - } - appendStringInfoChar(buf, ')'); -} - -/* ---------- - * get_insert_query_def - Parse back an INSERT parsetree - * ---------- - */ -static void -get_insert_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *select_rte = NULL; - RangeTblEntry *values_rte = NULL; - RangeTblEntry *rte; - char *sep; - ListCell *l; - List *strippedexprs; - - /* Insert the WITH clause if given */ - get_with_clause(query, context); - - /* - * If it's an INSERT ... SELECT or multi-row VALUES, there will be a - * single RTE for the SELECT or VALUES. Plain VALUES has neither. - */ - foreach(l, query->rtable) - { - rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind == RTE_SUBQUERY) - { - if (select_rte) - elog(ERROR, "too many subquery RTEs in INSERT"); - select_rte = rte; - } - - if (rte->rtekind == RTE_VALUES) - { - if (values_rte) - elog(ERROR, "too many values RTEs in INSERT"); - values_rte = rte; - } - } - if (select_rte && values_rte) - elog(ERROR, "both subquery and values RTEs in INSERT"); - - /* - * Start the query with INSERT INTO relname - */ - rte = rt_fetch(query->resultRelation, query->rtable); - Assert(rte->rtekind == RTE_RELATION); - - if (PRETTY_INDENT(context)) - { - context->indentLevel += PRETTYINDENT_STD; - appendStringInfoChar(buf, ' '); - } - appendStringInfo(buf, "INSERT INTO %s ", - generate_relation_or_shard_name(rte->relid, - context->distrelid, - context->shardid, NIL)); - /* INSERT requires AS keyword for target alias */ - if (rte->alias != NULL) - appendStringInfo(buf, "AS %s ", - quote_identifier(rte->alias->aliasname)); - - /* - * Add the insert-column-names list. Any indirection decoration needed on - * the column names can be inferred from the top targetlist. - */ - strippedexprs = NIL; - sep = ""; - if (query->targetList) - appendStringInfoChar(buf, '('); - foreach(l, query->targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - - if (tle->resjunk) - continue; /* ignore junk entries */ - - appendStringInfoString(buf, sep); - sep = ", "; - - /* - * Put out name of target column; look in the catalogs, not at - * tle->resname, since resname will fail to track RENAME. - */ - appendStringInfoString(buf, - quote_identifier(get_relid_attribute_name(rte->relid, - tle->resno))); - - /* - * Print any indirection needed (subfields or subscripts), and strip - * off the top-level nodes representing the indirection assignments. - * Add the stripped expressions to strippedexprs. (If it's a - * single-VALUES statement, the stripped expressions are the VALUES to - * print below. Otherwise they're just Vars and not really - * interesting.) - */ - strippedexprs = lappend(strippedexprs, - processIndirection((Node *) tle->expr, - context)); - } - if (query->targetList) - appendStringInfoString(buf, ") "); - - if (query->override) - { - if (query->override == OVERRIDING_SYSTEM_VALUE) - appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE "); - else if (query->override == OVERRIDING_USER_VALUE) - appendStringInfoString(buf, "OVERRIDING USER VALUE "); - } - - if (select_rte) - { - /* Add the SELECT */ - get_query_def(select_rte->subquery, buf, NIL, NULL, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - } - else if (values_rte) - { - /* Add the multi-VALUES expression lists */ - get_values_def(values_rte->values_lists, context); - } - else if (strippedexprs) - { - /* Add the single-VALUES expression list */ - appendContextKeyword(context, "VALUES (", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 2); - get_rule_expr((Node *) strippedexprs, context, false); - appendStringInfoChar(buf, ')'); - } - else - { - /* No expressions, so it must be DEFAULT VALUES */ - appendStringInfoString(buf, "DEFAULT VALUES"); - } - - /* Add ON CONFLICT if present */ - if (query->onConflict) - { - OnConflictExpr *confl = query->onConflict; - - appendStringInfoString(buf, " ON CONFLICT"); - - if (confl->arbiterElems) - { - /* Add the single-VALUES expression list */ - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) confl->arbiterElems, context, false); - appendStringInfoChar(buf, ')'); - - /* Add a WHERE clause (for partial indexes) if given */ - if (confl->arbiterWhere != NULL) - { - bool save_varprefix; - - /* - * Force non-prefixing of Vars, since parser assumes that they - * belong to target relation. WHERE clause does not use - * InferenceElem, so this is separately required. - */ - save_varprefix = context->varprefix; - context->varprefix = false; - - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(confl->arbiterWhere, context, false); - - context->varprefix = save_varprefix; - } - } - else if (OidIsValid(confl->constraint)) - { - char *constraint = get_constraint_name(confl->constraint); - int64 shardId = context->shardid; - - if (shardId > 0) - { - AppendShardIdToName(&constraint, shardId); - } - - if (!constraint) - elog(ERROR, "cache lookup failed for constraint %u", - confl->constraint); - appendStringInfo(buf, " ON CONSTRAINT %s", - quote_identifier(constraint)); - } - - if (confl->action == ONCONFLICT_NOTHING) - { - appendStringInfoString(buf, " DO NOTHING"); - } - else - { - appendStringInfoString(buf, " DO UPDATE SET "); - /* Deparse targetlist */ - get_update_query_targetlist_def(query, confl->onConflictSet, - context, rte); - - /* Add a WHERE clause if given */ - if (confl->onConflictWhere != NULL) - { - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(confl->onConflictWhere, context, false); - } - } - } - - /* Add RETURNING if present */ - if (query->returningList) - { - appendContextKeyword(context, " RETURNING", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_target_list(query->returningList, context, NULL); - } -} - - -/* ---------- - * get_update_query_def - Parse back an UPDATE parsetree - * ---------- - */ -static void -get_update_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *rte; - - /* Insert the WITH clause if given */ - get_with_clause(query, context); - - /* - * Start the query with UPDATE relname SET - */ - rte = rt_fetch(query->resultRelation, query->rtable); - - if (PRETTY_INDENT(context)) - { - appendStringInfoChar(buf, ' '); - context->indentLevel += PRETTYINDENT_STD; - } - - /* if it's a shard, do differently */ - if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - char *fragmentSchemaName = NULL; - char *fragmentTableName = NULL; - - ExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL); - - /* use schema and table name from the remote alias */ - appendStringInfo(buf, "UPDATE %s%s", - only_marker(rte), - generate_fragment_name(fragmentSchemaName, fragmentTableName)); - - if(rte->eref != NULL) - appendStringInfo(buf, " %s", - quote_identifier(rte->eref->aliasname)); - } - else - { - appendStringInfo(buf, "UPDATE %s%s", - only_marker(rte), - generate_relation_or_shard_name(rte->relid, - context->distrelid, - context->shardid, NIL)); - - if (rte->alias != NULL) - appendStringInfo(buf, " %s", - quote_identifier(rte->alias->aliasname)); - } - - appendStringInfoString(buf, " SET "); - - /* Deparse targetlist */ - get_update_query_targetlist_def(query, query->targetList, context, rte); - - /* Add the FROM clause if needed */ - get_from_clause(query, " FROM ", context); - - /* Add a WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(query->jointree->quals, context, false); - } - - /* Add RETURNING if present */ - if (query->returningList) - { - appendContextKeyword(context, " RETURNING", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_target_list(query->returningList, context, NULL); - } -} - - -/* ---------- - * get_update_query_targetlist_def - Parse back an UPDATE targetlist - * ---------- - */ -static void -get_update_query_targetlist_def(Query *query, List *targetList, - deparse_context *context, RangeTblEntry *rte) -{ - StringInfo buf = context->buf; - ListCell *l; - ListCell *next_ma_cell; - int remaining_ma_columns; - const char *sep; - SubLink *cur_ma_sublink; - List *ma_sublinks; - - /* - * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks - * into a list. We expect them to appear, in ID order, in resjunk tlist - * entries. - */ - ma_sublinks = NIL; - if (query->hasSubLinks) /* else there can't be any */ - { - foreach(l, targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - - if (tle->resjunk && IsA(tle->expr, SubLink)) - { - SubLink *sl = (SubLink *) tle->expr; - - if (sl->subLinkType == MULTIEXPR_SUBLINK) - { - ma_sublinks = lappend(ma_sublinks, sl); - Assert(sl->subLinkId == list_length(ma_sublinks)); - } - } - } - } - next_ma_cell = list_head(ma_sublinks); - cur_ma_sublink = NULL; - remaining_ma_columns = 0; - - /* Add the comma separated list of 'attname = value' */ - sep = ""; - foreach(l, targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - Node *expr; - - if (tle->resjunk) - continue; /* ignore junk entries */ - - /* Emit separator (OK whether we're in multiassignment or not) */ - appendStringInfoString(buf, sep); - sep = ", "; - - /* - * Check to see if we're starting a multiassignment group: if so, - * output a left paren. - */ - if (next_ma_cell != NULL && cur_ma_sublink == NULL) - { - /* - * We must dig down into the expr to see if it's a PARAM_MULTIEXPR - * Param. That could be buried under FieldStores and ArrayRefs - * and CoerceToDomains (cf processIndirection()), and underneath - * those there could be an implicit type coercion. Because we - * would ignore implicit type coercions anyway, we don't need to - * be as careful as processIndirection() is about descending past - * implicit CoerceToDomains. - */ - expr = (Node *) tle->expr; - while (expr) - { - if (IsA(expr, FieldStore)) - { - FieldStore *fstore = (FieldStore *) expr; - - expr = (Node *) linitial(fstore->newvals); - } - else if (IsA(expr, ArrayRef)) - { - ArrayRef *aref = (ArrayRef *) expr; - - if (aref->refassgnexpr == NULL) - break; - expr = (Node *) aref->refassgnexpr; - } - else if (IsA(expr, CoerceToDomain)) - { - CoerceToDomain *cdomain = (CoerceToDomain *) expr; - - if (cdomain->coercionformat != COERCE_IMPLICIT_CAST) - break; - expr = (Node *) cdomain->arg; - } - else - break; - } - expr = strip_implicit_coercions(expr); - - if (expr && IsA(expr, Param) && - ((Param *) expr)->paramkind == PARAM_MULTIEXPR) - { - cur_ma_sublink = (SubLink *) lfirst(next_ma_cell); - next_ma_cell = lnext(next_ma_cell); - remaining_ma_columns = count_nonjunk_tlist_entries( - ((Query *) cur_ma_sublink->subselect)->targetList); - Assert(((Param *) expr)->paramid == - ((cur_ma_sublink->subLinkId << 16) | 1)); - appendStringInfoChar(buf, '('); - } - } - - /* - * Put out name of target column; look in the catalogs, not at - * tle->resname, since resname will fail to track RENAME. - */ - appendStringInfoString(buf, - quote_identifier(get_relid_attribute_name(rte->relid, - tle->resno))); - - /* - * Print any indirection needed (subfields or subscripts), and strip - * off the top-level nodes representing the indirection assignments. - */ - expr = processIndirection((Node *) tle->expr, context); - - /* - * If we're in a multiassignment, skip printing anything more, unless - * this is the last column; in which case, what we print should be the - * sublink, not the Param. - */ - if (cur_ma_sublink != NULL) - { - if (--remaining_ma_columns > 0) - continue; /* not the last column of multiassignment */ - appendStringInfoChar(buf, ')'); - expr = (Node *) cur_ma_sublink; - cur_ma_sublink = NULL; - } - - appendStringInfoString(buf, " = "); - - get_rule_expr(expr, context, false); - } -} - - -/* ---------- - * get_delete_query_def - Parse back a DELETE parsetree - * ---------- - */ -static void -get_delete_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *rte; - - /* Insert the WITH clause if given */ - get_with_clause(query, context); - - /* - * Start the query with DELETE FROM relname - */ - rte = rt_fetch(query->resultRelation, query->rtable); - - if (PRETTY_INDENT(context)) - { - appendStringInfoChar(buf, ' '); - context->indentLevel += PRETTYINDENT_STD; - } - - /* if it's a shard, do differently */ - if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - char *fragmentSchemaName = NULL; - char *fragmentTableName = NULL; - - ExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL); - - /* use schema and table name from the remote alias */ - appendStringInfo(buf, "DELETE FROM %s%s", - only_marker(rte), - generate_fragment_name(fragmentSchemaName, fragmentTableName)); - - if(rte->eref != NULL) - appendStringInfo(buf, " %s", - quote_identifier(rte->eref->aliasname)); - } - else - { - appendStringInfo(buf, "DELETE FROM %s%s", - only_marker(rte), - generate_relation_or_shard_name(rte->relid, - context->distrelid, - context->shardid, NIL)); - - if (rte->alias != NULL) - appendStringInfo(buf, " %s", - quote_identifier(rte->alias->aliasname)); - } - - /* Add the USING clause if given */ - get_from_clause(query, " USING ", context); - - /* Add a WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(query->jointree->quals, context, false); - } - - /* Add RETURNING if present */ - if (query->returningList) - { - appendContextKeyword(context, " RETURNING", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_target_list(query->returningList, context, NULL); - } -} - - -/* ---------- - * get_utility_query_def - Parse back a UTILITY parsetree - * ---------- - */ -static void -get_utility_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - - if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt)) - { - NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt; - - appendContextKeyword(context, "", - 0, PRETTYINDENT_STD, 1); - appendStringInfo(buf, "NOTIFY %s", - quote_identifier(stmt->conditionname)); - if (stmt->payload) - { - appendStringInfoString(buf, ", "); - simple_quote_literal(buf, stmt->payload); - } - } - else if (query->utilityStmt && IsA(query->utilityStmt, TruncateStmt)) - { - TruncateStmt *stmt = (TruncateStmt *) query->utilityStmt; - List *relationList = stmt->relations; - ListCell *relationCell = NULL; - - appendContextKeyword(context, "", - 0, PRETTYINDENT_STD, 1); - - appendStringInfo(buf, "TRUNCATE TABLE"); - - foreach(relationCell, relationList) - { - RangeVar *relationVar = (RangeVar *) lfirst(relationCell); - Oid relationId = RangeVarGetRelid(relationVar, NoLock, false); - char *relationName = generate_relation_or_shard_name(relationId, - context->distrelid, - context->shardid, NIL); - appendStringInfo(buf, " %s", relationName); - - if (lnext(relationCell) != NULL) - { - appendStringInfo(buf, ","); - } - } - - if (stmt->restart_seqs) - { - appendStringInfo(buf, " RESTART IDENTITY"); - } - - if (stmt->behavior == DROP_CASCADE) - { - appendStringInfo(buf, " CASCADE"); - } - } - else - { - /* Currently only NOTIFY utility commands can appear in rules */ - elog(ERROR, "unexpected utility statement type"); - } -} - -/* - * Display a Var appropriately. - * - * In some cases (currently only when recursing into an unnamed join) - * the Var's varlevelsup has to be interpreted with respect to a context - * above the current one; levelsup indicates the offset. - * - * If istoplevel is TRUE, the Var is at the top level of a SELECT's - * targetlist, which means we need special treatment of whole-row Vars. - * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a - * dirty hack to prevent "tab.*" from being expanded into multiple columns. - * (The parser will strip the useless coercion, so no inefficiency is added in - * dump and reload.) We used to print just "tab" in such cases, but that is - * ambiguous and will yield the wrong result if "tab" is also a plain column - * name in the query. - * - * Returns the attname of the Var, or NULL if the Var has no attname (because - * it is a whole-row Var or a subplan output reference). - */ -static char * -get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *rte; - AttrNumber attnum; - int netlevelsup; - deparse_namespace *dpns; - deparse_columns *colinfo; - char *refname; - char *attname; - - /* Find appropriate nesting depth */ - netlevelsup = var->varlevelsup + levelsup; - if (netlevelsup >= list_length(context->namespaces)) - elog(ERROR, "bogus varlevelsup: %d offset %d", - var->varlevelsup, levelsup); - dpns = (deparse_namespace *) list_nth(context->namespaces, - netlevelsup); - - /* - * Try to find the relevant RTE in this rtable. In a plan tree, it's - * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig - * down into the subplans, or INDEX_VAR, which is resolved similarly. Also - * find the aliases previously assigned for this RTE. - */ - if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) - { - rte = rt_fetch(var->varno, dpns->rtable); - refname = (char *) list_nth(dpns->rtable_names, var->varno - 1); - colinfo = deparse_columns_fetch(var->varno, dpns); - attnum = var->varattno; - } - else - { - resolve_special_varno((Node *) var, context, NULL, - get_special_variable); - return NULL; - } - - /* - * The planner will sometimes emit Vars referencing resjunk elements of a - * subquery's target list (this is currently only possible if it chooses - * to generate a "physical tlist" for a SubqueryScan or CteScan node). - * Although we prefer to print subquery-referencing Vars using the - * subquery's alias, that's not possible for resjunk items since they have - * no alias. So in that case, drill down to the subplan and print the - * contents of the referenced tlist item. This works because in a plan - * tree, such Vars can only occur in a SubqueryScan or CteScan node, and - * we'll have set dpns->inner_planstate to reference the child plan node. - */ - if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) && - attnum > list_length(rte->eref->colnames) && - dpns->inner_planstate) - { - TargetEntry *tle; - deparse_namespace save_dpns; - - tle = get_tle_by_resno(dpns->inner_tlist, var->varattno); - if (!tle) - elog(ERROR, "invalid attnum %d for relation \"%s\"", - var->varattno, rte->eref->aliasname); - - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); - - /* - * Force parentheses because our caller probably assumed a Var is a - * simple expression. - */ - if (!IsA(tle->expr, Var)) - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) tle->expr, context, true); - if (!IsA(tle->expr, Var)) - appendStringInfoChar(buf, ')'); - - pop_child_plan(dpns, &save_dpns); - return NULL; - } - - /* - * If it's an unnamed join, look at the expansion of the alias variable. - * If it's a simple reference to one of the input vars, then recursively - * print the name of that var instead. When it's not a simple reference, - * we have to just print the unqualified join column name. (This can only - * happen with "dangerous" merged columns in a JOIN USING; we took pains - * previously to make the unqualified column name unique in such cases.) - * - * This wouldn't work in decompiling plan trees, because we don't store - * joinaliasvars lists after planning; but a plan tree should never - * contain a join alias variable. - */ - if (rte->rtekind == RTE_JOIN && rte->alias == NULL) - { - if (rte->joinaliasvars == NIL) - elog(ERROR, "cannot decompile join alias var in plan tree"); - if (attnum > 0) - { - Var *aliasvar; - - aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); - /* we intentionally don't strip implicit coercions here */ - if (aliasvar && IsA(aliasvar, Var)) - { - return get_variable(aliasvar, var->varlevelsup + levelsup, - istoplevel, context); - } - } - - /* - * Unnamed join has no refname. (Note: since it's unnamed, there is - * no way the user could have referenced it to create a whole-row Var - * for it. So we don't have to cover that case below.) - */ - Assert(refname == NULL); - } - - if (attnum == InvalidAttrNumber) - attname = NULL; - else if (attnum > 0) - { - /* Get column name to use from the colinfo struct */ - if (attnum > colinfo->num_cols) - elog(ERROR, "invalid attnum %d for relation \"%s\"", - attnum, rte->eref->aliasname); - attname = colinfo->colnames[attnum - 1]; - if (attname == NULL) /* dropped column? */ - elog(ERROR, "invalid attnum %d for relation \"%s\"", - attnum, rte->eref->aliasname); - } - else if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - /* System column on a Citus shard */ - attname = get_relid_attribute_name(rte->relid, attnum); - } - else - { - /* System column - name is fixed, get it from the catalog */ - attname = get_rte_attribute_name(rte, attnum); - } - - if (refname && (context->varprefix || attname == NULL)) - { - appendStringInfoString(buf, quote_identifier(refname)); - appendStringInfoChar(buf, '.'); - } - if (attname) - appendStringInfoString(buf, quote_identifier(attname)); - else - { - appendStringInfoChar(buf, '*'); - if (istoplevel) - appendStringInfo(buf, "::%s", - format_type_with_typemod(var->vartype, - var->vartypmod)); - } - - return attname; -} - -/* - * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This - * routine is actually a callback for get_special_varno, which handles finding - * the correct TargetEntry. We get the expression contained in that - * TargetEntry and just need to deparse it, a job we can throw back on - * get_rule_expr. - */ -static void -get_special_variable(Node *node, deparse_context *context, void *private) -{ - StringInfo buf = context->buf; - - /* - * Force parentheses because our caller probably assumed a Var is a simple - * expression. - */ - if (!IsA(node, Var)) - appendStringInfoChar(buf, '('); - get_rule_expr(node, context, true); - if (!IsA(node, Var)) - appendStringInfoChar(buf, ')'); -} - -/* - * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR, - * INDEX_VAR) until we find a real Var or some kind of non-Var node; then, - * invoke the callback provided. - */ -static void -resolve_special_varno(Node *node, deparse_context *context, void *private, - void (*callback) (Node *, deparse_context *, void *)) -{ - Var *var; - deparse_namespace *dpns; - - /* If it's not a Var, invoke the callback. */ - if (!IsA(node, Var)) - { - callback(node, context, private); - return; - } - - /* Find appropriate nesting depth */ - var = (Var *) node; - dpns = (deparse_namespace *) list_nth(context->namespaces, - var->varlevelsup); - - /* - * It's a special RTE, so recurse. - */ - if (var->varno == OUTER_VAR && dpns->outer_tlist) - { - TargetEntry *tle; - deparse_namespace save_dpns; - - tle = get_tle_by_resno(dpns->outer_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno); - - push_child_plan(dpns, dpns->outer_planstate, &save_dpns); - resolve_special_varno((Node *) tle->expr, context, private, callback); - pop_child_plan(dpns, &save_dpns); - return; - } - else if (var->varno == INNER_VAR && dpns->inner_tlist) - { - TargetEntry *tle; - deparse_namespace save_dpns; - - tle = get_tle_by_resno(dpns->inner_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno); - - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); - resolve_special_varno((Node *) tle->expr, context, private, callback); - pop_child_plan(dpns, &save_dpns); - return; - } - else if (var->varno == INDEX_VAR && dpns->index_tlist) - { - TargetEntry *tle; - - tle = get_tle_by_resno(dpns->index_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno); - - resolve_special_varno((Node *) tle->expr, context, private, callback); - return; - } - else if (var->varno < 1 || var->varno > list_length(dpns->rtable)) - elog(ERROR, "bogus varno: %d", var->varno); - - /* Not special. Just invoke the callback. */ - callback(node, context, private); -} - -/* - * Get the name of a field of an expression of composite type. The - * expression is usually a Var, but we handle other cases too. - * - * levelsup is an extra offset to interpret the Var's varlevelsup correctly. - * - * This is fairly straightforward when the expression has a named composite - * type; we need only look up the type in the catalogs. However, the type - * could also be RECORD. Since no actual table or view column is allowed to - * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE - * or to a subquery output. We drill down to find the ultimate defining - * expression and attempt to infer the field name from it. We ereport if we - * can't determine the name. - * - * Similarly, a PARAM of type RECORD has to refer to some expression of - * a determinable composite type. - */ -static const char * -get_name_for_var_field(Var *var, int fieldno, - int levelsup, deparse_context *context) -{ - RangeTblEntry *rte; - AttrNumber attnum; - int netlevelsup; - deparse_namespace *dpns; - TupleDesc tupleDesc; - Node *expr; - - /* - * If it's a RowExpr that was expanded from a whole-row Var, use the - * column names attached to it. - */ - if (IsA(var, RowExpr)) - { - RowExpr *r = (RowExpr *) var; - - if (fieldno > 0 && fieldno <= list_length(r->colnames)) - return strVal(list_nth(r->colnames, fieldno - 1)); - } - - /* - * If it's a Param of type RECORD, try to find what the Param refers to. - */ - if (IsA(var, Param)) - { - Param *param = (Param *) var; - ListCell *ancestor_cell; - - expr = find_param_referent(param, context, &dpns, &ancestor_cell); - if (expr) - { - /* Found a match, so recurse to decipher the field name */ - deparse_namespace save_dpns; - const char *result; - - push_ancestor_plan(dpns, ancestor_cell, &save_dpns); - result = get_name_for_var_field((Var *) expr, fieldno, - 0, context); - pop_ancestor_plan(dpns, &save_dpns); - return result; - } - } - - /* - * If it's a Var of type RECORD, we have to find what the Var refers to; - * if not, we can use get_expr_result_type. If that fails, we try - * lookup_rowtype_tupdesc, which will probably fail too, but will ereport - * an acceptable message. - */ - if (!IsA(var, Var) || - var->vartype != RECORDOID) - { - if (get_expr_result_type((Node *) var, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) - tupleDesc = lookup_rowtype_tupdesc_copy(exprType((Node *) var), - exprTypmod((Node *) var)); - Assert(tupleDesc); - /* Got the tupdesc, so we can extract the field name */ - Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); - return NameStr(tupleDesc->attrs[fieldno - 1]->attname); - } - - /* Find appropriate nesting depth */ - netlevelsup = var->varlevelsup + levelsup; - if (netlevelsup >= list_length(context->namespaces)) - elog(ERROR, "bogus varlevelsup: %d offset %d", - var->varlevelsup, levelsup); - dpns = (deparse_namespace *) list_nth(context->namespaces, - netlevelsup); - - /* - * Try to find the relevant RTE in this rtable. In a plan tree, it's - * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig - * down into the subplans, or INDEX_VAR, which is resolved similarly. - */ - if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) - { - rte = rt_fetch(var->varno, dpns->rtable); - attnum = var->varattno; - } - else if (var->varno == OUTER_VAR && dpns->outer_tlist) - { - TargetEntry *tle; - deparse_namespace save_dpns; - const char *result; - - tle = get_tle_by_resno(dpns->outer_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno); - - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->outer_planstate, &save_dpns); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - pop_child_plan(dpns, &save_dpns); - return result; - } - else if (var->varno == INNER_VAR && dpns->inner_tlist) - { - TargetEntry *tle; - deparse_namespace save_dpns; - const char *result; - - tle = get_tle_by_resno(dpns->inner_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno); - - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - pop_child_plan(dpns, &save_dpns); - return result; - } - else if (var->varno == INDEX_VAR && dpns->index_tlist) - { - TargetEntry *tle; - const char *result; - - tle = get_tle_by_resno(dpns->index_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno); - - Assert(netlevelsup == 0); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - return result; - } - else - { - elog(ERROR, "bogus varno: %d", var->varno); - return NULL; /* keep compiler quiet */ - } - - if (attnum == InvalidAttrNumber) - { - /* Var is whole-row reference to RTE, so select the right field */ - return get_rte_attribute_name(rte, fieldno); - } - - /* - * This part has essentially the same logic as the parser's - * expandRecordVariable() function, but we are dealing with a different - * representation of the input context, and we only need one field name - * not a TupleDesc. Also, we need special cases for finding subquery and - * CTE subplans when deparsing Plan trees. - */ - expr = (Node *) var; /* default if we can't drill down */ - - switch (rte->rtekind) - { - case RTE_RELATION: - case RTE_VALUES: - case RTE_NAMEDTUPLESTORE: - - /* - * This case should not occur: a column of a table or values list - * shouldn't have type RECORD. Fall through and fail (most - * likely) at the bottom. - */ - break; - case RTE_SUBQUERY: - /* Subselect-in-FROM: examine sub-select's output expr */ - { - if (rte->subquery) - { - TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList, - attnum); - - if (ste == NULL || ste->resjunk) - elog(ERROR, "subquery %s does not have attribute %d", - rte->eref->aliasname, attnum); - expr = (Node *) ste->expr; - if (IsA(expr, Var)) - { - /* - * Recurse into the sub-select to see what its Var - * refers to. We have to build an additional level of - * namespace to keep in step with varlevelsup in the - * subselect. - */ - deparse_namespace mydpns; - const char *result; - - set_deparse_for_query(&mydpns, rte->subquery, - context->namespaces); - - context->namespaces = lcons(&mydpns, - context->namespaces); - - result = get_name_for_var_field((Var *) expr, fieldno, - 0, context); - - context->namespaces = - list_delete_first(context->namespaces); - - return result; - } - /* else fall through to inspect the expression */ - } - else - { - /* - * We're deparsing a Plan tree so we don't have complete - * RTE entries (in particular, rte->subquery is NULL). But - * the only place we'd see a Var directly referencing a - * SUBQUERY RTE is in a SubqueryScan plan node, and we can - * look into the child plan's tlist instead. - */ - TargetEntry *tle; - deparse_namespace save_dpns; - const char *result; - - if (!dpns->inner_planstate) - elog(ERROR, "failed to find plan for subquery %s", - rte->eref->aliasname); - tle = get_tle_by_resno(dpns->inner_tlist, attnum); - if (!tle) - elog(ERROR, "bogus varattno for subquery var: %d", - attnum); - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - pop_child_plan(dpns, &save_dpns); - return result; - } - } - break; - case RTE_JOIN: - /* Join RTE --- recursively inspect the alias variable */ - if (rte->joinaliasvars == NIL) - elog(ERROR, "cannot decompile join alias var in plan tree"); - Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); - expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1); - Assert(expr != NULL); - /* we intentionally don't strip implicit coercions here */ - if (IsA(expr, Var)) - return get_name_for_var_field((Var *) expr, fieldno, - var->varlevelsup + levelsup, - context); - /* else fall through to inspect the expression */ - break; - case RTE_FUNCTION: - case RTE_TABLEFUNC: - - /* - * We couldn't get here unless a function is declared with one of - * its result columns as RECORD, which is not allowed. - */ - break; - case RTE_CTE: - /* CTE reference: examine subquery's output expr */ - { - CommonTableExpr *cte = NULL; - Index ctelevelsup; - ListCell *lc; - - /* - * Try to find the referenced CTE using the namespace stack. - */ - ctelevelsup = rte->ctelevelsup + netlevelsup; - if (ctelevelsup >= list_length(context->namespaces)) - lc = NULL; - else - { - deparse_namespace *ctedpns; - - ctedpns = (deparse_namespace *) - list_nth(context->namespaces, ctelevelsup); - foreach(lc, ctedpns->ctes) - { - cte = (CommonTableExpr *) lfirst(lc); - if (strcmp(cte->ctename, rte->ctename) == 0) - break; - } - } - if (lc != NULL) - { - Query *ctequery = (Query *) cte->ctequery; - TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte), - attnum); - - if (ste == NULL || ste->resjunk) - elog(ERROR, "subquery %s does not have attribute %d", - rte->eref->aliasname, attnum); - expr = (Node *) ste->expr; - if (IsA(expr, Var)) - { - /* - * Recurse into the CTE to see what its Var refers to. - * We have to build an additional level of namespace - * to keep in step with varlevelsup in the CTE. - * Furthermore it could be an outer CTE, so we may - * have to delete some levels of namespace. - */ - List *save_nslist = context->namespaces; - List *new_nslist; - deparse_namespace mydpns; - const char *result; - - set_deparse_for_query(&mydpns, ctequery, - context->namespaces); - - new_nslist = list_copy_tail(context->namespaces, - ctelevelsup); - context->namespaces = lcons(&mydpns, new_nslist); - - result = get_name_for_var_field((Var *) expr, fieldno, - 0, context); - - context->namespaces = save_nslist; - - return result; - } - /* else fall through to inspect the expression */ - } - else - { - /* - * We're deparsing a Plan tree so we don't have a CTE - * list. But the only place we'd see a Var directly - * referencing a CTE RTE is in a CteScan plan node, and we - * can look into the subplan's tlist instead. - */ - TargetEntry *tle; - deparse_namespace save_dpns; - const char *result; - - if (!dpns->inner_planstate) - elog(ERROR, "failed to find plan for CTE %s", - rte->eref->aliasname); - tle = get_tle_by_resno(dpns->inner_tlist, attnum); - if (!tle) - elog(ERROR, "bogus varattno for subquery var: %d", - attnum); - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_planstate, &save_dpns); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - pop_child_plan(dpns, &save_dpns); - return result; - } - } - break; - } - - /* - * We now have an expression we can't expand any more, so see if - * get_expr_result_type() can do anything with it. If not, pass to - * lookup_rowtype_tupdesc() which will probably fail, but will give an - * appropriate error message while failing. - */ - if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) - tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr), - exprTypmod(expr)); - Assert(tupleDesc); - /* Got the tupdesc, so we can extract the field name */ - Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); - return NameStr(tupleDesc->attrs[fieldno - 1]->attname); -} - -/* - * Try to find the referenced expression for a PARAM_EXEC Param that might - * reference a parameter supplied by an upper NestLoop or SubPlan plan node. - * - * If successful, return the expression and set *dpns_p and *ancestor_cell_p - * appropriately for calling push_ancestor_plan(). If no referent can be - * found, return NULL. - */ -static Node * -find_param_referent(Param *param, deparse_context *context, - deparse_namespace **dpns_p, ListCell **ancestor_cell_p) -{ - /* Initialize output parameters to prevent compiler warnings */ - *dpns_p = NULL; - *ancestor_cell_p = NULL; - - /* - * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or - * SubPlan argument. This will necessarily be in some ancestor of the - * current expression's PlanState. - */ - if (param->paramkind == PARAM_EXEC) - { - deparse_namespace *dpns; - PlanState *child_ps; - bool in_same_plan_level; - ListCell *lc; - - dpns = (deparse_namespace *) linitial(context->namespaces); - child_ps = dpns->planstate; - in_same_plan_level = true; - - foreach(lc, dpns->ancestors) - { - PlanState *ps = (PlanState *) lfirst(lc); - ListCell *lc2; - - /* - * NestLoops transmit params to their inner child only; also, once - * we've crawled up out of a subplan, this couldn't possibly be - * the right match. - */ - if (IsA(ps, NestLoopState) && - child_ps == innerPlanState(ps) && - in_same_plan_level) - { - NestLoop *nl = (NestLoop *) ps->plan; - - foreach(lc2, nl->nestParams) - { - NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2); - - if (nlp->paramno == param->paramid) - { - /* Found a match, so return it */ - *dpns_p = dpns; - *ancestor_cell_p = lc; - return (Node *) nlp->paramval; - } - } - } - - /* - * Check to see if we're crawling up from a subplan. - */ - foreach(lc2, ps->subPlan) - { - SubPlanState *sstate = (SubPlanState *) lfirst(lc2); - SubPlan *subplan = sstate->subplan; - ListCell *lc3; - ListCell *lc4; - - if (child_ps != sstate->planstate) - continue; - - /* Matched subplan, so check its arguments */ - forboth(lc3, subplan->parParam, lc4, subplan->args) - { - int paramid = lfirst_int(lc3); - Node *arg = (Node *) lfirst(lc4); - - if (paramid == param->paramid) - { - /* Found a match, so return it */ - *dpns_p = dpns; - *ancestor_cell_p = lc; - return arg; - } - } - - /* Keep looking, but we are emerging from a subplan. */ - in_same_plan_level = false; - break; - } - - /* - * Likewise check to see if we're emerging from an initplan. - * Initplans never have any parParams, so no need to search that - * list, but we need to know if we should reset - * in_same_plan_level. - */ - foreach(lc2, ps->initPlan) - { - SubPlanState *sstate = (SubPlanState *) lfirst(lc2); - - if (child_ps != sstate->planstate) - continue; - - /* No parameters to be had here. */ - Assert(sstate->subplan->parParam == NIL); - - /* Keep looking, but we are emerging from an initplan. */ - in_same_plan_level = false; - break; - } - - /* No luck, crawl up to next ancestor */ - child_ps = ps; - } - } - - /* No referent found */ - return NULL; -} - -/* - * Display a Param appropriately. - */ -static void -get_parameter(Param *param, deparse_context *context) -{ - Node *expr; - deparse_namespace *dpns; - ListCell *ancestor_cell; - - /* - * If it's a PARAM_EXEC parameter, try to locate the expression from which - * the parameter was computed. Note that failing to find a referent isn't - * an error, since the Param might well be a subplan output rather than an - * input. - */ - expr = find_param_referent(param, context, &dpns, &ancestor_cell); - if (expr) - { - /* Found a match, so print it */ - deparse_namespace save_dpns; - bool save_varprefix; - bool need_paren; - - /* Switch attention to the ancestor plan node */ - push_ancestor_plan(dpns, ancestor_cell, &save_dpns); - - /* - * Force prefixing of Vars, since they won't belong to the relation - * being scanned in the original plan node. - */ - save_varprefix = context->varprefix; - context->varprefix = true; - - /* - * A Param's expansion is typically a Var, Aggref, or upper-level - * Param, which wouldn't need extra parentheses. Otherwise, insert - * parens to ensure the expression looks atomic. - */ - need_paren = !(IsA(expr, Var) || - IsA(expr, Aggref) || - IsA(expr, Param)); - if (need_paren) - appendStringInfoChar(context->buf, '('); - - get_rule_expr(expr, context, false); - - if (need_paren) - appendStringInfoChar(context->buf, ')'); - - context->varprefix = save_varprefix; - - pop_ancestor_plan(dpns, &save_dpns); - - return; - } - - /* - * Not PARAM_EXEC, or couldn't find referent: just print $N. - */ - appendStringInfo(context->buf, "$%d", param->paramid); -} - -/* - * get_simple_binary_op_name - * - * helper function for isSimpleNode - * will return single char binary operator name, or NULL if it's not - */ -static const char * -get_simple_binary_op_name(OpExpr *expr) -{ - List *args = expr->args; - - if (list_length(args) == 2) - { - /* binary operator */ - Node *arg1 = (Node *) linitial(args); - Node *arg2 = (Node *) lsecond(args); - const char *op; - - op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2)); - if (strlen(op) == 1) - return op; - } - return NULL; -} - - -/* - * isSimpleNode - check if given node is simple (doesn't need parenthesizing) - * - * true : simple in the context of parent node's type - * false : not simple - */ -static bool -isSimpleNode(Node *node, Node *parentNode, int prettyFlags) -{ - if (!node) - return false; - - switch (nodeTag(node)) - { - case T_Var: - case T_Const: - case T_Param: - case T_CoerceToDomainValue: - case T_SetToDefault: - case T_CurrentOfExpr: - /* single words: always simple */ - return true; - - case T_ArrayRef: - case T_ArrayExpr: - case T_RowExpr: - case T_CoalesceExpr: - case T_MinMaxExpr: - case T_SQLValueFunction: - case T_XmlExpr: - case T_NextValueExpr: - case T_NullIfExpr: - case T_Aggref: - case T_WindowFunc: - case T_FuncExpr: - /* function-like: name(..) or name[..] */ - return true; - - /* CASE keywords act as parentheses */ - case T_CaseExpr: - return true; - - case T_FieldSelect: - - /* - * appears simple since . has top precedence, unless parent is - * T_FieldSelect itself! - */ - return (IsA(parentNode, FieldSelect) ? false : true); - - case T_FieldStore: - - /* - * treat like FieldSelect (probably doesn't matter) - */ - return (IsA(parentNode, FieldStore) ? false : true); - - case T_CoerceToDomain: - /* maybe simple, check args */ - return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg, - node, prettyFlags); - case T_RelabelType: - return isSimpleNode((Node *) ((RelabelType *) node)->arg, - node, prettyFlags); - case T_CoerceViaIO: - return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg, - node, prettyFlags); - case T_ArrayCoerceExpr: - return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg, - node, prettyFlags); - case T_ConvertRowtypeExpr: - return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg, - node, prettyFlags); - - case T_OpExpr: - { - /* depends on parent node type; needs further checking */ - if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr)) - { - const char *op; - const char *parentOp; - bool is_lopriop; - bool is_hipriop; - bool is_lopriparent; - bool is_hipriparent; - - op = get_simple_binary_op_name((OpExpr *) node); - if (!op) - return false; - - /* We know only the basic operators + - and * / % */ - is_lopriop = (strchr("+-", *op) != NULL); - is_hipriop = (strchr("*/%", *op) != NULL); - if (!(is_lopriop || is_hipriop)) - return false; - - parentOp = get_simple_binary_op_name((OpExpr *) parentNode); - if (!parentOp) - return false; - - is_lopriparent = (strchr("+-", *parentOp) != NULL); - is_hipriparent = (strchr("*/%", *parentOp) != NULL); - if (!(is_lopriparent || is_hipriparent)) - return false; - - if (is_hipriop && is_lopriparent) - return true; /* op binds tighter than parent */ - - if (is_lopriop && is_hipriparent) - return false; - - /* - * Operators are same priority --- can skip parens only if - * we have (a - b) - c, not a - (b - c). - */ - if (node == (Node *) linitial(((OpExpr *) parentNode)->args)) - return true; - - return false; - } - /* else do the same stuff as for T_SubLink et al. */ - } - /* FALLTHROUGH */ - - case T_SubLink: - case T_NullTest: - case T_BooleanTest: - case T_DistinctExpr: - switch (nodeTag(parentNode)) - { - case T_FuncExpr: - { - /* special handling for casts */ - CoercionForm type = ((FuncExpr *) parentNode)->funcformat; - - if (type == COERCE_EXPLICIT_CAST || - type == COERCE_IMPLICIT_CAST) - return false; - return true; /* own parentheses */ - } - case T_BoolExpr: /* lower precedence */ - case T_ArrayRef: /* other separators */ - case T_ArrayExpr: /* other separators */ - case T_RowExpr: /* other separators */ - case T_CoalesceExpr: /* own parentheses */ - case T_MinMaxExpr: /* own parentheses */ - case T_XmlExpr: /* own parentheses */ - case T_NullIfExpr: /* other separators */ - case T_Aggref: /* own parentheses */ - case T_WindowFunc: /* own parentheses */ - case T_CaseExpr: /* other separators */ - return true; - default: - return false; - } - - case T_BoolExpr: - switch (nodeTag(parentNode)) - { - case T_BoolExpr: - if (prettyFlags & PRETTYFLAG_PAREN) - { - BoolExprType type; - BoolExprType parentType; - - type = ((BoolExpr *) node)->boolop; - parentType = ((BoolExpr *) parentNode)->boolop; - switch (type) - { - case NOT_EXPR: - case AND_EXPR: - if (parentType == AND_EXPR || parentType == OR_EXPR) - return true; - break; - case OR_EXPR: - if (parentType == OR_EXPR) - return true; - break; - } - } - return false; - case T_FuncExpr: - { - /* special handling for casts */ - CoercionForm type = ((FuncExpr *) parentNode)->funcformat; - - if (type == COERCE_EXPLICIT_CAST || - type == COERCE_IMPLICIT_CAST) - return false; - return true; /* own parentheses */ - } - case T_ArrayRef: /* other separators */ - case T_ArrayExpr: /* other separators */ - case T_RowExpr: /* other separators */ - case T_CoalesceExpr: /* own parentheses */ - case T_MinMaxExpr: /* own parentheses */ - case T_XmlExpr: /* own parentheses */ - case T_NullIfExpr: /* other separators */ - case T_Aggref: /* own parentheses */ - case T_WindowFunc: /* own parentheses */ - case T_CaseExpr: /* other separators */ - return true; - default: - return false; - } - - default: - break; - } - /* those we don't know: in dubio complexo */ - return false; -} - - -/* - * appendContextKeyword - append a keyword to buffer - * - * If prettyPrint is enabled, perform a line break, and adjust indentation. - * Otherwise, just append the keyword. - */ -static void -appendContextKeyword(deparse_context *context, const char *str, - int indentBefore, int indentAfter, int indentPlus) -{ - StringInfo buf = context->buf; - - if (PRETTY_INDENT(context)) - { - int indentAmount; - - context->indentLevel += indentBefore; - - /* remove any trailing spaces currently in the buffer ... */ - removeStringInfoSpaces(buf); - /* ... then add a newline and some spaces */ - appendStringInfoChar(buf, '\n'); - - if (context->indentLevel < PRETTYINDENT_LIMIT) - indentAmount = Max(context->indentLevel, 0) + indentPlus; - else - { - /* - * If we're indented more than PRETTYINDENT_LIMIT characters, try - * to conserve horizontal space by reducing the per-level - * indentation. For best results the scale factor here should - * divide all the indent amounts that get added to indentLevel - * (PRETTYINDENT_STD, etc). It's important that the indentation - * not grow unboundedly, else deeply-nested trees use O(N^2) - * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT. - */ - indentAmount = PRETTYINDENT_LIMIT + - (context->indentLevel - PRETTYINDENT_LIMIT) / - (PRETTYINDENT_STD / 2); - indentAmount %= PRETTYINDENT_LIMIT; - /* scale/wrap logic affects indentLevel, but not indentPlus */ - indentAmount += indentPlus; - } - appendStringInfoSpaces(buf, indentAmount); - - appendStringInfoString(buf, str); - - context->indentLevel += indentAfter; - if (context->indentLevel < 0) - context->indentLevel = 0; - } - else - appendStringInfoString(buf, str); -} - -/* - * removeStringInfoSpaces - delete trailing spaces from a buffer. - * - * Possibly this should move to stringinfo.c at some point. - */ -static void -removeStringInfoSpaces(StringInfo str) -{ - while (str->len > 0 && str->data[str->len - 1] == ' ') - str->data[--(str->len)] = '\0'; -} - - -/* - * get_rule_expr_paren - deparse expr using get_rule_expr, - * embracing the string with parentheses if necessary for prettyPrint. - * - * Never embrace if prettyFlags=0, because it's done in the calling node. - * - * Any node that does *not* embrace its argument node by sql syntax (with - * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should - * use get_rule_expr_paren instead of get_rule_expr so parentheses can be - * added. - */ -static void -get_rule_expr_paren(Node *node, deparse_context *context, - bool showimplicit, Node *parentNode) -{ - bool need_paren; - - need_paren = PRETTY_PAREN(context) && - !isSimpleNode(node, parentNode, context->prettyFlags); - - if (need_paren) - appendStringInfoChar(context->buf, '('); - - get_rule_expr(node, context, showimplicit); - - if (need_paren) - appendStringInfoChar(context->buf, ')'); -} - - -/* ---------- - * get_rule_expr - Parse back an expression - * - * Note: showimplicit determines whether we display any implicit cast that - * is present at the top of the expression tree. It is a passed argument, - * not a field of the context struct, because we change the value as we - * recurse down into the expression. In general we suppress implicit casts - * when the result type is known with certainty (eg, the arguments of an - * OR must be boolean). We display implicit casts for arguments of functions - * and operators, since this is needed to be certain that the same function - * or operator will be chosen when the expression is re-parsed. - * ---------- - */ -static void -get_rule_expr(Node *node, deparse_context *context, - bool showimplicit) -{ - StringInfo buf = context->buf; - - if (node == NULL) - return; - - /* Guard against excessively long or deeply-nested queries */ - CHECK_FOR_INTERRUPTS(); - check_stack_depth(); - - /* - * Each level of get_rule_expr must emit an indivisible term - * (parenthesized if necessary) to ensure result is reparsed into the same - * expression tree. The only exception is that when the input is a List, - * we emit the component items comma-separated with no surrounding - * decoration; this is convenient for most callers. - */ - switch (nodeTag(node)) - { - case T_Var: - (void) get_variable((Var *) node, 0, false, context); - break; - - case T_Const: - get_const_expr((Const *) node, context, 0); - break; - - case T_Param: - get_parameter((Param *) node, context); - break; - - case T_Aggref: - get_agg_expr((Aggref *) node, context, (Aggref *) node); - break; - - case T_GroupingFunc: - { - GroupingFunc *gexpr = (GroupingFunc *) node; - - appendStringInfoString(buf, "GROUPING("); - get_rule_expr((Node *) gexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - - case T_WindowFunc: - get_windowfunc_expr((WindowFunc *) node, context); - break; - - case T_ArrayRef: - { - ArrayRef *aref = (ArrayRef *) node; - bool need_parens; - - /* - * If the argument is a CaseTestExpr, we must be inside a - * FieldStore, ie, we are assigning to an element of an array - * within a composite column. Since we already punted on - * displaying the FieldStore's target information, just punt - * here too, and display only the assignment source - * expression. - */ - if (IsA(aref->refexpr, CaseTestExpr)) - { - Assert(aref->refassgnexpr); - get_rule_expr((Node *) aref->refassgnexpr, - context, showimplicit); - break; - } - - /* - * Parenthesize the argument unless it's a simple Var or a - * FieldSelect. (In particular, if it's another ArrayRef, we - * *must* parenthesize to avoid confusion.) - */ - need_parens = !IsA(aref->refexpr, Var) && - !IsA(aref->refexpr, FieldSelect); - if (need_parens) - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) aref->refexpr, context, showimplicit); - if (need_parens) - appendStringInfoChar(buf, ')'); - - /* - * If there's a refassgnexpr, we want to print the node in the - * format "array[subscripts] := refassgnexpr". This is not - * legal SQL, so decompilation of INSERT or UPDATE statements - * should always use processIndirection as part of the - * statement-level syntax. We should only see this when - * EXPLAIN tries to print the targetlist of a plan resulting - * from such a statement. - */ - if (aref->refassgnexpr) - { - Node *refassgnexpr; - - /* - * Use processIndirection to print this node's subscripts - * as well as any additional field selections or - * subscripting in immediate descendants. It returns the - * RHS expr that is actually being "assigned". - */ - refassgnexpr = processIndirection(node, context); - appendStringInfoString(buf, " := "); - get_rule_expr(refassgnexpr, context, showimplicit); - } - else - { - /* Just an ordinary array fetch, so print subscripts */ - printSubscripts(aref, context); - } - } - break; - - case T_FuncExpr: - get_func_expr((FuncExpr *) node, context, showimplicit); - break; - - case T_NamedArgExpr: - { - NamedArgExpr *na = (NamedArgExpr *) node; - - appendStringInfo(buf, "%s => ", quote_identifier(na->name)); - get_rule_expr((Node *) na->arg, context, showimplicit); - } - break; - - case T_OpExpr: - get_oper_expr((OpExpr *) node, context); - break; - - case T_DistinctExpr: - { - DistinctExpr *expr = (DistinctExpr *) node; - List *args = expr->args; - Node *arg1 = (Node *) linitial(args); - Node *arg2 = (Node *) lsecond(args); - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg1, context, true, node); - appendStringInfoString(buf, " IS DISTINCT FROM "); - get_rule_expr_paren(arg2, context, true, node); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_NullIfExpr: - { - NullIfExpr *nullifexpr = (NullIfExpr *) node; - - appendStringInfoString(buf, "NULLIF("); - get_rule_expr((Node *) nullifexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - - case T_ScalarArrayOpExpr: - { - ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; - List *args = expr->args; - Node *arg1 = (Node *) linitial(args); - Node *arg2 = (Node *) lsecond(args); - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg1, context, true, node); - appendStringInfo(buf, " %s %s (", - generate_operator_name(expr->opno, - exprType(arg1), - get_base_element_type(exprType(arg2))), - expr->useOr ? "ANY" : "ALL"); - get_rule_expr_paren(arg2, context, true, node); - - /* - * There's inherent ambiguity in "x op ANY/ALL (y)" when y is - * a bare sub-SELECT. Since we're here, the sub-SELECT must - * be meant as a scalar sub-SELECT yielding an array value to - * be used in ScalarArrayOpExpr; but the grammar will - * preferentially interpret such a construct as an ANY/ALL - * SubLink. To prevent misparsing the output that way, insert - * a dummy coercion (which will be stripped by parse analysis, - * so no inefficiency is added in dump and reload). This is - * indeed most likely what the user wrote to get the construct - * accepted in the first place. - */ - if (IsA(arg2, SubLink) && - ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK) - appendStringInfo(buf, "::%s", - format_type_with_typemod(exprType(arg2), - exprTypmod(arg2))); - appendStringInfoChar(buf, ')'); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_BoolExpr: - { - BoolExpr *expr = (BoolExpr *) node; - Node *first_arg = linitial(expr->args); - ListCell *arg = lnext(list_head(expr->args)); - - switch (expr->boolop) - { - case AND_EXPR: - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(first_arg, context, - false, node); - while (arg) - { - appendStringInfoString(buf, " AND "); - get_rule_expr_paren((Node *) lfirst(arg), context, - false, node); - arg = lnext(arg); - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - break; - - case OR_EXPR: - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(first_arg, context, - false, node); - while (arg) - { - appendStringInfoString(buf, " OR "); - get_rule_expr_paren((Node *) lfirst(arg), context, - false, node); - arg = lnext(arg); - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - break; - - case NOT_EXPR: - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - appendStringInfoString(buf, "NOT "); - get_rule_expr_paren(first_arg, context, - false, node); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - break; - - default: - elog(ERROR, "unrecognized boolop: %d", - (int) expr->boolop); - } - } - break; - - case T_SubLink: - get_sublink_expr((SubLink *) node, context); - break; - - case T_SubPlan: - { - SubPlan *subplan = (SubPlan *) node; - - /* - * We cannot see an already-planned subplan in rule deparsing, - * only while EXPLAINing a query plan. We don't try to - * reconstruct the original SQL, just reference the subplan - * that appears elsewhere in EXPLAIN's result. - */ - if (subplan->useHashTable) - appendStringInfo(buf, "(hashed %s)", subplan->plan_name); - else - appendStringInfo(buf, "(%s)", subplan->plan_name); - } - break; - - case T_AlternativeSubPlan: - { - AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; - ListCell *lc; - - /* As above, this can only happen during EXPLAIN */ - appendStringInfoString(buf, "(alternatives: "); - foreach(lc, asplan->subplans) - { - SubPlan *splan = lfirst_node(SubPlan, lc); - - if (splan->useHashTable) - appendStringInfo(buf, "hashed %s", splan->plan_name); - else - appendStringInfoString(buf, splan->plan_name); - if (lnext(lc)) - appendStringInfoString(buf, " or "); - } - appendStringInfoChar(buf, ')'); - } - break; - - case T_FieldSelect: - { - FieldSelect *fselect = (FieldSelect *) node; - Node *arg = (Node *) fselect->arg; - int fno = fselect->fieldnum; - const char *fieldname; - bool need_parens; - - /* - * Parenthesize the argument unless it's an ArrayRef or - * another FieldSelect. Note in particular that it would be - * WRONG to not parenthesize a Var argument; simplicity is not - * the issue here, having the right number of names is. - */ - need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect); - if (need_parens) - appendStringInfoChar(buf, '('); - get_rule_expr(arg, context, true); - if (need_parens) - appendStringInfoChar(buf, ')'); - - /* - * Get and print the field name. - */ - fieldname = get_name_for_var_field((Var *) arg, fno, - 0, context); - appendStringInfo(buf, ".%s", quote_identifier(fieldname)); - } - break; - - case T_FieldStore: - { - FieldStore *fstore = (FieldStore *) node; - bool need_parens; - - /* - * There is no good way to represent a FieldStore as real SQL, - * so decompilation of INSERT or UPDATE statements should - * always use processIndirection as part of the - * statement-level syntax. We should only get here when - * EXPLAIN tries to print the targetlist of a plan resulting - * from such a statement. The plan case is even harder than - * ordinary rules would be, because the planner tries to - * collapse multiple assignments to the same field or subfield - * into one FieldStore; so we can see a list of target fields - * not just one, and the arguments could be FieldStores - * themselves. We don't bother to try to print the target - * field names; we just print the source arguments, with a - * ROW() around them if there's more than one. This isn't - * terribly complete, but it's probably good enough for - * EXPLAIN's purposes; especially since anything more would be - * either hopelessly confusing or an even poorer - * representation of what the plan is actually doing. - */ - need_parens = (list_length(fstore->newvals) != 1); - if (need_parens) - appendStringInfoString(buf, "ROW("); - get_rule_expr((Node *) fstore->newvals, context, showimplicit); - if (need_parens) - appendStringInfoChar(buf, ')'); - } - break; - - case T_RelabelType: - { - RelabelType *relabel = (RelabelType *) node; - Node *arg = (Node *) relabel->arg; - - if (relabel->relabelformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - relabel->resulttype, - relabel->resulttypmod, - node); - } - } - break; - - case T_CoerceViaIO: - { - CoerceViaIO *iocoerce = (CoerceViaIO *) node; - Node *arg = (Node *) iocoerce->arg; - - if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - iocoerce->resulttype, - -1, - node); - } - } - break; - - case T_ArrayCoerceExpr: - { - ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; - Node *arg = (Node *) acoerce->arg; - - if (acoerce->coerceformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - acoerce->resulttype, - acoerce->resulttypmod, - node); - } - } - break; - - case T_ConvertRowtypeExpr: - { - ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; - Node *arg = (Node *) convert->arg; - - if (convert->convertformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - convert->resulttype, -1, - node); - } - } - break; - - case T_CollateExpr: - { - CollateExpr *collate = (CollateExpr *) node; - Node *arg = (Node *) collate->arg; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, node); - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(collate->collOid)); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_CaseExpr: - { - CaseExpr *caseexpr = (CaseExpr *) node; - ListCell *temp; - - appendContextKeyword(context, "CASE", - 0, PRETTYINDENT_VAR, 0); - if (caseexpr->arg) - { - appendStringInfoChar(buf, ' '); - get_rule_expr((Node *) caseexpr->arg, context, true); - } - foreach(temp, caseexpr->args) - { - CaseWhen *when = (CaseWhen *) lfirst(temp); - Node *w = (Node *) when->expr; - - if (caseexpr->arg) - { - /* - * The parser should have produced WHEN clauses of the - * form "CaseTestExpr = RHS", possibly with an - * implicit coercion inserted above the CaseTestExpr. - * For accurate decompilation of rules it's essential - * that we show just the RHS. However in an - * expression that's been through the optimizer, the - * WHEN clause could be almost anything (since the - * equality operator could have been expanded into an - * inline function). If we don't recognize the form - * of the WHEN clause, just punt and display it as-is. - */ - if (IsA(w, OpExpr)) - { - List *args = ((OpExpr *) w)->args; - - if (list_length(args) == 2 && - IsA(strip_implicit_coercions(linitial(args)), - CaseTestExpr)) - w = (Node *) lsecond(args); - } - } - - if (!PRETTY_INDENT(context)) - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "WHEN ", - 0, 0, 0); - get_rule_expr(w, context, false); - appendStringInfoString(buf, " THEN "); - get_rule_expr((Node *) when->result, context, true); - } - if (!PRETTY_INDENT(context)) - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "ELSE ", - 0, 0, 0); - get_rule_expr((Node *) caseexpr->defresult, context, true); - if (!PRETTY_INDENT(context)) - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "END", - -PRETTYINDENT_VAR, 0, 0); - } - break; - - case T_CaseTestExpr: - { - /* - * Normally we should never get here, since for expressions - * that can contain this node type we attempt to avoid - * recursing to it. But in an optimized expression we might - * be unable to avoid that (see comments for CaseExpr). If we - * do see one, print it as CASE_TEST_EXPR. - */ - appendStringInfoString(buf, "CASE_TEST_EXPR"); - } - break; - - case T_ArrayExpr: - { - ArrayExpr *arrayexpr = (ArrayExpr *) node; - - appendStringInfoString(buf, "ARRAY["); - get_rule_expr((Node *) arrayexpr->elements, context, true); - appendStringInfoChar(buf, ']'); - - /* - * If the array isn't empty, we assume its elements are - * coerced to the desired type. If it's empty, though, we - * need an explicit coercion to the array type. - */ - if (arrayexpr->elements == NIL) - appendStringInfo(buf, "::%s", - format_type_with_typemod(arrayexpr->array_typeid, -1)); - } - break; - - case T_RowExpr: - { - RowExpr *rowexpr = (RowExpr *) node; - TupleDesc tupdesc = NULL; - ListCell *arg; - int i; - char *sep; - - /* - * If it's a named type and not RECORD, we may have to skip - * dropped columns and/or claim there are NULLs for added - * columns. - */ - if (rowexpr->row_typeid != RECORDOID) - { - tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); - Assert(list_length(rowexpr->args) <= tupdesc->natts); - } - - /* - * SQL99 allows "ROW" to be omitted when there is more than - * one column, but for simplicity we always print it. - */ - appendStringInfoString(buf, "ROW("); - sep = ""; - i = 0; - foreach(arg, rowexpr->args) - { - Node *e = (Node *) lfirst(arg); - - if (tupdesc == NULL || - !tupdesc->attrs[i]->attisdropped) - { - appendStringInfoString(buf, sep); - /* Whole-row Vars need special treatment here */ - get_rule_expr_toplevel(e, context, true); - sep = ", "; - } - i++; - } - if (tupdesc != NULL) - { - while (i < tupdesc->natts) - { - if (!tupdesc->attrs[i]->attisdropped) - { - appendStringInfoString(buf, sep); - appendStringInfoString(buf, "NULL"); - sep = ", "; - } - i++; - } - - ReleaseTupleDesc(tupdesc); - } - appendStringInfoChar(buf, ')'); - if (rowexpr->row_format == COERCE_EXPLICIT_CAST) - appendStringInfo(buf, "::%s", - format_type_with_typemod(rowexpr->row_typeid, -1)); - } - break; - - case T_RowCompareExpr: - { - RowCompareExpr *rcexpr = (RowCompareExpr *) node; - ListCell *arg; - char *sep; - - /* - * SQL99 allows "ROW" to be omitted when there is more than - * one column, but for simplicity we always print it. - */ - appendStringInfoString(buf, "(ROW("); - sep = ""; - foreach(arg, rcexpr->largs) - { - Node *e = (Node *) lfirst(arg); - - appendStringInfoString(buf, sep); - get_rule_expr(e, context, true); - sep = ", "; - } - - /* - * We assume that the name of the first-column operator will - * do for all the rest too. This is definitely open to - * failure, eg if some but not all operators were renamed - * since the construct was parsed, but there seems no way to - * be perfect. - */ - appendStringInfo(buf, ") %s ROW(", - generate_operator_name(linitial_oid(rcexpr->opnos), - exprType(linitial(rcexpr->largs)), - exprType(linitial(rcexpr->rargs)))); - sep = ""; - foreach(arg, rcexpr->rargs) - { - Node *e = (Node *) lfirst(arg); - - appendStringInfoString(buf, sep); - get_rule_expr(e, context, true); - sep = ", "; - } - appendStringInfoString(buf, "))"); - } - break; - - case T_CoalesceExpr: - { - CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; - - appendStringInfoString(buf, "COALESCE("); - get_rule_expr((Node *) coalesceexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - - case T_MinMaxExpr: - { - MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; - - switch (minmaxexpr->op) - { - case IS_GREATEST: - appendStringInfoString(buf, "GREATEST("); - break; - case IS_LEAST: - appendStringInfoString(buf, "LEAST("); - break; - } - get_rule_expr((Node *) minmaxexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - - case T_SQLValueFunction: - { - SQLValueFunction *svf = (SQLValueFunction *) node; - - /* - * Note: this code knows that typmod for time, timestamp, and - * timestamptz just prints as integer. - */ - switch (svf->op) - { - case SVFOP_CURRENT_DATE: - appendStringInfoString(buf, "CURRENT_DATE"); - break; - case SVFOP_CURRENT_TIME: - appendStringInfoString(buf, "CURRENT_TIME"); - break; - case SVFOP_CURRENT_TIME_N: - appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod); - break; - case SVFOP_CURRENT_TIMESTAMP: - appendStringInfoString(buf, "CURRENT_TIMESTAMP"); - break; - case SVFOP_CURRENT_TIMESTAMP_N: - appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)", - svf->typmod); - break; - case SVFOP_LOCALTIME: - appendStringInfoString(buf, "LOCALTIME"); - break; - case SVFOP_LOCALTIME_N: - appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod); - break; - case SVFOP_LOCALTIMESTAMP: - appendStringInfoString(buf, "LOCALTIMESTAMP"); - break; - case SVFOP_LOCALTIMESTAMP_N: - appendStringInfo(buf, "LOCALTIMESTAMP(%d)", - svf->typmod); - break; - case SVFOP_CURRENT_ROLE: - appendStringInfoString(buf, "CURRENT_ROLE"); - break; - case SVFOP_CURRENT_USER: - appendStringInfoString(buf, "CURRENT_USER"); - break; - case SVFOP_USER: - appendStringInfoString(buf, "USER"); - break; - case SVFOP_SESSION_USER: - appendStringInfoString(buf, "SESSION_USER"); - break; - case SVFOP_CURRENT_CATALOG: - appendStringInfoString(buf, "CURRENT_CATALOG"); - break; - case SVFOP_CURRENT_SCHEMA: - appendStringInfoString(buf, "CURRENT_SCHEMA"); - break; - } - } - break; - - case T_XmlExpr: - { - XmlExpr *xexpr = (XmlExpr *) node; - bool needcomma = false; - ListCell *arg; - ListCell *narg; - Const *con; - - switch (xexpr->op) - { - case IS_XMLCONCAT: - appendStringInfoString(buf, "XMLCONCAT("); - break; - case IS_XMLELEMENT: - appendStringInfoString(buf, "XMLELEMENT("); - break; - case IS_XMLFOREST: - appendStringInfoString(buf, "XMLFOREST("); - break; - case IS_XMLPARSE: - appendStringInfoString(buf, "XMLPARSE("); - break; - case IS_XMLPI: - appendStringInfoString(buf, "XMLPI("); - break; - case IS_XMLROOT: - appendStringInfoString(buf, "XMLROOT("); - break; - case IS_XMLSERIALIZE: - appendStringInfoString(buf, "XMLSERIALIZE("); - break; - case IS_DOCUMENT: - break; - } - if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE) - { - if (xexpr->xmloption == XMLOPTION_DOCUMENT) - appendStringInfoString(buf, "DOCUMENT "); - else - appendStringInfoString(buf, "CONTENT "); - } - if (xexpr->name) - { - appendStringInfo(buf, "NAME %s", - quote_identifier(map_xml_name_to_sql_identifier(xexpr->name))); - needcomma = true; - } - if (xexpr->named_args) - { - if (xexpr->op != IS_XMLFOREST) - { - if (needcomma) - appendStringInfoString(buf, ", "); - appendStringInfoString(buf, "XMLATTRIBUTES("); - needcomma = false; - } - forboth(arg, xexpr->named_args, narg, xexpr->arg_names) - { - Node *e = (Node *) lfirst(arg); - char *argname = strVal(lfirst(narg)); - - if (needcomma) - appendStringInfoString(buf, ", "); - get_rule_expr((Node *) e, context, true); - appendStringInfo(buf, " AS %s", - quote_identifier(map_xml_name_to_sql_identifier(argname))); - needcomma = true; - } - if (xexpr->op != IS_XMLFOREST) - appendStringInfoChar(buf, ')'); - } - if (xexpr->args) - { - if (needcomma) - appendStringInfoString(buf, ", "); - switch (xexpr->op) - { - case IS_XMLCONCAT: - case IS_XMLELEMENT: - case IS_XMLFOREST: - case IS_XMLPI: - case IS_XMLSERIALIZE: - /* no extra decoration needed */ - get_rule_expr((Node *) xexpr->args, context, true); - break; - case IS_XMLPARSE: - Assert(list_length(xexpr->args) == 2); - - get_rule_expr((Node *) linitial(xexpr->args), - context, true); - - con = lsecond_node(Const, xexpr->args); - Assert(!con->constisnull); - if (DatumGetBool(con->constvalue)) - appendStringInfoString(buf, - " PRESERVE WHITESPACE"); - else - appendStringInfoString(buf, - " STRIP WHITESPACE"); - break; - case IS_XMLROOT: - Assert(list_length(xexpr->args) == 3); - - get_rule_expr((Node *) linitial(xexpr->args), - context, true); - - appendStringInfoString(buf, ", VERSION "); - con = (Const *) lsecond(xexpr->args); - if (IsA(con, Const) && - con->constisnull) - appendStringInfoString(buf, "NO VALUE"); - else - get_rule_expr((Node *) con, context, false); - - con = lthird_node(Const, xexpr->args); - if (con->constisnull) - /* suppress STANDALONE NO VALUE */ ; - else - { - switch (DatumGetInt32(con->constvalue)) - { - case XML_STANDALONE_YES: - appendStringInfoString(buf, - ", STANDALONE YES"); - break; - case XML_STANDALONE_NO: - appendStringInfoString(buf, - ", STANDALONE NO"); - break; - case XML_STANDALONE_NO_VALUE: - appendStringInfoString(buf, - ", STANDALONE NO VALUE"); - break; - default: - break; - } - } - break; - case IS_DOCUMENT: - get_rule_expr_paren((Node *) xexpr->args, context, false, node); - break; - } - - } - if (xexpr->op == IS_XMLSERIALIZE) - appendStringInfo(buf, " AS %s", - format_type_with_typemod(xexpr->type, - xexpr->typmod)); - if (xexpr->op == IS_DOCUMENT) - appendStringInfoString(buf, " IS DOCUMENT"); - else - appendStringInfoChar(buf, ')'); - } - break; - - case T_NullTest: - { - NullTest *ntest = (NullTest *) node; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren((Node *) ntest->arg, context, true, node); - - /* - * For scalar inputs, we prefer to print as IS [NOT] NULL, - * which is shorter and traditional. If it's a rowtype input - * but we're applying a scalar test, must print IS [NOT] - * DISTINCT FROM NULL to be semantically correct. - */ - if (ntest->argisrow || - !type_is_rowtype(exprType((Node *) ntest->arg))) - { - switch (ntest->nulltesttype) - { - case IS_NULL: - appendStringInfoString(buf, " IS NULL"); - break; - case IS_NOT_NULL: - appendStringInfoString(buf, " IS NOT NULL"); - break; - default: - elog(ERROR, "unrecognized nulltesttype: %d", - (int) ntest->nulltesttype); - } - } - else - { - switch (ntest->nulltesttype) - { - case IS_NULL: - appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL"); - break; - case IS_NOT_NULL: - appendStringInfoString(buf, " IS DISTINCT FROM NULL"); - break; - default: - elog(ERROR, "unrecognized nulltesttype: %d", - (int) ntest->nulltesttype); - } - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_BooleanTest: - { - BooleanTest *btest = (BooleanTest *) node; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren((Node *) btest->arg, context, false, node); - switch (btest->booltesttype) - { - case IS_TRUE: - appendStringInfoString(buf, " IS TRUE"); - break; - case IS_NOT_TRUE: - appendStringInfoString(buf, " IS NOT TRUE"); - break; - case IS_FALSE: - appendStringInfoString(buf, " IS FALSE"); - break; - case IS_NOT_FALSE: - appendStringInfoString(buf, " IS NOT FALSE"); - break; - case IS_UNKNOWN: - appendStringInfoString(buf, " IS UNKNOWN"); - break; - case IS_NOT_UNKNOWN: - appendStringInfoString(buf, " IS NOT UNKNOWN"); - break; - default: - elog(ERROR, "unrecognized booltesttype: %d", - (int) btest->booltesttype); - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_CoerceToDomain: - { - CoerceToDomain *ctest = (CoerceToDomain *) node; - Node *arg = (Node *) ctest->arg; - - if (ctest->coercionformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr(arg, context, false); - } - else - { - get_coercion_expr(arg, context, - ctest->resulttype, - ctest->resulttypmod, - node); - } - } - break; - - case T_CoerceToDomainValue: - appendStringInfoString(buf, "VALUE"); - break; - - case T_SetToDefault: - appendStringInfoString(buf, "DEFAULT"); - break; - - case T_CurrentOfExpr: - { - CurrentOfExpr *cexpr = (CurrentOfExpr *) node; - - if (cexpr->cursor_name) - appendStringInfo(buf, "CURRENT OF %s", - quote_identifier(cexpr->cursor_name)); - else - appendStringInfo(buf, "CURRENT OF $%d", - cexpr->cursor_param); - } - break; - - case T_NextValueExpr: - { - NextValueExpr *nvexpr = (NextValueExpr *) node; - - /* - * This isn't exactly nextval(), but that seems close enough - * for EXPLAIN's purposes. - */ - appendStringInfoString(buf, "nextval("); - simple_quote_literal(buf, - generate_relation_name(nvexpr->seqid, - NIL)); - appendStringInfoChar(buf, ')'); - } - break; - - case T_InferenceElem: - { - InferenceElem *iexpr = (InferenceElem *) node; - bool save_varprefix; - bool need_parens; - - /* - * InferenceElem can only refer to target relation, so a - * prefix is not useful, and indeed would cause parse errors. - */ - save_varprefix = context->varprefix; - context->varprefix = false; - - /* - * Parenthesize the element unless it's a simple Var or a bare - * function call. Follows pg_get_indexdef_worker(). - */ - need_parens = !IsA(iexpr->expr, Var); - if (IsA(iexpr->expr, FuncExpr) && - ((FuncExpr *) iexpr->expr)->funcformat == - COERCE_EXPLICIT_CALL) - need_parens = false; - - if (need_parens) - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) iexpr->expr, - context, false); - if (need_parens) - appendStringInfoChar(buf, ')'); - - context->varprefix = save_varprefix; - - if (iexpr->infercollid) - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(iexpr->infercollid)); - - /* Add the operator class name, if not default */ - if (iexpr->inferopclass) - { - Oid inferopclass = iexpr->inferopclass; - Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass); - - get_opclass_name(inferopclass, inferopcinputtype, buf); - } - } - break; - - case T_PartitionBoundSpec: - { - PartitionBoundSpec *spec = (PartitionBoundSpec *) node; - ListCell *cell; - char *sep; - - switch (spec->strategy) - { - case PARTITION_STRATEGY_LIST: - Assert(spec->listdatums != NIL); - - appendStringInfoString(buf, "FOR VALUES IN ("); - sep = ""; - foreach(cell, spec->listdatums) - { - Const *val = castNode(Const, lfirst(cell)); - - appendStringInfoString(buf, sep); - get_const_expr(val, context, -1); - sep = ", "; - } - - appendStringInfoString(buf, ")"); - break; - - case PARTITION_STRATEGY_RANGE: - Assert(spec->lowerdatums != NIL && - spec->upperdatums != NIL && - list_length(spec->lowerdatums) == - list_length(spec->upperdatums)); - - appendStringInfo(buf, "FOR VALUES FROM %s TO %s", - get_range_partbound_string(spec->lowerdatums), - get_range_partbound_string(spec->upperdatums)); - break; - - default: - elog(ERROR, "unrecognized partition strategy: %d", - (int) spec->strategy); - break; - } - } - break; - - case T_List: - { - char *sep; - ListCell *l; - - sep = ""; - foreach(l, (List *) node) - { - appendStringInfoString(buf, sep); - get_rule_expr((Node *) lfirst(l), context, showimplicit); - sep = ", "; - } - } - break; - - case T_TableFunc: - get_tablefunc((TableFunc *) node, context, showimplicit); - break; - - default: - elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); - break; - } -} - -/* - * get_rule_expr_toplevel - Parse back a toplevel expression - * - * Same as get_rule_expr(), except that if the expr is just a Var, we pass - * istoplevel = true not false to get_variable(). This causes whole-row Vars - * to get printed with decoration that will prevent expansion of "*". - * We need to use this in contexts such as ROW() and VALUES(), where the - * parser would expand "foo.*" appearing at top level. (In principle we'd - * use this in get_target_list() too, but that has additional worries about - * whether to print AS, so it needs to invoke get_variable() directly anyway.) - */ -static void -get_rule_expr_toplevel(Node *node, deparse_context *context, - bool showimplicit) -{ - if (node && IsA(node, Var)) - (void) get_variable((Var *) node, 0, true, context); - else - get_rule_expr(node, context, showimplicit); -} - -/* - * get_rule_expr_funccall - Parse back a function-call expression - * - * Same as get_rule_expr(), except that we guarantee that the output will - * look like a function call, or like one of the things the grammar treats as - * equivalent to a function call (see the func_expr_windowless production). - * This is needed in places where the grammar uses func_expr_windowless and - * you can't substitute a parenthesized a_expr. If what we have isn't going - * to look like a function call, wrap it in a dummy CAST() expression, which - * will satisfy the grammar --- and, indeed, is likely what the user wrote to - * produce such a thing. - */ -static void -get_rule_expr_funccall(Node *node, deparse_context *context, - bool showimplicit) -{ - if (looks_like_function(node)) - get_rule_expr(node, context, showimplicit); - else - { - StringInfo buf = context->buf; - - appendStringInfoString(buf, "CAST("); - /* no point in showing any top-level implicit cast */ - get_rule_expr(node, context, false); - appendStringInfo(buf, " AS %s)", - format_type_with_typemod(exprType(node), - exprTypmod(node))); - } -} - -/* - * Helper function to identify node types that satisfy func_expr_windowless. - * If in doubt, "false" is always a safe answer. - */ -static bool -looks_like_function(Node *node) -{ - if (node == NULL) - return false; /* probably shouldn't happen */ - switch (nodeTag(node)) - { - case T_FuncExpr: - /* OK, unless it's going to deparse as a cast */ - return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL); - case T_NullIfExpr: - case T_CoalesceExpr: - case T_MinMaxExpr: - case T_SQLValueFunction: - case T_XmlExpr: - /* these are all accepted by func_expr_common_subexpr */ - return true; - default: - break; - } - return false; -} - - -/* - * get_oper_expr - Parse back an OpExpr node - */ -static void -get_oper_expr(OpExpr *expr, deparse_context *context) -{ - StringInfo buf = context->buf; - Oid opno = expr->opno; - List *args = expr->args; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - if (list_length(args) == 2) - { - /* binary operator */ - Node *arg1 = (Node *) linitial(args); - Node *arg2 = (Node *) lsecond(args); - - get_rule_expr_paren(arg1, context, true, (Node *) expr); - appendStringInfo(buf, " %s ", - generate_operator_name(opno, - exprType(arg1), - exprType(arg2))); - get_rule_expr_paren(arg2, context, true, (Node *) expr); - } - else - { - /* unary operator --- but which side? */ - Node *arg = (Node *) linitial(args); - HeapTuple tp; - Form_pg_operator optup; - - tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); - if (!HeapTupleIsValid(tp)) - elog(ERROR, "cache lookup failed for operator %u", opno); - optup = (Form_pg_operator) GETSTRUCT(tp); - switch (optup->oprkind) - { - case 'l': - appendStringInfo(buf, "%s ", - generate_operator_name(opno, - InvalidOid, - exprType(arg))); - get_rule_expr_paren(arg, context, true, (Node *) expr); - break; - case 'r': - get_rule_expr_paren(arg, context, true, (Node *) expr); - appendStringInfo(buf, " %s", - generate_operator_name(opno, - exprType(arg), - InvalidOid)); - break; - default: - elog(ERROR, "bogus oprkind: %d", optup->oprkind); - } - ReleaseSysCache(tp); - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); -} - -/* - * get_func_expr - Parse back a FuncExpr node - */ -static void -get_func_expr(FuncExpr *expr, deparse_context *context, - bool showimplicit) -{ - StringInfo buf = context->buf; - Oid funcoid = expr->funcid; - Oid argtypes[FUNC_MAX_ARGS]; - int nargs; - List *argnames; - bool use_variadic; - ListCell *l; - - /* - * If the function call came from an implicit coercion, then just show the - * first argument --- unless caller wants to see implicit coercions. - */ - if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit) - { - get_rule_expr_paren((Node *) linitial(expr->args), context, - false, (Node *) expr); - return; - } - - /* - * If the function call came from a cast, then show the first argument - * plus an explicit cast operation. - */ - if (expr->funcformat == COERCE_EXPLICIT_CAST || - expr->funcformat == COERCE_IMPLICIT_CAST) - { - Node *arg = linitial(expr->args); - Oid rettype = expr->funcresulttype; - int32 coercedTypmod; - - /* Get the typmod if this is a length-coercion function */ - (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod); - - get_coercion_expr(arg, context, - rettype, coercedTypmod, - (Node *) expr); - - return; - } - - /* - * Normal function: display as proname(args). First we need to extract - * the argument datatypes. - */ - if (list_length(expr->args) > FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("too many arguments"))); - nargs = 0; - argnames = NIL; - foreach(l, expr->args) - { - Node *arg = (Node *) lfirst(l); - - if (IsA(arg, NamedArgExpr)) - argnames = lappend(argnames, ((NamedArgExpr *) arg)->name); - argtypes[nargs] = exprType(arg); - nargs++; - } - - appendStringInfo(buf, "%s(", - generate_function_name(funcoid, nargs, - argnames, argtypes, - expr->funcvariadic, - &use_variadic, - context->special_exprkind)); - nargs = 0; - foreach(l, expr->args) - { - if (nargs++ > 0) - appendStringInfoString(buf, ", "); - if (use_variadic && lnext(l) == NULL) - appendStringInfoString(buf, "VARIADIC "); - get_rule_expr((Node *) lfirst(l), context, true); - } - appendStringInfoChar(buf, ')'); -} - -/* - * get_agg_expr - Parse back an Aggref node - */ -static void -get_agg_expr(Aggref *aggref, deparse_context *context, - Aggref *original_aggref) -{ - StringInfo buf = context->buf; - Oid argtypes[FUNC_MAX_ARGS]; - int nargs; - bool use_variadic; - - /* - * For a combining aggregate, we look up and deparse the corresponding - * partial aggregate instead. This is necessary because our input - * argument list has been replaced; the new argument list always has just - * one element, which will point to a partial Aggref that supplies us with - * transition states to combine. - */ - if (DO_AGGSPLIT_COMBINE(aggref->aggsplit)) - { - TargetEntry *tle = linitial_node(TargetEntry, aggref->args); - - Assert(list_length(aggref->args) == 1); - resolve_special_varno((Node *) tle->expr, context, original_aggref, - get_agg_combine_expr); - return; - } - - /* - * Mark as PARTIAL, if appropriate. We look to the original aggref so as - * to avoid printing this when recursing from the code just above. - */ - if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit)) - appendStringInfoString(buf, "PARTIAL "); - - /* Extract the argument types as seen by the parser */ - nargs = get_aggregate_argtypes(aggref, argtypes); - - /* Print the aggregate name, schema-qualified if needed */ - appendStringInfo(buf, "%s(%s", - generate_function_name(aggref->aggfnoid, nargs, - NIL, argtypes, - aggref->aggvariadic, - &use_variadic, - context->special_exprkind), - (aggref->aggdistinct != NIL) ? "DISTINCT " : ""); - - if (AGGKIND_IS_ORDERED_SET(aggref->aggkind)) - { - /* - * Ordered-set aggregates do not use "*" syntax. Also, we needn't - * worry about inserting VARIADIC. So we can just dump the direct - * args as-is. - */ - Assert(!aggref->aggvariadic); - get_rule_expr((Node *) aggref->aggdirectargs, context, true); - Assert(aggref->aggorder != NIL); - appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY "); - get_rule_orderby(aggref->aggorder, aggref->args, false, context); - } - else - { - /* aggstar can be set only in zero-argument aggregates */ - if (aggref->aggstar) - appendStringInfoChar(buf, '*'); - else - { - ListCell *l; - int i; - - i = 0; - foreach(l, aggref->args) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - Node *arg = (Node *) tle->expr; - - Assert(!IsA(arg, NamedArgExpr)); - if (tle->resjunk) - continue; - if (i++ > 0) - appendStringInfoString(buf, ", "); - if (use_variadic && i == nargs) - appendStringInfoString(buf, "VARIADIC "); - get_rule_expr(arg, context, true); - } - } - - if (aggref->aggorder != NIL) - { - appendStringInfoString(buf, " ORDER BY "); - get_rule_orderby(aggref->aggorder, aggref->args, false, context); - } - } - - if (aggref->aggfilter != NULL) - { - appendStringInfoString(buf, ") FILTER (WHERE "); - get_rule_expr((Node *) aggref->aggfilter, context, false); - } - - appendStringInfoChar(buf, ')'); -} - -/* - * This is a helper function for get_agg_expr(). It's used when we deparse - * a combining Aggref; resolve_special_varno locates the corresponding partial - * Aggref and then calls this. - */ -static void -get_agg_combine_expr(Node *node, deparse_context *context, void *private) -{ - Aggref *aggref; - Aggref *original_aggref = private; - - if (!IsA(node, Aggref)) - elog(ERROR, "combining Aggref does not point to an Aggref"); - - aggref = (Aggref *) node; - get_agg_expr(aggref, context, original_aggref); -} - -/* - * get_windowfunc_expr - Parse back a WindowFunc node - */ -static void -get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context) -{ - StringInfo buf = context->buf; - Oid argtypes[FUNC_MAX_ARGS]; - int nargs; - List *argnames; - ListCell *l; - - if (list_length(wfunc->args) > FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("too many arguments"))); - nargs = 0; - argnames = NIL; - foreach(l, wfunc->args) - { - Node *arg = (Node *) lfirst(l); - - if (IsA(arg, NamedArgExpr)) - argnames = lappend(argnames, ((NamedArgExpr *) arg)->name); - argtypes[nargs] = exprType(arg); - nargs++; - } - - appendStringInfo(buf, "%s(", - generate_function_name(wfunc->winfnoid, nargs, - argnames, argtypes, - false, NULL, - context->special_exprkind)); - /* winstar can be set only in zero-argument aggregates */ - if (wfunc->winstar) - appendStringInfoChar(buf, '*'); - else - get_rule_expr((Node *) wfunc->args, context, true); - - if (wfunc->aggfilter != NULL) - { - appendStringInfoString(buf, ") FILTER (WHERE "); - get_rule_expr((Node *) wfunc->aggfilter, context, false); - } - - appendStringInfoString(buf, ") OVER "); - - foreach(l, context->windowClause) - { - WindowClause *wc = (WindowClause *) lfirst(l); - - if (wc->winref == wfunc->winref) - { - if (wc->name) - appendStringInfoString(buf, quote_identifier(wc->name)); - else - get_rule_windowspec(wc, context->windowTList, context); - break; - } - } - if (l == NULL) - { - if (context->windowClause) - elog(ERROR, "could not find window clause for winref %u", - wfunc->winref); - - /* - * In EXPLAIN, we don't have window context information available, so - * we have to settle for this: - */ - appendStringInfoString(buf, "(?)"); - } -} - -/* ---------- - * get_coercion_expr - * - * Make a string representation of a value coerced to a specific type - * ---------- - */ -static void -get_coercion_expr(Node *arg, deparse_context *context, - Oid resulttype, int32 resulttypmod, - Node *parentNode) -{ - StringInfo buf = context->buf; - - /* - * Since parse_coerce.c doesn't immediately collapse application of - * length-coercion functions to constants, what we'll typically see in - * such cases is a Const with typmod -1 and a length-coercion function - * right above it. Avoid generating redundant output. However, beware of - * suppressing casts when the user actually wrote something like - * 'foo'::text::char(3). - * - * Note: it might seem that we are missing the possibility of needing to - * print a COLLATE clause for such a Const. However, a Const could only - * have nondefault collation in a post-constant-folding tree, in which the - * length coercion would have been folded too. See also the special - * handling of CollateExpr in coerce_to_target_type(): any collation - * marking will be above the coercion node, not below it. - */ - if (arg && IsA(arg, Const) && - ((Const *) arg)->consttype == resulttype && - ((Const *) arg)->consttypmod == -1) - { - /* Show the constant without normal ::typename decoration */ - get_const_expr((Const *) arg, context, -1); - } - else - { - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, false, parentNode); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - appendStringInfo(buf, "::%s", - format_type_with_typemod(resulttype, resulttypmod)); -} - -/* ---------- - * get_const_expr - * - * Make a string representation of a Const - * - * showtype can be -1 to never show "::typename" decoration, or +1 to always - * show it, or 0 to show it only if the constant wouldn't be assumed to be - * the right type by default. - * - * If the Const's collation isn't default for its type, show that too. - * We mustn't do this when showtype is -1 (since that means the caller will - * print "::typename", and we can't put a COLLATE clause in between). It's - * caller's responsibility that collation isn't missed in such cases. - * ---------- - */ -static void -get_const_expr(Const *constval, deparse_context *context, int showtype) -{ - StringInfo buf = context->buf; - Oid typoutput; - bool typIsVarlena; - char *extval; - bool needlabel = false; - - if (constval->constisnull) - { - /* - * Always label the type of a NULL constant to prevent misdecisions - * about type when reparsing. - */ - appendStringInfoString(buf, "NULL"); - if (showtype >= 0) - { - appendStringInfo(buf, "::%s", - format_type_with_typemod(constval->consttype, - constval->consttypmod)); - get_const_collation(constval, context); - } - return; - } - - getTypeOutputInfo(constval->consttype, - &typoutput, &typIsVarlena); - - extval = OidOutputFunctionCall(typoutput, constval->constvalue); - - switch (constval->consttype) - { - case INT4OID: - - /* - * INT4 can be printed without any decoration, unless it is - * negative; in that case print it as '-nnn'::integer to ensure - * that the output will re-parse as a constant, not as a constant - * plus operator. In most cases we could get away with printing - * (-nnn) instead, because of the way that gram.y handles negative - * literals; but that doesn't work for INT_MIN, and it doesn't - * seem that much prettier anyway. - */ - if (extval[0] != '-') - appendStringInfoString(buf, extval); - else - { - appendStringInfo(buf, "'%s'", extval); - needlabel = true; /* we must attach a cast */ - } - break; - - case NUMERICOID: - - /* - * NUMERIC can be printed without quotes if it looks like a float - * constant (not an integer, and not Infinity or NaN) and doesn't - * have a leading sign (for the same reason as for INT4). - */ - if (isdigit((unsigned char) extval[0]) && - strcspn(extval, "eE.") != strlen(extval)) - { - appendStringInfoString(buf, extval); - } - else - { - appendStringInfo(buf, "'%s'", extval); - needlabel = true; /* we must attach a cast */ - } - break; - - case BITOID: - case VARBITOID: - appendStringInfo(buf, "B'%s'", extval); - break; - - case BOOLOID: - if (strcmp(extval, "t") == 0) - appendStringInfoString(buf, "true"); - else - appendStringInfoString(buf, "false"); - break; - - default: - simple_quote_literal(buf, extval); - break; - } - - pfree(extval); - - if (showtype < 0) - return; - - /* - * For showtype == 0, append ::typename unless the constant will be - * implicitly typed as the right type when it is read in. - * - * XXX this code has to be kept in sync with the behavior of the parser, - * especially make_const. - */ - switch (constval->consttype) - { - case BOOLOID: - case UNKNOWNOID: - /* These types can be left unlabeled */ - needlabel = false; - break; - case INT4OID: - /* We determined above whether a label is needed */ - break; - case NUMERICOID: - - /* - * Float-looking constants will be typed as numeric, which we - * checked above; but if there's a nondefault typmod we need to - * show it. - */ - needlabel |= (constval->consttypmod >= 0); - break; - default: - needlabel = true; - break; - } - if (needlabel || showtype > 0) - appendStringInfo(buf, "::%s", - format_type_with_typemod(constval->consttype, - constval->consttypmod)); - - get_const_collation(constval, context); -} - -/* - * helper for get_const_expr: append COLLATE if needed - */ -static void -get_const_collation(Const *constval, deparse_context *context) -{ - StringInfo buf = context->buf; - - if (OidIsValid(constval->constcollid)) - { - Oid typcollation = get_typcollation(constval->consttype); - - if (constval->constcollid != typcollation) - { - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(constval->constcollid)); - } - } -} - -/* - * simple_quote_literal - Format a string as a SQL literal, append to buf - */ -static void -simple_quote_literal(StringInfo buf, const char *val) -{ - const char *valptr; - - /* - * We form the string literal according to the prevailing setting of - * standard_conforming_strings; we never use E''. User is responsible for - * making sure result is used correctly. - */ - appendStringInfoChar(buf, '\''); - for (valptr = val; *valptr; valptr++) - { - char ch = *valptr; - - if (SQL_STR_DOUBLE(ch, !standard_conforming_strings)) - appendStringInfoChar(buf, ch); - appendStringInfoChar(buf, ch); - } - appendStringInfoChar(buf, '\''); -} - - -/* ---------- - * get_sublink_expr - Parse back a sublink - * ---------- - */ -static void -get_sublink_expr(SubLink *sublink, deparse_context *context) -{ - StringInfo buf = context->buf; - Query *query = (Query *) (sublink->subselect); - char *opname = NULL; - bool need_paren; - - if (sublink->subLinkType == ARRAY_SUBLINK) - appendStringInfoString(buf, "ARRAY("); - else - appendStringInfoChar(buf, '('); - - /* - * Note that we print the name of only the first operator, when there are - * multiple combining operators. This is an approximation that could go - * wrong in various scenarios (operators in different schemas, renamed - * operators, etc) but there is not a whole lot we can do about it, since - * the syntax allows only one operator to be shown. - */ - if (sublink->testexpr) - { - if (IsA(sublink->testexpr, OpExpr)) - { - /* single combining operator */ - OpExpr *opexpr = (OpExpr *) sublink->testexpr; - - get_rule_expr(linitial(opexpr->args), context, true); - opname = generate_operator_name(opexpr->opno, - exprType(linitial(opexpr->args)), - exprType(lsecond(opexpr->args))); - } - else if (IsA(sublink->testexpr, BoolExpr)) - { - /* multiple combining operators, = or <> cases */ - char *sep; - ListCell *l; - - appendStringInfoChar(buf, '('); - sep = ""; - foreach(l, ((BoolExpr *) sublink->testexpr)->args) - { - OpExpr *opexpr = lfirst_node(OpExpr, l); - - appendStringInfoString(buf, sep); - get_rule_expr(linitial(opexpr->args), context, true); - if (!opname) - opname = generate_operator_name(opexpr->opno, - exprType(linitial(opexpr->args)), - exprType(lsecond(opexpr->args))); - sep = ", "; - } - appendStringInfoChar(buf, ')'); - } - else if (IsA(sublink->testexpr, RowCompareExpr)) - { - /* multiple combining operators, < <= > >= cases */ - RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr; - - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) rcexpr->largs, context, true); - opname = generate_operator_name(linitial_oid(rcexpr->opnos), - exprType(linitial(rcexpr->largs)), - exprType(linitial(rcexpr->rargs))); - appendStringInfoChar(buf, ')'); - } - else - elog(ERROR, "unrecognized testexpr type: %d", - (int) nodeTag(sublink->testexpr)); - } - - need_paren = true; - - switch (sublink->subLinkType) - { - case EXISTS_SUBLINK: - appendStringInfoString(buf, "EXISTS "); - break; - - case ANY_SUBLINK: - if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */ - appendStringInfoString(buf, " IN "); - else - appendStringInfo(buf, " %s ANY ", opname); - break; - - case ALL_SUBLINK: - appendStringInfo(buf, " %s ALL ", opname); - break; - - case ROWCOMPARE_SUBLINK: - appendStringInfo(buf, " %s ", opname); - break; - - case EXPR_SUBLINK: - case MULTIEXPR_SUBLINK: - case ARRAY_SUBLINK: - need_paren = false; - break; - - case CTE_SUBLINK: /* shouldn't occur in a SubLink */ - default: - elog(ERROR, "unrecognized sublink type: %d", - (int) sublink->subLinkType); - break; - } - - if (need_paren) - appendStringInfoChar(buf, '('); - - get_query_def(query, buf, context->namespaces, NULL, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - - if (need_paren) - appendStringInfoString(buf, "))"); - else - appendStringInfoChar(buf, ')'); -} - - -/* ---------- - * get_tablefunc - Parse back a table function - * ---------- - */ -static void -get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit) -{ - StringInfo buf = context->buf; - - /* XMLTABLE is the only existing implementation. */ - - appendStringInfoString(buf, "XMLTABLE("); - - if (tf->ns_uris != NIL) - { - ListCell *lc1, - *lc2; - bool first = true; - - appendStringInfoString(buf, "XMLNAMESPACES ("); - forboth(lc1, tf->ns_uris, lc2, tf->ns_names) - { - Node *expr = (Node *) lfirst(lc1); - char *name = strVal(lfirst(lc2)); - - if (!first) - appendStringInfoString(buf, ", "); - else - first = false; - - if (name != NULL) - { - get_rule_expr(expr, context, showimplicit); - appendStringInfo(buf, " AS %s", name); - } - else - { - appendStringInfoString(buf, "DEFAULT "); - get_rule_expr(expr, context, showimplicit); - } - } - appendStringInfoString(buf, "), "); - } - - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) tf->rowexpr, context, showimplicit); - appendStringInfoString(buf, ") PASSING ("); - get_rule_expr((Node *) tf->docexpr, context, showimplicit); - appendStringInfoChar(buf, ')'); - - if (tf->colexprs != NIL) - { - ListCell *l1; - ListCell *l2; - ListCell *l3; - ListCell *l4; - ListCell *l5; - int colnum = 0; - - l2 = list_head(tf->coltypes); - l3 = list_head(tf->coltypmods); - l4 = list_head(tf->colexprs); - l5 = list_head(tf->coldefexprs); - - appendStringInfoString(buf, " COLUMNS "); - foreach(l1, tf->colnames) - { - char *colname = strVal(lfirst(l1)); - Oid typid; - int32 typmod; - Node *colexpr; - Node *coldefexpr; - bool ordinality = tf->ordinalitycol == colnum; - bool notnull = bms_is_member(colnum, tf->notnulls); - - typid = lfirst_oid(l2); - l2 = lnext(l2); - typmod = lfirst_int(l3); - l3 = lnext(l3); - colexpr = (Node *) lfirst(l4); - l4 = lnext(l4); - coldefexpr = (Node *) lfirst(l5); - l5 = lnext(l5); - - if (colnum > 0) - appendStringInfoString(buf, ", "); - colnum++; - - appendStringInfo(buf, "%s %s", quote_identifier(colname), - ordinality ? "FOR ORDINALITY" : - format_type_with_typemod(typid, typmod)); - if (ordinality) - continue; - - if (coldefexpr != NULL) - { - appendStringInfoString(buf, " DEFAULT ("); - get_rule_expr((Node *) coldefexpr, context, showimplicit); - appendStringInfoChar(buf, ')'); - } - if (colexpr != NULL) - { - appendStringInfoString(buf, " PATH ("); - get_rule_expr((Node *) colexpr, context, showimplicit); - appendStringInfoChar(buf, ')'); - } - if (notnull) - appendStringInfoString(buf, " NOT NULL"); - } - } - - appendStringInfoChar(buf, ')'); -} - -/* ---------- - * get_from_clause - Parse back a FROM clause - * - * "prefix" is the keyword that denotes the start of the list of FROM - * elements. It is FROM when used to parse back SELECT and UPDATE, but - * is USING when parsing back DELETE. - * ---------- - */ -static void -get_from_clause(Query *query, const char *prefix, deparse_context *context) -{ - StringInfo buf = context->buf; - bool first = true; - ListCell *l; - - /* - * We use the query's jointree as a guide to what to print. However, we - * must ignore auto-added RTEs that are marked not inFromCl. (These can - * only appear at the top level of the jointree, so it's sufficient to - * check here.) This check also ensures we ignore the rule pseudo-RTEs - * for NEW and OLD. - */ - foreach(l, query->jointree->fromlist) - { - Node *jtnode = (Node *) lfirst(l); - - if (IsA(jtnode, RangeTblRef)) - { - int varno = ((RangeTblRef *) jtnode)->rtindex; - RangeTblEntry *rte = rt_fetch(varno, query->rtable); - - if (!rte->inFromCl) - continue; - } - - if (first) - { - appendContextKeyword(context, prefix, - -PRETTYINDENT_STD, PRETTYINDENT_STD, 2); - first = false; - - get_from_clause_item(jtnode, query, context); - } - else - { - StringInfoData itembuf; - - appendStringInfoString(buf, ", "); - - /* - * Put the new FROM item's text into itembuf so we can decide - * after we've got it whether or not it needs to go on a new line. - */ - initStringInfo(&itembuf); - context->buf = &itembuf; - - get_from_clause_item(jtnode, query, context); - - /* Restore context's output buffer */ - context->buf = buf; - - /* Consider line-wrapping if enabled */ - if (PRETTY_INDENT(context) && context->wrapColumn >= 0) - { - /* Does the new item start with a new line? */ - if (itembuf.len > 0 && itembuf.data[0] == '\n') - { - /* If so, we shouldn't add anything */ - /* instead, remove any trailing spaces currently in buf */ - removeStringInfoSpaces(buf); - } - else - { - char *trailing_nl; - - /* Locate the start of the current line in the buffer */ - trailing_nl = strrchr(buf->data, '\n'); - if (trailing_nl == NULL) - trailing_nl = buf->data; - else - trailing_nl++; - - /* - * Add a newline, plus some indentation, if the new item - * would cause an overflow. - */ - if (strlen(trailing_nl) + itembuf.len > context->wrapColumn) - appendContextKeyword(context, "", -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_VAR); - } - } - - /* Add the new item */ - appendStringInfoString(buf, itembuf.data); - - /* clean up */ - pfree(itembuf.data); - } - } -} - -static void -get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces); - - if (IsA(jtnode, RangeTblRef)) - { - int varno = ((RangeTblRef *) jtnode)->rtindex; - RangeTblEntry *rte = rt_fetch(varno, query->rtable); - char *refname = get_rtable_name(varno, context); - deparse_columns *colinfo = deparse_columns_fetch(varno, dpns); - RangeTblFunction *rtfunc1 = NULL; - bool printalias; - CitusRTEKind rteKind = GetRangeTblKind(rte); - - if (rte->lateral) - appendStringInfoString(buf, "LATERAL "); - - /* Print the FROM item proper */ - switch (rte->rtekind) - { - case RTE_RELATION: - /* Normal relation RTE */ - appendStringInfo(buf, "%s%s", - only_marker(rte), - generate_relation_or_shard_name(rte->relid, - context->distrelid, - context->shardid, - context->namespaces)); - break; - case RTE_SUBQUERY: - /* Subquery RTE */ - appendStringInfoChar(buf, '('); - get_query_def(rte->subquery, buf, context->namespaces, NULL, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - appendStringInfoChar(buf, ')'); - break; - case RTE_FUNCTION: - /* if it's a shard, do differently */ - if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - char *fragmentSchemaName = NULL; - char *fragmentTableName = NULL; - - ExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL); - - /* use schema and table name from the remote alias */ - appendStringInfoString(buf, - generate_fragment_name(fragmentSchemaName, - fragmentTableName)); - break; - } - - /* Function RTE */ - rtfunc1 = (RangeTblFunction *) linitial(rte->functions); - - /* - * Omit ROWS FROM() syntax for just one function, unless it - * has both a coldeflist and WITH ORDINALITY. If it has both, - * we must use ROWS FROM() syntax to avoid ambiguity about - * whether the coldeflist includes the ordinality column. - */ - if (list_length(rte->functions) == 1 && - (rtfunc1->funccolnames == NIL || !rte->funcordinality)) - { - get_rule_expr_funccall(rtfunc1->funcexpr, context, true); - /* we'll print the coldeflist below, if it has one */ - } - else - { - bool all_unnest; - ListCell *lc; - - /* - * If all the function calls in the list are to unnest, - * and none need a coldeflist, then collapse the list back - * down to UNNEST(args). (If we had more than one - * built-in unnest function, this would get more - * difficult.) - * - * XXX This is pretty ugly, since it makes not-terribly- - * future-proof assumptions about what the parser would do - * with the output; but the alternative is to emit our - * nonstandard ROWS FROM() notation for what might have - * been a perfectly spec-compliant multi-argument - * UNNEST(). - */ - all_unnest = true; - foreach(lc, rte->functions) - { - RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - - if (!IsA(rtfunc->funcexpr, FuncExpr) || - ((FuncExpr *) rtfunc->funcexpr)->funcid != F_ARRAY_UNNEST || - rtfunc->funccolnames != NIL) - { - all_unnest = false; - break; - } - } - - if (all_unnest) - { - List *allargs = NIL; - - foreach(lc, rte->functions) - { - RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - List *args = ((FuncExpr *) rtfunc->funcexpr)->args; - - allargs = list_concat(allargs, list_copy(args)); - } - - appendStringInfoString(buf, "UNNEST("); - get_rule_expr((Node *) allargs, context, true); - appendStringInfoChar(buf, ')'); - } - else - { - int funcno = 0; - - appendStringInfoString(buf, "ROWS FROM("); - foreach(lc, rte->functions) - { - RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - - if (funcno > 0) - appendStringInfoString(buf, ", "); - get_rule_expr_funccall(rtfunc->funcexpr, context, true); - if (rtfunc->funccolnames != NIL) - { - /* Reconstruct the column definition list */ - appendStringInfoString(buf, " AS "); - get_from_clause_coldeflist(rtfunc, - NULL, - context); - } - funcno++; - } - appendStringInfoChar(buf, ')'); - } - /* prevent printing duplicate coldeflist below */ - rtfunc1 = NULL; - } - if (rte->funcordinality) - appendStringInfoString(buf, " WITH ORDINALITY"); - break; - case RTE_TABLEFUNC: - get_tablefunc(rte->tablefunc, context, true); - break; - case RTE_VALUES: - /* Values list RTE */ - appendStringInfoChar(buf, '('); - get_values_def(rte->values_lists, context); - appendStringInfoChar(buf, ')'); - break; - case RTE_CTE: - appendStringInfoString(buf, quote_identifier(rte->ctename)); - break; - default: - elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind); - break; - } - - /* Print the relation alias, if needed */ - printalias = false; - if (rte->alias != NULL) - { - /* Always print alias if user provided one */ - printalias = true; - } - else if (colinfo->printaliases) - { - /* Always print alias if we need to print column aliases */ - printalias = true; - } - else if (rte->rtekind == RTE_RELATION) - { - /* - * No need to print alias if it's same as relation name (this - * would normally be the case, but not if set_rtable_names had to - * resolve a conflict). - */ - if (strcmp(refname, get_relation_name(rte->relid)) != 0) - printalias = true; - } - else if (rte->rtekind == RTE_FUNCTION) - { - /* - * For a function RTE, always print alias. This covers possible - * renaming of the function and/or instability of the - * FigureColname rules for things that aren't simple functions. - * Note we'd need to force it anyway for the columndef list case. - */ - printalias = true; - } - else if (rte->rtekind == RTE_VALUES) - { - /* Alias is syntactically required for VALUES */ - printalias = true; - } - else if (rte->rtekind == RTE_CTE) - { - /* - * No need to print alias if it's same as CTE name (this would - * normally be the case, but not if set_rtable_names had to - * resolve a conflict). - */ - if (strcmp(refname, rte->ctename) != 0) - printalias = true; - } - else if (rte->rtekind == RTE_SUBQUERY) - { - /* subquery requires alias too */ - printalias = true; - } - if (printalias) - appendStringInfo(buf, " %s", quote_identifier(refname)); - - /* Print the column definitions or aliases, if needed */ - if (rtfunc1 && rtfunc1->funccolnames != NIL) - { - /* Reconstruct the columndef list, which is also the aliases */ - get_from_clause_coldeflist(rtfunc1, colinfo, context); - } - else if (GetRangeTblKind(rte) != CITUS_RTE_SHARD) - { - /* Else print column aliases as needed */ - get_column_alias_list(colinfo, context); - } - /* check if column's are given aliases in distributed tables */ - else if (colinfo->parentUsing != NIL) - { - Assert(colinfo->printaliases); - get_column_alias_list(colinfo, context); - } - - /* Tablesample clause must go after any alias */ - if ((rteKind == CITUS_RTE_RELATION || rteKind == CITUS_RTE_SHARD) && - rte->tablesample) - { - get_tablesample_def(rte->tablesample, context); - } - } - else if (IsA(jtnode, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) jtnode; - deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns); - bool need_paren_on_right; - - need_paren_on_right = PRETTY_PAREN(context) && - !IsA(j->rarg, RangeTblRef) && - !(IsA(j->rarg, JoinExpr) &&((JoinExpr *) j->rarg)->alias != NULL); - - if (!PRETTY_PAREN(context) || j->alias != NULL) - appendStringInfoChar(buf, '('); - - get_from_clause_item(j->larg, query, context); - - switch (j->jointype) - { - case JOIN_INNER: - if (j->quals) - appendContextKeyword(context, " JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - else - appendContextKeyword(context, " CROSS JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - break; - case JOIN_LEFT: - appendContextKeyword(context, " LEFT JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - break; - case JOIN_FULL: - appendContextKeyword(context, " FULL JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - break; - case JOIN_RIGHT: - appendContextKeyword(context, " RIGHT JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - break; - default: - elog(ERROR, "unrecognized join type: %d", - (int) j->jointype); - } - - if (need_paren_on_right) - appendStringInfoChar(buf, '('); - get_from_clause_item(j->rarg, query, context); - if (need_paren_on_right) - appendStringInfoChar(buf, ')'); - - if (j->usingClause) - { - ListCell *lc; - bool first = true; - - appendStringInfoString(buf, " USING ("); - /* Use the assigned names, not what's in usingClause */ - foreach(lc, colinfo->usingNames) - { - char *colname = (char *) lfirst(lc); - - if (first) - first = false; - else - appendStringInfoString(buf, ", "); - appendStringInfoString(buf, quote_identifier(colname)); - } - appendStringInfoChar(buf, ')'); - } - else if (j->quals) - { - appendStringInfoString(buf, " ON "); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr(j->quals, context, false); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - else if (j->jointype != JOIN_INNER) - { - /* If we didn't say CROSS JOIN above, we must provide an ON */ - appendStringInfoString(buf, " ON TRUE"); - } - - if (!PRETTY_PAREN(context) || j->alias != NULL) - appendStringInfoChar(buf, ')'); - - /* Yes, it's correct to put alias after the right paren ... */ - if (j->alias != NULL) - { - /* - * Note that it's correct to emit an alias clause if and only if - * there was one originally. Otherwise we'd be converting a named - * join to unnamed or vice versa, which creates semantic - * subtleties we don't want. However, we might print a different - * alias name than was there originally. - */ - appendStringInfo(buf, " %s", - quote_identifier(get_rtable_name(j->rtindex, - context))); - get_column_alias_list(colinfo, context); - } - } - else - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(jtnode)); -} - -/* - * get_column_alias_list - print column alias list for an RTE - * - * Caller must already have printed the relation's alias name. - */ -static void -get_column_alias_list(deparse_columns *colinfo, deparse_context *context) -{ - StringInfo buf = context->buf; - int i; - bool first = true; - - /* Don't print aliases if not needed */ - if (!colinfo->printaliases) - return; - - for (i = 0; i < colinfo->num_new_cols; i++) - { - char *colname = colinfo->new_colnames[i]; - - if (first) - { - appendStringInfoChar(buf, '('); - first = false; - } - else - appendStringInfoString(buf, ", "); - appendStringInfoString(buf, quote_identifier(colname)); - } - if (!first) - appendStringInfoChar(buf, ')'); -} - -/* - * get_from_clause_coldeflist - reproduce FROM clause coldeflist - * - * When printing a top-level coldeflist (which is syntactically also the - * relation's column alias list), use column names from colinfo. But when - * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the - * original coldeflist's names, which are available in rtfunc->funccolnames. - * Pass NULL for colinfo to select the latter behavior. - * - * The coldeflist is appended immediately (no space) to buf. Caller is - * responsible for ensuring that an alias or AS is present before it. - */ -static void -get_from_clause_coldeflist(RangeTblFunction *rtfunc, - deparse_columns *colinfo, - deparse_context *context) -{ - StringInfo buf = context->buf; - ListCell *l1; - ListCell *l2; - ListCell *l3; - ListCell *l4; - int i; - - appendStringInfoChar(buf, '('); - - /* there's no forfour(), so must chase one list the hard way */ - i = 0; - l4 = list_head(rtfunc->funccolnames); - forthree(l1, rtfunc->funccoltypes, - l2, rtfunc->funccoltypmods, - l3, rtfunc->funccolcollations) - { - Oid atttypid = lfirst_oid(l1); - int32 atttypmod = lfirst_int(l2); - Oid attcollation = lfirst_oid(l3); - char *attname; - - if (colinfo) - attname = colinfo->colnames[i]; - else - attname = strVal(lfirst(l4)); - - Assert(attname); /* shouldn't be any dropped columns here */ - - if (i > 0) - appendStringInfoString(buf, ", "); - appendStringInfo(buf, "%s %s", - quote_identifier(attname), - format_type_with_typemod(atttypid, atttypmod)); - if (OidIsValid(attcollation) && - attcollation != get_typcollation(atttypid)) - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(attcollation)); - - l4 = lnext(l4); - i++; - } - - appendStringInfoChar(buf, ')'); -} - -/* - * get_tablesample_def - print a TableSampleClause - */ -static void -get_tablesample_def(TableSampleClause *tablesample, deparse_context *context) -{ - StringInfo buf = context->buf; - Oid argtypes[1]; - int nargs; - ListCell *l; - - /* - * We should qualify the handler's function name if it wouldn't be - * resolved by lookup in the current search path. - */ - argtypes[0] = INTERNALOID; - appendStringInfo(buf, " TABLESAMPLE %s (", - generate_function_name(tablesample->tsmhandler, 1, - NIL, argtypes, - false, NULL, EXPR_KIND_NONE)); - - nargs = 0; - foreach(l, tablesample->args) - { - if (nargs++ > 0) - appendStringInfoString(buf, ", "); - get_rule_expr((Node *) lfirst(l), context, false); - } - appendStringInfoChar(buf, ')'); - - if (tablesample->repeatable != NULL) - { - appendStringInfoString(buf, " REPEATABLE ("); - get_rule_expr((Node *) tablesample->repeatable, context, false); - appendStringInfoChar(buf, ')'); - } -} - -/* - * get_opclass_name - fetch name of an index operator class - * - * The opclass name is appended (after a space) to buf. - * - * Output is suppressed if the opclass is the default for the given - * actual_datatype. (If you don't want this behavior, just pass - * InvalidOid for actual_datatype.) - */ -static void -get_opclass_name(Oid opclass, Oid actual_datatype, - StringInfo buf) -{ - HeapTuple ht_opc; - Form_pg_opclass opcrec; - char *opcname; - char *nspname; - - ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass)); - if (!HeapTupleIsValid(ht_opc)) - elog(ERROR, "cache lookup failed for opclass %u", opclass); - opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc); - - if (!OidIsValid(actual_datatype) || - GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass) - { - /* Okay, we need the opclass name. Do we need to qualify it? */ - opcname = NameStr(opcrec->opcname); - if (OpclassIsVisible(opclass)) - appendStringInfo(buf, " %s", quote_identifier(opcname)); - else - { - nspname = get_namespace_name(opcrec->opcnamespace); - appendStringInfo(buf, " %s.%s", - quote_identifier(nspname), - quote_identifier(opcname)); - } - } - ReleaseSysCache(ht_opc); -} - -/* - * processIndirection - take care of array and subfield assignment - * - * We strip any top-level FieldStore or assignment ArrayRef nodes that - * appear in the input, printing them as decoration for the base column - * name (which we assume the caller just printed). We might also need to - * strip CoerceToDomain nodes, but only ones that appear above assignment - * nodes. - * - * Returns the subexpression that's to be assigned. - */ -static Node * -processIndirection(Node *node, deparse_context *context) -{ - StringInfo buf = context->buf; - CoerceToDomain *cdomain = NULL; - - for (;;) - { - if (node == NULL) - break; - if (IsA(node, FieldStore)) - { - FieldStore *fstore = (FieldStore *) node; - Oid typrelid; - char *fieldname; - - /* lookup tuple type */ - typrelid = get_typ_typrelid(fstore->resulttype); - if (!OidIsValid(typrelid)) - elog(ERROR, "argument type %s of FieldStore is not a tuple type", - format_type_be(fstore->resulttype)); - - /* - * Print the field name. There should only be one target field in - * stored rules. There could be more than that in executable - * target lists, but this function cannot be used for that case. - */ - Assert(list_length(fstore->fieldnums) == 1); - fieldname = get_relid_attribute_name(typrelid, - linitial_int(fstore->fieldnums)); - appendStringInfo(buf, ".%s", quote_identifier(fieldname)); - - /* - * We ignore arg since it should be an uninteresting reference to - * the target column or subcolumn. - */ - node = (Node *) linitial(fstore->newvals); - } - else if (IsA(node, ArrayRef)) - { - ArrayRef *aref = (ArrayRef *) node; - - if (aref->refassgnexpr == NULL) - break; - printSubscripts(aref, context); - - /* - * We ignore refexpr since it should be an uninteresting reference - * to the target column or subcolumn. - */ - node = (Node *) aref->refassgnexpr; - } - else if (IsA(node, CoerceToDomain)) - { - cdomain = (CoerceToDomain *) node; - /* If it's an explicit domain coercion, we're done */ - if (cdomain->coercionformat != COERCE_IMPLICIT_CAST) - break; - /* Tentatively descend past the CoerceToDomain */ - node = (Node *) cdomain->arg; - } - else - break; - } - - /* - * If we descended past a CoerceToDomain whose argument turned out not to - * be a FieldStore or array assignment, back up to the CoerceToDomain. - * (This is not enough to be fully correct if there are nested implicit - * CoerceToDomains, but such cases shouldn't ever occur.) - */ - if (cdomain && node == (Node *) cdomain->arg) - node = (Node *) cdomain; - - return node; -} - -static void -printSubscripts(ArrayRef *aref, deparse_context *context) -{ - StringInfo buf = context->buf; - ListCell *lowlist_item; - ListCell *uplist_item; - - lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */ - foreach(uplist_item, aref->refupperindexpr) - { - appendStringInfoChar(buf, '['); - if (lowlist_item) - { - /* If subexpression is NULL, get_rule_expr prints nothing */ - get_rule_expr((Node *) lfirst(lowlist_item), context, false); - appendStringInfoChar(buf, ':'); - lowlist_item = lnext(lowlist_item); - } - /* If subexpression is NULL, get_rule_expr prints nothing */ - get_rule_expr((Node *) lfirst(uplist_item), context, false); - appendStringInfoChar(buf, ']'); - } -} - -/* - * get_relation_name - * Get the unqualified name of a relation specified by OID - * - * This differs from the underlying get_rel_name() function in that it will - * throw error instead of silently returning NULL if the OID is bad. - */ -static char * -get_relation_name(Oid relid) -{ - char *relname = get_rel_name(relid); - - if (!relname) - elog(ERROR, "cache lookup failed for relation %u", relid); - return relname; -} - -/* - * generate_relation_or_shard_name - * Compute the name to display for a relation or shard - * - * If the provided relid is equal to the provided distrelid, this function - * returns a shard-extended relation name; otherwise, it falls through to a - * simple generate_relation_name call. - */ -static char * -generate_relation_or_shard_name(Oid relid, Oid distrelid, int64 shardid, - List *namespaces) -{ - char *relname = NULL; - - if (relid == distrelid) - { - relname = get_relation_name(relid); - - if (shardid > 0) - { - Oid schemaOid = get_rel_namespace(relid); - char *schemaName = get_namespace_name(schemaOid); - - AppendShardIdToName(&relname, shardid); - - relname = quote_qualified_identifier(schemaName, relname); - } - } - else - { - relname = generate_relation_name(relid, namespaces); - } - - return relname; -} - -/* - * generate_relation_name - * Compute the name to display for a relation specified by OID - * - * The result includes all necessary quoting and schema-prefixing. - * - * If namespaces isn't NIL, it must be a list of deparse_namespace nodes. - * We will forcibly qualify the relation name if it equals any CTE name - * visible in the namespace list. - */ -char * -generate_relation_name(Oid relid, List *namespaces) -{ - HeapTuple tp; - Form_pg_class reltup; - bool need_qual; - ListCell *nslist; - char *relname; - char *nspname; - char *result; - - tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); - if (!HeapTupleIsValid(tp)) - elog(ERROR, "cache lookup failed for relation %u", relid); - reltup = (Form_pg_class) GETSTRUCT(tp); - relname = NameStr(reltup->relname); - - /* Check for conflicting CTE name */ - need_qual = false; - foreach(nslist, namespaces) - { - deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist); - ListCell *ctlist; - - foreach(ctlist, dpns->ctes) - { - CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist); - - if (strcmp(cte->ctename, relname) == 0) - { - need_qual = true; - break; - } - } - if (need_qual) - break; - } - - /* Otherwise, qualify the name if not visible in search path */ - if (!need_qual) - need_qual = !RelationIsVisible(relid); - - if (need_qual) - nspname = get_namespace_name(reltup->relnamespace); - else - nspname = NULL; - - result = quote_qualified_identifier(nspname, relname); - - ReleaseSysCache(tp); - - return result; -} - -/* - * generate_fragment_name - * Compute the name to display for a shard or merged table - * - * The result includes all necessary quoting and schema-prefixing. The schema - * name can be NULL for regular shards. For merged tables, they are always - * declared within a job-specific schema, and therefore can't have null schema - * names. - */ -static char * -generate_fragment_name(char *schemaName, char *tableName) -{ - StringInfo fragmentNameString = makeStringInfo(); - - if (schemaName != NULL) - { - appendStringInfo(fragmentNameString, "%s.%s", quote_identifier(schemaName), - quote_identifier(tableName)); - } - else - { - appendStringInfoString(fragmentNameString, quote_identifier(tableName)); - } - - return fragmentNameString->data; -} - -/* - * generate_function_name - * Compute the name to display for a function specified by OID, - * given that it is being called with the specified actual arg names and - * types. (Those matter because of ambiguous-function resolution rules.) - * - * If we're dealing with a potentially variadic function (in practice, this - * means a FuncExpr or Aggref, not some other way of calling a function), then - * has_variadic must specify whether variadic arguments have been merged, - * and *use_variadic_p will be set to indicate whether to print VARIADIC in - * the output. For non-FuncExpr cases, has_variadic should be FALSE and - * use_variadic_p can be NULL. - * - * The result includes all necessary quoting and schema-prefixing. - */ -static char * -generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, - bool has_variadic, bool *use_variadic_p, - ParseExprKind special_exprkind) -{ - char *result; - HeapTuple proctup; - Form_pg_proc procform; - char *proname; - bool use_variadic; - char *nspname; - FuncDetailCode p_result; - Oid p_funcid; - Oid p_rettype; - bool p_retset; - int p_nvargs; - Oid p_vatype; - Oid *p_true_typeids; - bool force_qualify = false; - - proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); - if (!HeapTupleIsValid(proctup)) - elog(ERROR, "cache lookup failed for function %u", funcid); - procform = (Form_pg_proc) GETSTRUCT(proctup); - proname = NameStr(procform->proname); - - /* - * Due to parser hacks to avoid needing to reserve CUBE, we need to force - * qualification in some special cases. - */ - if (special_exprkind == EXPR_KIND_GROUP_BY) - { - if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0) - force_qualify = true; - } - - /* - * Determine whether VARIADIC should be printed. We must do this first - * since it affects the lookup rules in func_get_detail(). - * - * Currently, we always print VARIADIC if the function has a merged - * variadic-array argument. Note that this is always the case for - * functions taking a VARIADIC argument type other than VARIADIC ANY. - * - * In principle, if VARIADIC wasn't originally specified and the array - * actual argument is deconstructable, we could print the array elements - * separately and not print VARIADIC, thus more nearly reproducing the - * original input. For the moment that seems like too much complication - * for the benefit, and anyway we do not know whether VARIADIC was - * originally specified if it's a non-ANY type. - */ - if (use_variadic_p) - { - /* Parser should not have set funcvariadic unless fn is variadic */ - Assert(!has_variadic || OidIsValid(procform->provariadic)); - use_variadic = has_variadic; - *use_variadic_p = use_variadic; - } - else - { - Assert(!has_variadic); - use_variadic = false; - } - - /* - * The idea here is to schema-qualify only if the parser would fail to - * resolve the correct function given the unqualified func name with the - * specified argtypes and VARIADIC flag. But if we already decided to - * force qualification, then we can skip the lookup and pretend we didn't - * find it. - */ - if (!force_qualify) - p_result = func_get_detail(list_make1(makeString(proname)), - NIL, argnames, nargs, argtypes, - !use_variadic, true, - &p_funcid, &p_rettype, - &p_retset, &p_nvargs, &p_vatype, - &p_true_typeids, NULL); - else - { - p_result = FUNCDETAIL_NOTFOUND; - p_funcid = InvalidOid; - } - - if ((p_result == FUNCDETAIL_NORMAL || - p_result == FUNCDETAIL_AGGREGATE || - p_result == FUNCDETAIL_WINDOWFUNC) && - p_funcid == funcid) - nspname = NULL; - else - nspname = get_namespace_name(procform->pronamespace); - - result = quote_qualified_identifier(nspname, proname); - - ReleaseSysCache(proctup); - - return result; -} - -/* - * generate_operator_name - * Compute the name to display for an operator specified by OID, - * given that it is being called with the specified actual arg types. - * (Arg types matter because of ambiguous-operator resolution rules. - * Pass InvalidOid for unused arg of a unary operator.) - * - * The result includes all necessary quoting and schema-prefixing, - * plus the OPERATOR() decoration needed to use a qualified operator name - * in an expression. - */ -static char * -generate_operator_name(Oid operid, Oid arg1, Oid arg2) -{ - StringInfoData buf; - HeapTuple opertup; - Form_pg_operator operform; - char *oprname; - char *nspname; - - initStringInfo(&buf); - - opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid)); - if (!HeapTupleIsValid(opertup)) - elog(ERROR, "cache lookup failed for operator %u", operid); - operform = (Form_pg_operator) GETSTRUCT(opertup); - oprname = NameStr(operform->oprname); - - /* - * Unlike generate_operator_name() in postgres/src/backend/utils/adt/ruleutils.c, - * we don't check if the operator is in current namespace or not. This is - * because this check is costly when the operator is not in current namespace. - */ - nspname = get_namespace_name(operform->oprnamespace); - Assert(nspname != NULL); - appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname)); - appendStringInfoString(&buf, oprname); - appendStringInfoChar(&buf, ')'); - - ReleaseSysCache(opertup); - - return buf.data; -} - -/* - * get_one_range_partition_bound_string - * A C string representation of one range partition bound - */ -char * -get_range_partbound_string(List *bound_datums) -{ - deparse_context context; - StringInfo buf = makeStringInfo(); - ListCell *cell; - char *sep; - - memset(&context, 0, sizeof(deparse_context)); - context.buf = buf; - - appendStringInfoString(buf, "("); - sep = ""; - foreach(cell, bound_datums) - { - PartitionRangeDatum *datum = - castNode(PartitionRangeDatum, lfirst(cell)); - - appendStringInfoString(buf, sep); - if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE) - appendStringInfoString(buf, "MINVALUE"); - else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE) - appendStringInfoString(buf, "MAXVALUE"); - else - { - Const *val = castNode(Const, datum->value); - - get_const_expr(val, &context, -1); - } - sep = ", "; - } - appendStringInfoString(buf, ")"); - - return buf->data; -} - -#endif /* (PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 110000) */ diff --git a/src/backend/distributed/worker/worker_merge_protocol.c b/src/backend/distributed/worker/worker_merge_protocol.c index 1b9c4cca0..ab997cf16 100644 --- a/src/backend/distributed/worker/worker_merge_protocol.c +++ b/src/backend/distributed/worker/worker_merge_protocol.c @@ -382,7 +382,7 @@ RemoveJobSchema(StringInfo schemaName) bool permissionsOK = pg_namespace_ownercheck(schemaId, GetUserId()); if (!permissionsOK) { - aclcheck_error(ACLCHECK_NOT_OWNER, ACLCHECK_OBJECT_SCHEMA, schemaName->data); + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA, schemaName->data); } schemaObject.classId = NamespaceRelationId; diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index fd1f20fee..6b45dfb59 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -23,11 +23,8 @@ /* cluster.c - forward declarations */ extern List * PlanClusterStmt(ClusterStmt *clusterStmt, const char *clusterCommand); -#if PG_VERSION_NUM >= 110000 - /* call.c */ extern bool CallDistributedProcedureRemotely(CallStmt *callStmt, DestReceiver *dest); -#endif /* PG_VERSION_NUM >= 110000 */ /* extension.c - forward declarations */ extern bool IsCitusExtensionStmt(Node *parsetree); diff --git a/src/include/distributed/version_compat.h b/src/include/distributed/version_compat.h index df592213f..51388d92d 100644 --- a/src/include/distributed/version_compat.h +++ b/src/include/distributed/version_compat.h @@ -16,260 +16,10 @@ #include "catalog/namespace.h" #include "nodes/parsenodes.h" #include "parser/parse_func.h" - #if (PG_VERSION_NUM >= 120000) #include "optimizer/optimizer.h" #endif -#if (PG_VERSION_NUM < 110000) - -#include "access/hash.h" -#include "storage/fd.h" -#include "optimizer/prep.h" -#include "postmaster/bgworker.h" -#include "utils/memutils.h" -#include "funcapi.h" - -/* PostgreSQL 11 splits hash procs into "standard" and "extended" */ -#define HASHSTANDARD_PROC HASHPROC - -/* following functions are renamed in PG11 */ -#define PreventInTransactionBlock PreventTransactionChain -#define DatumGetJsonbP(d) DatumGetJsonb(d) -#define RequireTransactionBlock RequireTransactionChain - -/* following defines also exist for PG11 */ -#define RELATION_OBJECT_TYPE ACL_OBJECT_RELATION -#define IndexInfoAttributeNumberArray(indexinfo) (indexinfo->ii_KeyAttrNumbers) - -/* CreateTrigger api is changed in PG11 */ -#define CreateTriggerInternal(stmt, queryString, relOid, refRelOid, constraintOid, \ - indexOid, funcoid, parentTriggerOid, whenClause, isInternal, \ - in_partition) \ - CreateTrigger(stmt, queryString, relOid, refRelOid, constraintOid, indexOid, \ - isInternal) - -#define get_attname_internal(relationId, columnNumber, false) \ - get_attname(relationId, columnNumber) - -#define BackgroundWorkerInitializeConnectionByOid(dboid, useroid, flags) \ - BackgroundWorkerInitializeConnectionByOid(dboid, useroid) - -#define AtEOXact_Files(isCommit) \ - AtEOXact_Files() - -#define ACLCHECK_OBJECT_TABLE ACL_KIND_CLASS -#define ACLCHECK_OBJECT_SCHEMA ACL_KIND_NAMESPACE -#define ACLCHECK_OBJECT_INDEX ACL_KIND_CLASS -#define ACLCHECK_OBJECT_SEQUENCE ACL_KIND_CLASS - - -static inline int -BasicOpenFilePerm(FileName fileName, int fileFlags, int fileMode) -{ - return BasicOpenFile(fileName, fileFlags, fileMode); -} - - -static inline File -PathNameOpenFilePerm(FileName fileName, int fileFlags, int fileMode) -{ - return PathNameOpenFile(fileName, fileFlags, fileMode); -} - - -static inline MemoryContext -AllocSetContextCreateExtended(MemoryContext parent, const char *name, Size minContextSize, - Size initBlockSize, Size maxBlockSize) -{ - return AllocSetContextCreate(parent, name, minContextSize, initBlockSize, - maxBlockSize); -} - - -static inline void -ExplainPropertyIntegerInternal(const char *qlabel, const char *unit, int64 value, - ExplainState *es) -{ - return ExplainPropertyInteger(qlabel, value, es); -} - - -static inline List * -ExtractVacuumTargetRels(VacuumStmt *vacuumStmt) -{ - List *vacuumList = NIL; - - if (vacuumStmt->relation != NULL) - { - vacuumList = list_make1(vacuumStmt->relation); - } - - return vacuumList; -} - - -static inline List * -VacuumColumnList(VacuumStmt *vacuumStmt, int relationIndex) -{ - Assert(relationIndex == 0); - - return vacuumStmt->va_cols; -} - - -#define RVR_MISSING_OK 1 -#define RVR_NOWAIT 2 - -static inline Oid -RangeVarGetRelidInternal(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, - RangeVarGetRelidCallback callback, void *callback_arg) -{ - bool missingOK = ((flags & RVR_MISSING_OK) != 0); - bool noWait = ((flags & RVR_NOWAIT) != 0); - - return RangeVarGetRelidExtended(relation, lockmode, missingOK, noWait, - callback, callback_arg); -} - - -static inline Expr * -canonicalize_qual_compat(Expr *qual, bool is_check) -{ - return canonicalize_qual(qual); -} - - -/* - * A convenient wrapper around get_expr_result_type() that is added on PG11 - * - * Note that this function ignores the second parameter and behaves - * slightly differently than the PG11 version. - * - * 1. The original function throws errors if noError flag is not set, we ignore - * this flag here and return NULL in that case - * 2. TYPEFUNC_COMPOSITE_DOMAIN is introduced in PG11, and references to this - * macro is removed - * */ -static inline TupleDesc -get_expr_result_tupdesc(Node *expr, bool noError) -{ - TupleDesc tupleDesc; - TypeFuncClass functypclass; - - functypclass = get_expr_result_type(expr, NULL, &tupleDesc); - - if (functypclass == TYPEFUNC_COMPOSITE) - { - return tupleDesc; - } - return NULL; -} - - -/* following compat function and macro should be removed when we drop support for PG10 */ -static inline Oid -LookupFuncWithArgsCompat(ObjectType objtype, ObjectWithArgs *func, bool noError) -{ - if (objtype == OBJECT_FUNCTION) - { - return LookupFuncWithArgs(func, noError); - } - else if (objtype == OBJECT_AGGREGATE) - { - return LookupAggWithArgs(func, noError); - } - - return InvalidOid; -} - - -#endif - -#if (PG_VERSION_NUM >= 110000) -#include "optimizer/prep.h" - -/* following macros should be removed when we drop support for PG10 and below */ -#define RELATION_OBJECT_TYPE OBJECT_TABLE -#define IndexInfoAttributeNumberArray(indexinfo) (indexinfo->ii_IndexAttrNumbers) -#define CreateTriggerInternal CreateTrigger -#define get_attname_internal get_attname - -#define ACLCHECK_OBJECT_TABLE OBJECT_TABLE -#define ACLCHECK_OBJECT_SCHEMA OBJECT_SCHEMA -#define ACLCHECK_OBJECT_INDEX OBJECT_INDEX -#define ACLCHECK_OBJECT_SEQUENCE OBJECT_SEQUENCE - - -#define ConstraintRelidIndexId ConstraintRelidTypidNameIndexId - -static inline void -ExplainPropertyIntegerInternal(const char *qlabel, const char *unit, int64 value, - ExplainState *es) -{ - return ExplainPropertyInteger(qlabel, unit, value, es); -} - - -static inline Expr * -canonicalize_qual_compat(Expr *qual, bool is_check) -{ - return canonicalize_qual(qual, is_check); -} - - -/* - * ExtractVacuumTargetRels returns list of target - * relations from vacuum statement. - */ -static inline List * -ExtractVacuumTargetRels(VacuumStmt *vacuumStmt) -{ - List *vacuumList = NIL; - - ListCell *vacuumRelationCell = NULL; - foreach(vacuumRelationCell, vacuumStmt->rels) - { - VacuumRelation *vacuumRelation = (VacuumRelation *) lfirst(vacuumRelationCell); - vacuumList = lappend(vacuumList, vacuumRelation->relation); - } - - return vacuumList; -} - - -/* - * VacuumColumnList returns list of columns from relation - * in the vacuum statement at specified relationIndex. - */ -static inline List * -VacuumColumnList(VacuumStmt *vacuumStmt, int relationIndex) -{ - VacuumRelation *vacuumRelation = (VacuumRelation *) list_nth(vacuumStmt->rels, - relationIndex); - - return vacuumRelation->va_cols; -} - - -static inline Oid -RangeVarGetRelidInternal(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, - RangeVarGetRelidCallback callback, void *callback_arg) -{ - return RangeVarGetRelidExtended(relation, lockmode, flags, callback, callback_arg); -} - - -/* following compat function and macro should be removed when we drop support for PG10 */ -static inline Oid -LookupFuncWithArgsCompat(ObjectType objtype, ObjectWithArgs *func, bool noError) -{ - return LookupFuncWithArgs(objtype, func, noError); -} - - -#endif - #if PG_VERSION_NUM >= 120000 #define MakeSingleTupleTableSlotCompat MakeSingleTupleTableSlot diff --git a/src/test/regress/expected/distributed_procedure.out b/src/test/regress/expected/distributed_procedure.out index 673fc2df6..fb1fceb37 100644 --- a/src/test/regress/expected/distributed_procedure.out +++ b/src/test/regress/expected/distributed_procedure.out @@ -1,10 +1,3 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above - \gset -\if :server_verion_eleven_and_above -\else -\q -\endif SET citus.next_shard_id TO 20030000; CREATE USER procedureuser; NOTICE: not propagating CREATE ROLE/USER commands to worker nodes diff --git a/src/test/regress/expected/distributed_procedure_0.out b/src/test/regress/expected/distributed_procedure_0.out deleted file mode 100644 index a5ea9fa80..000000000 --- a/src/test/regress/expected/distributed_procedure_0.out +++ /dev/null @@ -1,6 +0,0 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above - \gset -\if :server_verion_eleven_and_above -\else -\q diff --git a/src/test/regress/expected/distributed_types_0.out b/src/test/regress/expected/distributed_types_0.out deleted file mode 100644 index d27e5d686..000000000 --- a/src/test/regress/expected/distributed_types_0.out +++ /dev/null @@ -1,392 +0,0 @@ -SET citus.next_shard_id TO 20010000; -CREATE USER typeuser; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -SELECT run_command_on_workers($$CREATE USER typeuser;$$); - run_command_on_workers ------------------------------------ - (localhost,57637,t,"CREATE ROLE") - (localhost,57638,t,"CREATE ROLE") -(2 rows) - -CREATE SCHEMA type_tests AUTHORIZATION typeuser; -CREATE SCHEMA type_tests2 AUTHORIZATION typeuser; -- to test creation in a specific schema and moving to schema -SET search_path TO type_tests; -SET citus.shard_count TO 4; --- single statement transactions with a simple type used in a table -CREATE TYPE tc1 AS (a int, b int); -CREATE TABLE t1 (a int PRIMARY KEY, b tc1); -SELECT create_distributed_table('t1','a'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO t1 VALUES (1, (2,3)::tc1); -SELECT * FROM t1; - a | b ----+------- - 1 | (2,3) -(1 row) - -ALTER TYPE tc1 RENAME TO tc1_newname; -INSERT INTO t1 VALUES (3, (4,5)::tc1_newname); -- insert with a cast would fail if the rename didn't propagate -ALTER TYPE tc1_newname SET SCHEMA type_tests2; -INSERT INTO t1 VALUES (6, (7,8)::type_tests2.tc1_newname); -- insert with a cast would fail if the rename didn't propagate --- single statement transactions with a an enum used in a table -CREATE TYPE te1 AS ENUM ('one', 'two', 'three'); -CREATE TABLE t2 (a int PRIMARY KEY, b te1); -SELECT create_distributed_table('t2','a'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO t2 VALUES (1, 'two'); -SELECT * FROM t2; - a | b ----+----- - 1 | two -(1 row) - --- rename enum, subsequent operations on the type would fail if the rename was not propagated -ALTER TYPE te1 RENAME TO te1_newname; --- add an extra value to the enum and use in table -ALTER TYPE te1_newname ADD VALUE 'four'; -UPDATE t2 SET b = 'four'; -SELECT * FROM t2; - a | b ----+------ - 1 | four -(1 row) - --- change the schema of the type and use the new fully qualified name in an insert -ALTER TYPE te1_newname SET SCHEMA type_tests2; -INSERT INTO t2 VALUES (3, 'three'::type_tests2.te1_newname); --- transaction block with simple type -BEGIN; -CREATE TYPE tc2 AS (a int, b int); -CREATE TABLE t3 (a int PRIMARY KEY, b tc2); -SELECT create_distributed_table('t3','a'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO t3 VALUES (4, (5,6)::tc2); -SELECT * FROM t3; - a | b ----+------- - 4 | (5,6) -(1 row) - -COMMIT; --- transaction block with simple type -BEGIN; -CREATE TYPE te2 AS ENUM ('yes', 'no'); -CREATE TABLE t4 (a int PRIMARY KEY, b te2); -SELECT create_distributed_table('t4','a'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO t4 VALUES (1, 'yes'); -SELECT * FROM t4; - a | b ----+----- - 1 | yes -(1 row) - --- ALTER TYPE ... ADD VALUE does not work in transactions -COMMIT; --- verify order of enum labels -SELECT string_agg(enumlabel, ',' ORDER BY enumsortorder ASC) FROM pg_enum WHERE enumtypid = 'type_tests.te2'::regtype; - string_agg ------------- - yes,no -(1 row) - -SELECT run_command_on_workers($$SELECT string_agg(enumlabel, ',' ORDER BY enumsortorder ASC) FROM pg_enum WHERE enumtypid = 'type_tests.te2'::regtype;$$); - run_command_on_workers ------------------------------- - (localhost,57637,t,"yes,no") - (localhost,57638,t,"yes,no") -(2 rows) - --- test some combination of types without ddl propagation, this will prevent the workers --- from having those types created. They are created just-in-time on table distribution -SET citus.enable_ddl_propagation TO off; -CREATE TYPE tc3 AS (a int, b int); -CREATE TYPE tc4 AS (a int, b tc3[]); -CREATE TYPE tc5 AS (a int, b tc4); -CREATE TYPE te3 AS ENUM ('a','b'); -RESET citus.enable_ddl_propagation; -CREATE TABLE t5 (a int PRIMARY KEY, b tc5[], c te3); -SELECT create_distributed_table('t5','a'); - create_distributed_table --------------------------- - -(1 row) - --- test adding an attribute to a type and a column to a table both for a non-distributed type -SET citus.enable_ddl_propagation TO off; -CREATE TYPE te4 AS ENUM ('c','d'); -CREATE TYPE tc6 AS (a int, b int); -CREATE TYPE tc6c AS (a int, b int); -RESET citus.enable_ddl_propagation; --- types need to be fully qualified because of the search_path which is not supported by ALTER TYPE ... ADD COLUMN -ALTER TABLE t5 ADD COLUMN d type_tests.te4; -ALTER TABLE t5 ADD COLUMN e type_tests.tc6; -ALTER TYPE tc6 ADD ATTRIBUTE c tc6c; --- last two values are only there if above commands succeeded -INSERT INTO t5 VALUES (1, NULL, 'a', 'd', (1,2,(4,5)::tc6c)::tc6); --- test renaming an attribute of a distrbuted type and read it by its new name to verify propagation -ALTER TYPE tc6 RENAME ATTRIBUTE b TO d; -SELECT (e::tc6).d FROM t5 ORDER BY 1; - d ---- - 2 -(1 row) - --- change owner of supported types and check ownership on remote server -ALTER TYPE te4 OWNER TO typeuser; -SELECT typname, usename FROM pg_type, pg_user where typname = 'te4' and typowner = usesysid; - typname | usename ----------+---------- - te4 | typeuser -(1 row) - -SELECT run_command_on_workers($$SELECT row(typname, usename) FROM pg_type, pg_user where typname = 'te4' and typowner = usesysid;$$); - run_command_on_workers --------------------------------------- - (localhost,57637,t,"(te4,typeuser)") - (localhost,57638,t,"(te4,typeuser)") -(2 rows) - -ALTER TYPE tc6 OWNER TO typeuser; -SELECT typname, usename FROM pg_type, pg_user where typname = 'tc6' and typowner = usesysid; - typname | usename ----------+---------- - tc6 | typeuser -(1 row) - -SELECT run_command_on_workers($$SELECT row(typname, usename) FROM pg_type, pg_user where typname = 'tc6' and typowner = usesysid;$$); - run_command_on_workers --------------------------------------- - (localhost,57637,t,"(tc6,typeuser)") - (localhost,57638,t,"(tc6,typeuser)") -(2 rows) - --- create a type as a different user -SET ROLE typeuser; --- create directly on the worker -CREATE TYPE tc7 AS (a int, b int); -CREATE TYPE te5 AS ENUM ('a','b','c'); --- cascade to the worker when table gets created -SET citus.enable_ddl_propagation TO off; -CREATE TYPE tc8 AS (a int, b int); -CREATE TYPE te6 AS ENUM ('a','b','c'); -RESET citus.enable_ddl_propagation; -CREATE TABLE t6 (a int, b tc8, c te6); -SELECT create_distributed_table('t6', 'a'); - create_distributed_table --------------------------- - -(1 row) - -RESET ROLE; --- test ownership of all types -SELECT typname, usename FROM pg_type, pg_user where typname = 'tc7' and typowner = usesysid; - typname | usename ----------+---------- - tc7 | typeuser -(1 row) - -SELECT run_command_on_workers($$SELECT row(typname, usename) FROM pg_type, pg_user where typname = 'tc7' and typowner = usesysid;$$); - run_command_on_workers --------------------------------------- - (localhost,57637,t,"(tc7,typeuser)") - (localhost,57638,t,"(tc7,typeuser)") -(2 rows) - -SELECT typname, usename FROM pg_type, pg_user where typname = 'te5' and typowner = usesysid; - typname | usename ----------+---------- - te5 | typeuser -(1 row) - -SELECT run_command_on_workers($$SELECT row(typname, usename) FROM pg_type, pg_user where typname = 'te5' and typowner = usesysid;$$); - run_command_on_workers --------------------------------------- - (localhost,57637,t,"(te5,typeuser)") - (localhost,57638,t,"(te5,typeuser)") -(2 rows) - -SELECT typname, usename FROM pg_type, pg_user where typname = 'tc8' and typowner = usesysid; - typname | usename ----------+---------- - tc8 | typeuser -(1 row) - -SELECT run_command_on_workers($$SELECT row(typname, usename) FROM pg_type, pg_user where typname = 'tc8' and typowner = usesysid;$$); - run_command_on_workers --------------------------------------- - (localhost,57637,t,"(tc8,typeuser)") - (localhost,57638,t,"(tc8,typeuser)") -(2 rows) - -SELECT typname, usename FROM pg_type, pg_user where typname = 'te6' and typowner = usesysid; - typname | usename ----------+---------- - te6 | typeuser -(1 row) - -SELECT run_command_on_workers($$SELECT row(typname, usename) FROM pg_type, pg_user where typname = 'te6' and typowner = usesysid;$$); - run_command_on_workers --------------------------------------- - (localhost,57637,t,"(te6,typeuser)") - (localhost,57638,t,"(te6,typeuser)") -(2 rows) - --- deleting the enum cascade will remove the type from the table and the workers -DROP TYPE te3 CASCADE; -NOTICE: drop cascades to table t5 column c --- DELETE multiple types at once -DROP TYPE tc3, tc4, tc5 CASCADE; -NOTICE: drop cascades to table t5 column b --- test if the types are deleted -SELECT typname FROM pg_type, pg_user where typname IN ('te3','tc3','tc4','tc5') and typowner = usesysid ORDER BY typname; - typname ---------- -(0 rows) - -SELECT run_command_on_workers($$SELECT typname FROM pg_type, pg_user where typname IN ('te3','tc3','tc4','tc5') and typowner = usesysid ORDER BY typname;$$); - run_command_on_workers ------------------------- - (localhost,57637,t,"") - (localhost,57638,t,"") -(2 rows) - --- make sure attribute names are quoted correctly, no errors indicates types are propagated correctly -CREATE TYPE tc9 AS ("field-with-dashes" text COLLATE "POSIX"); -ALTER TYPE tc9 ADD ATTRIBUTE "some-more" int, ADD ATTRIBUTE normal int; -ALTER TYPE tc9 RENAME ATTRIBUTE normal TO "not-so-normal"; --- test alter statements for non-distributed types, if they would be propagated they would --- error, preventing from changing them -SET citus.enable_ddl_propagation TO off; -CREATE TYPE non_distributed_composite_type AS (a int, b int); -CREATE TYPE non_distributed_enum_type AS ENUM ('a', 'c'); -SET citus.enable_ddl_propagation TO on; -ALTER TYPE non_distributed_composite_type ADD ATTRIBUTE c int; -ALTER TYPE non_distributed_composite_type RENAME ATTRIBUTE c TO d; -ALTER TYPE non_distributed_composite_type ALTER ATTRIBUTE d SET DATA TYPE text COLLATE "POSIX" CASCADE; -ALTER TYPE non_distributed_composite_type DROP ATTRIBUTE d; -ALTER TYPE non_distributed_composite_type OWNER TO typeuser; -ALTER TYPE non_distributed_composite_type RENAME TO non_distributed_composite_type_renamed; -ALTER TYPE non_distributed_composite_type_renamed RENAME TO non_distributed_composite_type; -ALTER TYPE non_distributed_composite_type SET SCHEMA type_tests2; -ALTER TYPE type_tests2.non_distributed_composite_type SET SCHEMA type_tests; -ALTER TYPE non_distributed_enum_type OWNER TO typeuser; -ALTER TYPE non_distributed_enum_type RENAME TO non_distributed_enum_type_renamed; -ALTER TYPE non_distributed_enum_type_renamed RENAME TO non_distributed_enum_type; -ALTER TYPE non_distributed_enum_type SET SCHEMA type_tests2; -ALTER TYPE type_tests2.non_distributed_enum_type SET SCHEMA type_tests; -ALTER TYPE non_distributed_enum_type ADD VALUE 'b' BEFORE 'c'; -ALTER TYPE non_distributed_enum_type ADD VALUE 'd' AFTER 'c'; -ALTER TYPE non_distributed_enum_type RENAME VALUE 'd' TO 'something-with-quotes''andstuff'; --- test all forms of alter statements on distributed types -CREATE TYPE distributed_composite_type AS (a int, b int); -CREATE TYPE distributed_enum_type AS ENUM ('a', 'c'); --- enforce distribution of types in every case -CREATE TABLE type_proc (a int, b distributed_composite_type, c distributed_enum_type); -SELECT create_distributed_table('type_proc','a'); - create_distributed_table --------------------------- - -(1 row) - -DROP TABLE type_proc; -ALTER TYPE distributed_composite_type ADD ATTRIBUTE c int; -ALTER TYPE distributed_composite_type RENAME ATTRIBUTE c TO d; -ALTER TYPE distributed_composite_type ALTER ATTRIBUTE d SET DATA TYPE text COLLATE "POSIX" CASCADE; -ALTER TYPE distributed_composite_type DROP ATTRIBUTE d; -ALTER TYPE distributed_composite_type OWNER TO typeuser; -ALTER TYPE distributed_composite_type RENAME TO distributed_composite_type_renamed; -ALTER TYPE distributed_composite_type_renamed RENAME TO distributed_composite_type; -ALTER TYPE distributed_composite_type SET SCHEMA type_tests2; -ALTER TYPE type_tests2.distributed_composite_type SET SCHEMA type_tests; -ALTER TYPE distributed_enum_type OWNER TO typeuser; -ALTER TYPE distributed_enum_type RENAME TO distributed_enum_type_renamed; -ALTER TYPE distributed_enum_type_renamed RENAME TO distributed_enum_type; -ALTER TYPE distributed_enum_type SET SCHEMA type_tests2; -ALTER TYPE type_tests2.distributed_enum_type SET SCHEMA type_tests; -ALTER TYPE distributed_enum_type ADD VALUE 'b' BEFORE 'c'; -ALTER TYPE distributed_enum_type ADD VALUE 'd' AFTER 'c'; -ALTER TYPE distributed_enum_type RENAME VALUE 'd' TO 'something-with-quotes''andstuff'; --- make sure types are not distributed by default when feature flag is turned off -SET citus.enable_create_type_propagation TO off; -CREATE TYPE feature_flag_composite_type AS (a int, b int); -CREATE TYPE feature_flag_enum_type AS ENUM ('a', 'b'); --- verify types do not exist on workers -SELECT count(*) FROM pg_type where typname IN ('feature_flag_composite_type', 'feature_flag_enum_type'); - count -------- - 2 -(1 row) - -SELECT run_command_on_workers($$SELECT count(*) FROM pg_type where typname IN ('feature_flag_composite_type', 'feature_flag_enum_type');$$); - run_command_on_workers ------------------------- - (localhost,57637,t,0) - (localhost,57638,t,0) -(2 rows) - --- verify they are still distributed when required -CREATE TABLE feature_flag_table (a int PRIMARY KEY, b feature_flag_composite_type, c feature_flag_enum_type); -SELECT create_distributed_table('feature_flag_table','a'); - create_distributed_table --------------------------- - -(1 row) - -SELECT count(*) FROM pg_type where typname IN ('feature_flag_composite_type', 'feature_flag_enum_type'); - count -------- - 2 -(1 row) - -SELECT run_command_on_workers($$SELECT count(*) FROM pg_type where typname IN ('feature_flag_composite_type', 'feature_flag_enum_type');$$); - run_command_on_workers ------------------------- - (localhost,57637,t,2) - (localhost,57638,t,2) -(2 rows) - -RESET citus.enable_create_type_propagation; --- clear objects -SET client_min_messages TO error; -- suppress cascading objects dropping -DROP SCHEMA type_tests CASCADE; -SELECT run_command_on_workers($$DROP SCHEMA type_tests CASCADE;$$); - run_command_on_workers ------------------------------------ - (localhost,57637,t,"DROP SCHEMA") - (localhost,57638,t,"DROP SCHEMA") -(2 rows) - -DROP SCHEMA type_tests2 CASCADE; -SELECT run_command_on_workers($$DROP SCHEMA type_tests2 CASCADE;$$); - run_command_on_workers ------------------------------------ - (localhost,57637,t,"DROP SCHEMA") - (localhost,57638,t,"DROP SCHEMA") -(2 rows) - -DROP USER typeuser; -SELECT run_command_on_workers($$DROP USER typeuser;$$); - run_command_on_workers ---------------------------------- - (localhost,57637,t,"DROP ROLE") - (localhost,57638,t,"DROP ROLE") -(2 rows) - diff --git a/src/test/regress/expected/failure_vacuum.out b/src/test/regress/expected/failure_vacuum.out index d71e8194f..50fde6940 100644 --- a/src/test/regress/expected/failure_vacuum.out +++ b/src/test/regress/expected/failure_vacuum.out @@ -1,14 +1,6 @@ -- We have different output files for the executor. This is because -- we don't mark transactions with ANALYZE as critical anymore, and -- get WARNINGs instead of ERRORs. --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - SET citus.next_shard_id TO 12000000; SELECT citus.mitmproxy('conn.allow()'); mitmproxy diff --git a/src/test/regress/expected/failure_vacuum_0.out b/src/test/regress/expected/failure_vacuum_0.out deleted file mode 100644 index c4cba7a55..000000000 --- a/src/test/regress/expected/failure_vacuum_0.out +++ /dev/null @@ -1,138 +0,0 @@ --- We have different output files for the executor. The executor --- with PG10 behaves like non-executor PG11, and with PG11 it --- behaves like non-executor PG10. --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - f -(1 row) - -SELECT citus.mitmproxy('conn.allow()'); - mitmproxy ------------ - -(1 row) - -SET citus.shard_count = 1; -SET citus.shard_replication_factor = 2; -- one shard per worker -SET citus.multi_shard_commit_protocol TO '1pc'; -CREATE TABLE vacuum_test (key int, value int); -SELECT create_distributed_table('vacuum_test', 'key'); - create_distributed_table --------------------------- - -(1 row) - -SELECT citus.clear_network_traffic(); - clear_network_traffic ------------------------ - -(1 row) - -SELECT citus.mitmproxy('conn.onQuery(query="^VACUUM").kill()'); - mitmproxy ------------ - -(1 row) - -VACUUM vacuum_test; -ERROR: server closed the connection unexpectedly - This probably means the server terminated abnormally - before or while processing the request. -CONTEXT: while executing command on localhost:9060 -SELECT citus.mitmproxy('conn.onQuery(query="^ANALYZE").kill()'); - mitmproxy ------------ - -(1 row) - -ANALYZE vacuum_test; -ERROR: server closed the connection unexpectedly - This probably means the server terminated abnormally - before or while processing the request. -CONTEXT: while executing command on localhost:9060 -SELECT citus.mitmproxy('conn.onQuery(query="^COMMIT").kill()'); - mitmproxy ------------ - -(1 row) - -ANALYZE vacuum_test; -WARNING: connection not open -CONTEXT: while executing command on localhost:9060 -WARNING: failed to commit transaction on localhost:9060 -WARNING: connection not open -CONTEXT: while executing command on localhost:9060 --- ANALYZE transactions being critical is an open question, see #2430 -UPDATE pg_dist_shard_placement SET shardstate = 1 -WHERE shardid IN ( - SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'vacuum_test'::regclass -); --- the same tests with cancel -SELECT citus.mitmproxy('conn.onQuery(query="^VACUUM").cancel(' || pg_backend_pid() || ')'); - mitmproxy ------------ - -(1 row) - -VACUUM vacuum_test; -ERROR: canceling statement due to user request -SELECT citus.mitmproxy('conn.onQuery(query="^ANALYZE").cancel(' || pg_backend_pid() || ')'); - mitmproxy ------------ - -(1 row) - -ANALYZE vacuum_test; -ERROR: canceling statement due to user request --- cancel during COMMIT should be ignored -SELECT citus.mitmproxy('conn.onQuery(query="^COMMIT").cancel(' || pg_backend_pid() || ')'); - mitmproxy ------------ - -(1 row) - -ANALYZE vacuum_test; -SELECT citus.mitmproxy('conn.allow()'); - mitmproxy ------------ - -(1 row) - -CREATE TABLE other_vacuum_test (key int, value int); -SELECT create_distributed_table('other_vacuum_test', 'key'); - create_distributed_table --------------------------- - -(1 row) - -SELECT citus.mitmproxy('conn.onQuery(query="^VACUUM.*other").kill()'); - mitmproxy ------------ - -(1 row) - -VACUUM vacuum_test, other_vacuum_test; -ERROR: syntax error at or near "," -LINE 1: VACUUM vacuum_test, other_vacuum_test; - ^ -SELECT citus.mitmproxy('conn.onQuery(query="^VACUUM.*other").cancel(' || pg_backend_pid() || ')'); - mitmproxy ------------ - -(1 row) - -VACUUM vacuum_test, other_vacuum_test; -ERROR: syntax error at or near "," -LINE 1: VACUUM vacuum_test, other_vacuum_test; - ^ --- ==== Clean up, we're done here ==== -SELECT citus.mitmproxy('conn.allow()'); - mitmproxy ------------ - -(1 row) - -DROP TABLE vacuum_test, other_vacuum_test; diff --git a/src/test/regress/expected/failure_vacuum_8.out b/src/test/regress/expected/failure_vacuum_8.out index a1123085b..52cb95d32 100644 --- a/src/test/regress/expected/failure_vacuum_8.out +++ b/src/test/regress/expected/failure_vacuum_8.out @@ -1,14 +1,6 @@ -- We have different output files for the executor. This is because -- we don't mark transactions with ANALYZE as critical anymore, and -- get WARNINGs instead of ERRORs. --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - f -(1 row) - SET citus.next_shard_id TO 12000000; SELECT citus.mitmproxy('conn.allow()'); mitmproxy diff --git a/src/test/regress/expected/failure_vacuum_9.out b/src/test/regress/expected/failure_vacuum_9.out index 9e6813e94..430d21b6d 100644 --- a/src/test/regress/expected/failure_vacuum_9.out +++ b/src/test/regress/expected/failure_vacuum_9.out @@ -1,14 +1,6 @@ -- We have different output files for the executor. This is because -- we don't mark transactions with ANALYZE as critical anymore, and -- get WARNINGs instead of ERRORs. --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - SET citus.next_shard_id TO 12000000; SELECT citus.mitmproxy('conn.allow()'); mitmproxy diff --git a/src/test/regress/expected/foreign_key_restriction_enforcement.out b/src/test/regress/expected/foreign_key_restriction_enforcement.out index 3d0309031..5efcbd9dd 100644 --- a/src/test/regress/expected/foreign_key_restriction_enforcement.out +++ b/src/test/regress/expected/foreign_key_restriction_enforcement.out @@ -1,15 +1,8 @@ --- +-- -- Tests multiple commands in transactions where -- there is foreign key relation between reference -- tables and distributed tables -- -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - CREATE SCHEMA test_fkey_to_ref_in_tx; SET search_path TO 'test_fkey_to_ref_in_tx'; SET citus.next_shard_id TO 2380000; @@ -81,7 +74,7 @@ BEGIN; ROLLBACK; -- case 1.2: SELECT to a reference table is followed by a multiple router SELECTs to a distributed table -BEGIN; +BEGIN; SELECT count(*) FROM reference_table; count ------- @@ -113,7 +106,7 @@ BEGIN; (1 row) ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM transitive_reference_table; count ------- @@ -190,7 +183,7 @@ BEGIN; UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 18; ROLLBACK; -- case 1.5: SELECT to a reference table is followed by a DDL that touches fkey column -BEGIN; +BEGIN; SELECT count(*) FROM reference_table; count ------- @@ -202,7 +195,7 @@ DEBUG: rewriting table "on_update_fkey_table" DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" serially DEBUG: validating foreign key constraint "fkey" ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM transitive_reference_table; count ------- @@ -215,7 +208,7 @@ DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_tabl DEBUG: validating foreign key constraint "fkey" ROLLBACK; -- case 1.6: SELECT to a reference table is followed by an unrelated DDL -BEGIN; +BEGIN; SELECT count(*) FROM reference_table; count ------- @@ -226,7 +219,7 @@ BEGIN; DEBUG: switching to sequential query execution mode DETAIL: cannot execute parallel DDL on relation "on_update_fkey_table" after SELECT command on reference relation "reference_table" because there is a foreign key between them and "reference_table" has been accessed in this transaction ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM transitive_reference_table; count ------- @@ -237,7 +230,7 @@ BEGIN; DEBUG: switching to sequential query execution mode DETAIL: cannot execute parallel DDL on relation "on_update_fkey_table" after SELECT command on reference relation "transitive_reference_table" because there is a foreign key between them and "transitive_reference_table" has been accessed in this transaction ROLLBACK; --- case 1.7.1: SELECT to a reference table is followed by a DDL that is on +-- case 1.7.1: SELECT to a reference table is followed by a DDL that is on -- the foreign key column BEGIN; SELECT count(*) FROM reference_table; @@ -261,9 +254,9 @@ BEGIN; SET LOCAL client_min_messages TO ERROR; ALTER TABLE on_update_fkey_table DROP COLUMN value_1 CASCADE; ROLLBACK; --- case 1.7.2: SELECT to a reference table is followed by a DDL that is on +-- case 1.7.2: SELECT to a reference table is followed by a DDL that is on -- the foreign key column after a parallel query has been executed -BEGIN; +BEGIN; SELECT count(*) FROM unrelated_dist_table; count ------- @@ -281,7 +274,7 @@ ERROR: cannot modify table "on_update_fkey_table" because there was a parallel DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM unrelated_dist_table; count ------- @@ -299,9 +292,9 @@ ERROR: cannot modify table "on_update_fkey_table" because there was a parallel DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" ROLLBACK; --- case 1.7.3: SELECT to a reference table is followed by a DDL that is not on +-- case 1.7.3: SELECT to a reference table is followed by a DDL that is not on -- the foreign key column, and a parallel query has already been executed -BEGIN; +BEGIN; SELECT count(*) FROM unrelated_dist_table; count ------- @@ -319,7 +312,7 @@ ERROR: cannot execute parallel DDL on relation "on_update_fkey_table" after SEL DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM unrelated_dist_table; count ------- @@ -1026,7 +1019,7 @@ ROLLBACK; -- an unrelated update followed by update on the reference table and update -- on the cascading distributed table -- note that the UPDATE on the reference table will try to set the execution --- mode to sequential, which will fail since there is an already opened +-- mode to sequential, which will fail since there is an already opened -- parallel connections BEGIN; UPDATE unrelated_dist_table SET value_1 = 15; @@ -1060,7 +1053,7 @@ ROLLBACK; -- already executed a parallel query BEGIN; CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); + SELECT create_reference_table('test_table_1'); create_reference_table ------------------------ @@ -1089,7 +1082,7 @@ ROLLBACK; BEGIN; SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); + SELECT create_reference_table('test_table_1'); create_reference_table ------------------------ @@ -1117,7 +1110,6 @@ ROLLBACK; -- parallel connection via create_distributed_table(), later -- adding foreign key to reference table fails BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); SELECT create_reference_table('test_table_1'); create_reference_table @@ -1144,7 +1136,6 @@ ERROR: current transaction is aborted, commands ignored until end of transactio COMMIT; -- same test with the above on sequential mode should work fine BEGIN; - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; CREATE TABLE test_table_1(id int PRIMARY KEY); SELECT create_reference_table('test_table_1'); @@ -1165,7 +1156,7 @@ BEGIN; SET LOCAL client_min_messages TO ERROR; DROP TABLE test_table_1, test_table_2; COMMIT; --- similar test with the above, but this time the order of +-- similar test with the above, but this time the order of -- create_distributed_table and create_reference_table is -- changed BEGIN; @@ -1246,7 +1237,6 @@ ROLLBACK; -- make sure that we cannot create hash distributed tables with -- foreign keys to reference tables when they have data in it BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); @@ -1270,7 +1260,6 @@ COMMIT; -- the same test with above in sequential mode would still not work -- since COPY cannot be executed in sequential mode BEGIN; - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; CREATE TABLE test_table_1(id int PRIMARY KEY); INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; @@ -1286,17 +1275,15 @@ NOTICE: Copying data from local table... SELECT create_distributed_table('test_table_2', 'id'); ERROR: cannot distribute "test_table_2" in sequential mode because it is not empty HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. - -- make sure that the output isn't too verbose SET LOCAL client_min_messages TO ERROR; ERROR: current transaction is aborted, commands ignored until end of transaction block DROP TABLE test_table_2, test_table_1; ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; +COMMIT; -- we should be able to execute and DML/DDL/SELECT after we've -- switched to sequential via create_distributed_table BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); SELECT create_reference_table('test_table_1'); @@ -1354,11 +1341,11 @@ SELECT create_distributed_table('distributed_table', 'id'); (1 row) -ALTER TABLE - distributed_table -ADD CONSTRAINT - fkey_delete FOREIGN KEY(value_1) -REFERENCES +ALTER TABLE + distributed_table +ADD CONSTRAINT + fkey_delete FOREIGN KEY(value_1) +REFERENCES reference_table(id) ON DELETE CASCADE; INSERT INTO reference_table SELECT i FROM generate_series(0, 10) i; DEBUG: distributed INSERT ... SELECT can only select from distributed tables @@ -1370,7 +1357,7 @@ DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator -- this query returns 100 rows in Postgres, but not in Citus -- see https://github.com/citusdata/citus_docs/issues/664 for the discussion -WITH t1 AS (DELETE FROM reference_table RETURNING id) +WITH t1 AS (DELETE FROM reference_table RETURNING id) DELETE FROM distributed_table USING t1 WHERE value_1 = t1.id RETURNING *; DEBUG: generating subplan 170_1 for CTE t1: DELETE FROM test_fkey_to_ref_in_tx.reference_table RETURNING id DEBUG: Plan 170 query after replacing subqueries and CTEs: DELETE FROM test_fkey_to_ref_in_tx.distributed_table USING (SELECT intermediate_result.id FROM read_intermediate_result('170_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) t1 WHERE (distributed_table.value_1 OPERATOR(pg_catalog.=) t1.id) RETURNING distributed_table.id, distributed_table.value_1, t1.id @@ -1391,7 +1378,7 @@ DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator -- this query returns 100 rows in Postgres, but not in Citus -- see https://github.com/citusdata/citus_docs/issues/664 for the discussion -WITH t1 AS (DELETE FROM reference_table RETURNING id) +WITH t1 AS (DELETE FROM reference_table RETURNING id) SELECT count(*) FROM distributed_table, t1 WHERE value_1 = t1.id; DEBUG: generating subplan 174_1 for CTE t1: DELETE FROM test_fkey_to_ref_in_tx.reference_table RETURNING id DEBUG: Plan 174 query after replacing subqueries and CTEs: SELECT count(*) AS count FROM test_fkey_to_ref_in_tx.distributed_table, (SELECT intermediate_result.id FROM read_intermediate_result('174_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) t1 WHERE (distributed_table.value_1 OPERATOR(pg_catalog.=) t1.id) @@ -1402,7 +1389,7 @@ DETAIL: Reference relation "reference_table" is modified, which might lead to d 0 (1 row) --- this query should fail since we first to a parallel access to a distributed table +-- this query should fail since we first to a parallel access to a distributed table -- with t1, and then access to t2 WITH t1 AS (DELETE FROM distributed_table RETURNING id), t2 AS (DELETE FROM reference_table RETURNING id) @@ -1423,7 +1410,6 @@ HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_m -- finally, make sure that we can execute the same queries -- in the sequential mode BEGIN; - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; WITH t1 AS (DELETE FROM distributed_table RETURNING id), t2 AS (DELETE FROM reference_table RETURNING id) @@ -1438,7 +1424,6 @@ DEBUG: Plan 181 query after replacing subqueries and CTEs: SELECT count(*) AS c ROLLBACK; BEGIN; - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; WITH t1 AS (DELETE FROM distributed_table RETURNING id) DELETE FROM reference_table RETURNING id; diff --git a/src/test/regress/expected/foreign_key_restriction_enforcement_0.out b/src/test/regress/expected/foreign_key_restriction_enforcement_0.out deleted file mode 100644 index fde87b2c2..000000000 --- a/src/test/regress/expected/foreign_key_restriction_enforcement_0.out +++ /dev/null @@ -1,1457 +0,0 @@ --- --- Tests multiple commands in transactions where --- there is foreign key relation between reference --- tables and distributed tables --- -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - f -(1 row) - -CREATE SCHEMA test_fkey_to_ref_in_tx; -SET search_path TO 'test_fkey_to_ref_in_tx'; -SET citus.next_shard_id TO 2380000; -SET citus.next_placement_id TO 2380000; -SET citus.shard_replication_factor TO 1; -CREATE TABLE transitive_reference_table(id int PRIMARY KEY); -SELECT create_reference_table('transitive_reference_table'); - create_reference_table ------------------------- - -(1 row) - -CREATE TABLE reference_table(id int PRIMARY KEY, value_1 int); -SELECT create_reference_table('reference_table'); - create_reference_table ------------------------- - -(1 row) - -CREATE TABLE on_update_fkey_table(id int PRIMARY KEY, value_1 int); -SELECT create_distributed_table('on_update_fkey_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -CREATE TABLE unrelated_dist_table(id int PRIMARY KEY, value_1 int); -SELECT create_distributed_table('unrelated_dist_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE on_update_fkey_table ADD CONSTRAINT fkey FOREIGN KEY(value_1) REFERENCES reference_table(id) ON UPDATE CASCADE; -ALTER TABLE reference_table ADD CONSTRAINT fkey FOREIGN KEY(value_1) REFERENCES transitive_reference_table(id) ON UPDATE CASCADE; -INSERT INTO transitive_reference_table SELECT i FROM generate_series(0, 100) i; -INSERT INTO reference_table SELECT i, i FROM generate_series(0, 100) i; -INSERT INTO on_update_fkey_table SELECT i, i % 100 FROM generate_series(0, 1000) i; -INSERT INTO unrelated_dist_table SELECT i, i % 100 FROM generate_series(0, 1000) i; --- in order to see when the mode automatically swithces to sequential execution -SET client_min_messages TO DEBUG1; --- case 1.1: SELECT to a reference table is followed by a parallel SELECT to a distributed table -BEGIN; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - SELECT count(*) FROM on_update_fkey_table; - count -------- - 1001 -(1 row) - -ROLLBACK; -BEGIN; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - SELECT count(*) FROM on_update_fkey_table; - count -------- - 1001 -(1 row) - -ROLLBACK; --- case 1.2: SELECT to a reference table is followed by a multiple router SELECTs to a distributed table -BEGIN; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE id = 15; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE id = 16; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE id = 17; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE id = 18; - count -------- - 1 -(1 row) - -ROLLBACK; -BEGIN; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE id = 15; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE id = 16; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE id = 17; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE id = 18; - count -------- - 1 -(1 row) - -ROLLBACK; --- case 1.3: SELECT to a reference table is followed by a multi-shard UPDATE to a distributed table -BEGIN; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; -ROLLBACK; -BEGIN; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; -ROLLBACK; --- case 1.4: SELECT to a reference table is followed by a multiple sing-shard UPDATE to a distributed table -BEGIN; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 15; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 16; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 17; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 18; -ROLLBACK; -BEGIN; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 15; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 16; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 17; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE id = 18; -ROLLBACK; --- case 1.5: SELECT to a reference table is followed by a DDL that touches fkey column -BEGIN; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE bigint; -DEBUG: rewriting table "on_update_fkey_table" -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -DEBUG: validating foreign key constraint "fkey" -ROLLBACK; -BEGIN; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE bigint; -DEBUG: rewriting table "on_update_fkey_table" -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -DEBUG: validating foreign key constraint "fkey" -ROLLBACK; --- case 1.6: SELECT to a reference table is followed by an unrelated DDL -BEGIN; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - ALTER TABLE on_update_fkey_table ADD COLUMN X INT; -DEBUG: switching to sequential query execution mode -DETAIL: cannot execute parallel DDL on relation "on_update_fkey_table" after SELECT command on reference relation "reference_table" because there is a foreign key between them and "reference_table" has been accessed in this transaction -ROLLBACK; -BEGIN; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - ALTER TABLE on_update_fkey_table ADD COLUMN X INT; -DEBUG: switching to sequential query execution mode -DETAIL: cannot execute parallel DDL on relation "on_update_fkey_table" after SELECT command on reference relation "transitive_reference_table" because there is a foreign key between them and "transitive_reference_table" has been accessed in this transaction -ROLLBACK; --- case 1.7.1: SELECT to a reference table is followed by a DDL that is on --- the foreign key column -BEGIN; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; - ALTER TABLE on_update_fkey_table DROP COLUMN value_1 CASCADE; -ROLLBACK; -BEGIN; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; - ALTER TABLE on_update_fkey_table DROP COLUMN value_1 CASCADE; -ROLLBACK; --- case 1.7.2: SELECT to a reference table is followed by a DDL that is on --- the foreign key column after a parallel query has been executed -BEGIN; - SELECT count(*) FROM unrelated_dist_table; - count -------- - 1001 -(1 row) - - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - ALTER TABLE on_update_fkey_table DROP COLUMN value_1 CASCADE; -ERROR: cannot modify table "on_update_fkey_table" because there was a parallel operation on a distributed table in the transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - SELECT count(*) FROM unrelated_dist_table; - count -------- - 1001 -(1 row) - - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - ALTER TABLE on_update_fkey_table DROP COLUMN value_1 CASCADE; -ERROR: cannot modify table "on_update_fkey_table" because there was a parallel operation on a distributed table in the transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 1.7.3: SELECT to a reference table is followed by a DDL that is not on --- the foreign key column, and a parallel query has already been executed -BEGIN; - SELECT count(*) FROM unrelated_dist_table; - count -------- - 1001 -(1 row) - - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - ALTER TABLE on_update_fkey_table ADD COLUMN X INT; -ERROR: cannot execute parallel DDL on relation "on_update_fkey_table" after SELECT command on reference relation "reference_table" because there is a foreign key between them and "reference_table" has been accessed in this transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - SELECT count(*) FROM unrelated_dist_table; - count -------- - 1001 -(1 row) - - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - ALTER TABLE on_update_fkey_table ADD COLUMN X INT; -ERROR: cannot execute parallel DDL on relation "on_update_fkey_table" after SELECT command on reference relation "transitive_reference_table" because there is a foreign key between them and "transitive_reference_table" has been accessed in this transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 1.8: SELECT to a reference table is followed by a COPY -BEGIN; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - - COPY on_update_fkey_table FROM STDIN WITH CSV; -ROLLBACK; -BEGIN; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - - COPY on_update_fkey_table FROM STDIN WITH CSV; -ROLLBACK; --- case 2.1: UPDATE to a reference table is followed by a multi-shard SELECT -BEGIN; - UPDATE reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 0 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101; - count -------- - 10 -(1 row) - -ROLLBACK; -BEGIN; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101; - count -------- - 0 -(1 row) - -ROLLBACK; --- case 2.2: UPDATE to a reference table is followed by multiple router SELECT -BEGIN; - UPDATE reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101 AND id = 99; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101 AND id = 199; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101 AND id = 299; - count -------- - 1 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101 AND id = 399; - count -------- - 1 -(1 row) - -ROLLBACK; -BEGIN; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101 AND id = 99; - count -------- - 0 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101 AND id = 199; - count -------- - 0 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101 AND id = 299; - count -------- - 0 -(1 row) - - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 101 AND id = 399; - count -------- - 0 -(1 row) - -ROLLBACK; --- case 2.3: UPDATE to a reference table is followed by a multi-shard UPDATE -BEGIN; - UPDATE reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - UPDATE on_update_fkey_table SET value_1 = 15; -ROLLBACK; -BEGIN; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - UPDATE on_update_fkey_table SET value_1 = 15; -ROLLBACK; --- case 2.4: UPDATE to a reference table is followed by multiple router UPDATEs -BEGIN; - UPDATE reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - UPDATE on_update_fkey_table SET value_1 = 101 WHERE id = 1; - UPDATE on_update_fkey_table SET value_1 = 101 WHERE id = 2; - UPDATE on_update_fkey_table SET value_1 = 101 WHERE id = 3; - UPDATE on_update_fkey_table SET value_1 = 101 WHERE id = 4; -ROLLBACK; -BEGIN; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - UPDATE on_update_fkey_table SET value_1 = 101 WHERE id = 1; -ERROR: insert or update on table "on_update_fkey_table_2380002" violates foreign key constraint "fkey_2380002" -DETAIL: Key (value_1)=(101) is not present in table "reference_table_2380001". -CONTEXT: while executing command on localhost:57637 - UPDATE on_update_fkey_table SET value_1 = 101 WHERE id = 2; -ERROR: current transaction is aborted, commands ignored until end of transaction block - UPDATE on_update_fkey_table SET value_1 = 101 WHERE id = 3; -ERROR: current transaction is aborted, commands ignored until end of transaction block - UPDATE on_update_fkey_table SET value_1 = 101 WHERE id = 4; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- case 2.5: UPDATE to a reference table is followed by a DDL that touches fkey column -BEGIN; - UPDATE reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE bigint; -DEBUG: rewriting table "on_update_fkey_table" -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -DEBUG: validating foreign key constraint "fkey" -ROLLBACK; -BEGIN; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE bigint; -DEBUG: rewriting table "on_update_fkey_table" -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -DEBUG: validating foreign key constraint "fkey" -ROLLBACK; --- case 2.6: UPDATE to a reference table is followed by an unrelated DDL -BEGIN; - UPDATE reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - ALTER TABLE on_update_fkey_table ADD COLUMN value_1_X INT; -ROLLBACK; -BEGIN; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - ALTER TABLE on_update_fkey_table ADD COLUMN value_1_X INT; -ROLLBACK; --- case 2.7: UPDATE to a reference table is followed by COPY -BEGIN; - UPDATE reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - COPY on_update_fkey_table FROM STDIN WITH CSV; -ROLLBACK; -BEGIN; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - COPY on_update_fkey_table FROM STDIN WITH CSV; -ERROR: insert or update on table "on_update_fkey_table_2380005" violates foreign key constraint "fkey_2380005" -DETAIL: Key (value_1)=(101) is not present in table "reference_table_2380001". -ROLLBACK; --- case 2.8: UPDATE to a reference table is followed by TRUNCATE -BEGIN; - UPDATE reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - TRUNCATE on_update_fkey_table; -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -ROLLBACK; -BEGIN; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - TRUNCATE on_update_fkey_table; -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -ROLLBACK; --- case 3.1: an unrelated DDL to a reference table is followed by a real-time SELECT -BEGIN; - ALTER TABLE reference_table ALTER COLUMN id SET DEFAULT 1001; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - SELECT count(*) FROM on_update_fkey_table; - count -------- - 1001 -(1 row) - -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DEFAULT 1001; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - SELECT count(*) FROM on_update_fkey_table; - count -------- - 1001 -(1 row) - -ROLLBACK; --- case 3.2: DDL that touches fkey column to a reference table is followed by a real-time SELECT -BEGIN; - ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE int; - SELECT count(*) FROM on_update_fkey_table; - count -------- - 1001 -(1 row) - -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE int; - SELECT count(*) FROM on_update_fkey_table; - count -------- - 1001 -(1 row) - -ROLLBACK; --- case 3.3: DDL to a reference table followed by a multi shard UPDATE -BEGIN; - ALTER TABLE reference_table ALTER COLUMN id SET DEFAULT 1001; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - UPDATE on_update_fkey_table SET value_1 = 5 WHERE id != 11; -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DEFAULT 1001; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - UPDATE on_update_fkey_table SET value_1 = 5 WHERE id != 11; -ROLLBACK; --- case 3.4: DDL to a reference table followed by multiple router UPDATEs -BEGIN; - ALTER TABLE reference_table ALTER COLUMN id SET DEFAULT 1001; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - UPDATE on_update_fkey_table SET value_1 = 98 WHERE id = 1; - UPDATE on_update_fkey_table SET value_1 = 98 WHERE id = 2; - UPDATE on_update_fkey_table SET value_1 = 98 WHERE id = 3; - UPDATE on_update_fkey_table SET value_1 = 98 WHERE id = 4; -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DEFAULT 1001; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - UPDATE on_update_fkey_table SET value_1 = 98 WHERE id = 1; - UPDATE on_update_fkey_table SET value_1 = 98 WHERE id = 2; - UPDATE on_update_fkey_table SET value_1 = 98 WHERE id = 3; - UPDATE on_update_fkey_table SET value_1 = 98 WHERE id = 4; -ROLLBACK; --- case 3.5: DDL to reference table followed by a DDL to dist table -BEGIN; - ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE smallint; -DEBUG: rewriting table "reference_table" -DEBUG: building index "reference_table_pkey" on table "reference_table" -DEBUG: validating foreign key constraint "fkey" - CREATE INDEX fkey_test_index_1 ON on_update_fkey_table(value_1); -DEBUG: building index "fkey_test_index_1" on table "on_update_fkey_table" -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE smallint; -DEBUG: rewriting table "transitive_reference_table" -DEBUG: building index "transitive_reference_table_pkey" on table "transitive_reference_table" -DEBUG: validating foreign key constraint "fkey" - CREATE INDEX fkey_test_index_1 ON on_update_fkey_table(value_1); -DEBUG: building index "fkey_test_index_1" on table "on_update_fkey_table" -ROLLBACK; --- case 4.6: DDL to reference table followed by a DDL to dist table, both touching fkey columns -BEGIN; - ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE smallint; -DEBUG: rewriting table "reference_table" -DEBUG: building index "reference_table_pkey" on table "reference_table" -DEBUG: validating foreign key constraint "fkey" - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE smallint; -DEBUG: rewriting table "on_update_fkey_table" -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -DEBUG: validating foreign key constraint "fkey" -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE smallint; -DEBUG: rewriting table "transitive_reference_table" -DEBUG: building index "transitive_reference_table_pkey" on table "transitive_reference_table" -DEBUG: validating foreign key constraint "fkey" - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE smallint; -DEBUG: rewriting table "on_update_fkey_table" -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -DEBUG: validating foreign key constraint "fkey" -ROLLBACK; --- case 3.7: DDL to a reference table is followed by COPY -BEGIN; - ALTER TABLE reference_table ADD COLUMN X int; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - COPY on_update_fkey_table FROM STDIN WITH CSV; -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ADD COLUMN X int; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - COPY on_update_fkey_table FROM STDIN WITH CSV; -ROLLBACK; --- case 3.8: DDL to a reference table is followed by TRUNCATE -BEGIN; - ALTER TABLE reference_table ADD COLUMN X int; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - TRUNCATE on_update_fkey_table; -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ADD COLUMN X int; -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "transitive_reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - TRUNCATE on_update_fkey_table; -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -ROLLBACK; --- case 3.9: DDL to a reference table is followed by TRUNCATE -BEGIN; - ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE smallint; -DEBUG: rewriting table "reference_table" -DEBUG: building index "reference_table_pkey" on table "reference_table" -DEBUG: validating foreign key constraint "fkey" - TRUNCATE on_update_fkey_table; -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -ROLLBACK; -BEGIN; - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE smallint; -DEBUG: rewriting table "transitive_reference_table" -DEBUG: building index "transitive_reference_table_pkey" on table "transitive_reference_table" -DEBUG: validating foreign key constraint "fkey" - TRUNCATE on_update_fkey_table; -DEBUG: building index "on_update_fkey_table_pkey" on table "on_update_fkey_table" -ROLLBACK; ------ ---- Now, start testing the other way araound ------ --- case 4.1: SELECT to a dist table is follwed by a SELECT to a reference table -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - -ROLLBACK; -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - -ROLLBACK; --- case 4.2: SELECT to a dist table is follwed by a DML to a reference table -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - UPDATE reference_table SET id = 101 WHERE id = 99; -ERROR: cannot modify reference table "reference_table" because there was a parallel operation on a distributed table -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -ERROR: cannot modify reference table "transitive_reference_table" because there was a parallel operation on a distributed table -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 4.3: SELECT to a dist table is follwed by an unrelated DDL to a reference table -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - ALTER TABLE reference_table ADD COLUMN X INT; -ERROR: cannot execute DDL on reference relation "reference_table" because there was a parallel SELECT access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - ALTER TABLE transitive_reference_table ADD COLUMN X INT; -ERROR: cannot execute DDL on reference relation "transitive_reference_table" because there was a parallel SELECT access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 4.4: SELECT to a dist table is follwed by a DDL to a reference table -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE smallint; -DEBUG: rewriting table "reference_table" -DEBUG: building index "reference_table_pkey" on table "reference_table" -DEBUG: validating foreign key constraint "fkey" -ERROR: cannot execute DDL on reference relation "reference_table" because there was a parallel SELECT access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE smallint; -DEBUG: rewriting table "transitive_reference_table" -DEBUG: building index "transitive_reference_table_pkey" on table "transitive_reference_table" -DEBUG: validating foreign key constraint "fkey" -ERROR: cannot execute DDL on reference relation "transitive_reference_table" because there was a parallel SELECT access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 4.5: SELECT to a dist table is follwed by a TRUNCATE -\set VERBOSITY terse -SET client_min_messages to LOG; -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - TRUNCATE reference_table CASCADE; -NOTICE: truncate cascades to table "on_update_fkey_table" -ERROR: cannot execute DDL on reference relation "reference_table" because there was a parallel SELECT access to distributed relation "on_update_fkey_table" in the same transaction -ROLLBACK; -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE value_1 = 99; - count -------- - 10 -(1 row) - - TRUNCATE transitive_reference_table CASCADE; -NOTICE: truncate cascades to table "reference_table" -NOTICE: truncate cascades to table "on_update_fkey_table" -ERROR: cannot execute DDL on reference relation "transitive_reference_table" because there was a parallel SELECT access to distributed relation "on_update_fkey_table" in the same transaction -ROLLBACK; --- case 4.6: Router SELECT to a dist table is followed by a TRUNCATE -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE id = 9; - count -------- - 1 -(1 row) - - TRUNCATE reference_table CASCADE; -NOTICE: truncate cascades to table "on_update_fkey_table" -ROLLBACK; -BEGIN; - SELECT count(*) FROM on_update_fkey_table WHERE id = 9; - count -------- - 1 -(1 row) - - TRUNCATE transitive_reference_table CASCADE; -NOTICE: truncate cascades to table "reference_table" -NOTICE: truncate cascades to table "on_update_fkey_table" -ROLLBACK; -RESET client_min_messages; -\set VERBOSITY default --- case 5.1: Parallel UPDATE on distributed table follow by a SELECT -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; - SELECT count(*) FROM reference_table; - count -------- - 101 -(1 row) - -ROLLBACK; -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; - SELECT count(*) FROM transitive_reference_table; - count -------- - 101 -(1 row) - -ROLLBACK; --- case 5.2: Parallel UPDATE on distributed table follow by a UPDATE -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; - UPDATE reference_table SET id = 160 WHERE id = 15; -ERROR: cannot execute DML on reference relation "reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; - UPDATE transitive_reference_table SET id = 160 WHERE id = 15; -ERROR: cannot execute DML on reference relation "transitive_reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 5.3: Parallel UPDATE on distributed table follow by an unrelated DDL on reference table -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; - ALTER TABLE reference_table ADD COLUMN X INT; -ERROR: cannot execute DDL on reference relation "reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; - ALTER TABLE transitive_reference_table ADD COLUMN X INT; -ERROR: cannot execute DDL on reference relation "transitive_reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 5.4: Parallel UPDATE on distributed table follow by a related DDL on reference table -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; - ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE smallint; -ERROR: cannot execute DDL on reference relation "reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 16 WHERE value_1 = 15; - ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE smallint; -ERROR: cannot execute DDL on reference relation "transitive_reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 6:1: Unrelated parallel DDL on distributed table followed by SELECT on ref. table -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - SELECT count(*) FROM reference_table; -ERROR: cannot execute SELECT on reference relation "reference_table" because there was a parallel DDL access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - SELECT count(*) FROM transitive_reference_table; -ERROR: cannot execute SELECT on reference relation "transitive_reference_table" because there was a parallel DDL access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 6:2: Related parallel DDL on distributed table followed by SELECT on ref. table -BEGIN; - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE smallint; - UPDATE reference_table SET id = 160 WHERE id = 15; -ROLLBACK; -BEGIN; - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE smallint; - UPDATE transitive_reference_table SET id = 160 WHERE id = 15; -ROLLBACK; --- case 6:3: Unrelated parallel DDL on distributed table followed by UPDATE on ref. table -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - SELECT count(*) FROM reference_table; -ERROR: cannot execute SELECT on reference relation "reference_table" because there was a parallel DDL access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - SELECT count(*) FROM transitive_reference_table; -ERROR: cannot execute SELECT on reference relation "transitive_reference_table" because there was a parallel DDL access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 6:4: Related parallel DDL on distributed table followed by SELECT on ref. table -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - UPDATE reference_table SET id = 160 WHERE id = 15; -ERROR: cannot execute DML on reference relation "reference_table" because there was a parallel DDL access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - UPDATE transitive_reference_table SET id = 160 WHERE id = 15; -ERROR: cannot execute DML on reference relation "transitive_reference_table" because there was a parallel DDL access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 6:5: Unrelated parallel DDL on distributed table followed by unrelated DDL on ref. table -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - ALTER TABLE reference_table ADD COLUMN X int; -ERROR: cannot execute DDL on reference relation "reference_table" because there was a parallel DDL access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - ALTER TABLE transitive_reference_table ADD COLUMN X int; -ERROR: cannot execute DDL on reference relation "transitive_reference_table" because there was a parallel DDL access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- case 6:6: Unrelated parallel DDL on distributed table followed by related DDL on ref. table -BEGIN; - ALTER TABLE on_update_fkey_table ADD COLUMN X int; - ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE smallint; -ERROR: cannot modify table "on_update_fkey_table" because there was a parallel operation on a distributed table in the transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- some more extensive tests --- UPDATE on dist table is followed by DELETE to reference table -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 5 WHERE id != 11; - DELETE FROM reference_table WHERE id = 99; -ERROR: cannot execute DML on reference relation "reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - UPDATE on_update_fkey_table SET value_1 = 5 WHERE id != 11; - DELETE FROM transitive_reference_table WHERE id = 99; -ERROR: cannot execute DML on reference relation "transitive_reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- an unrelated update followed by update on dist table and update --- on reference table -BEGIN; - UPDATE unrelated_dist_table SET value_1 = 15; - UPDATE on_update_fkey_table SET value_1 = 5 WHERE id != 11; - UPDATE reference_table SET id = 101 WHERE id = 99; -ERROR: cannot execute DML on reference relation "reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; -BEGIN; - UPDATE unrelated_dist_table SET value_1 = 15; - UPDATE on_update_fkey_table SET value_1 = 5 WHERE id != 11; - UPDATE transitive_reference_table SET id = 101 WHERE id = 99; -ERROR: cannot execute DML on reference relation "transitive_reference_table" because there was a parallel DML access to distributed relation "on_update_fkey_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" -ROLLBACK; --- an unrelated update followed by update on the reference table and update --- on the cascading distributed table --- note that the UPDATE on the reference table will try to set the execution --- mode to sequential, which will fail since there is an already opened --- parallel connections -BEGIN; - UPDATE unrelated_dist_table SET value_1 = 15; - UPDATE reference_table SET id = 101 WHERE id = 99; -ERROR: cannot modify reference table "reference_table" because there was a parallel operation on a distributed table -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" - UPDATE on_update_fkey_table SET value_1 = 5 WHERE id != 11; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; -BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; - DROP TABLE test_table_1 CASCADE; -ROLLBACK; --- the fails since we're trying to switch sequential mode after --- already executed a parallel query -BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - CREATE TABLE tt4(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES tt4(id)); - SELECT create_distributed_table('tt4', 'id'); - create_distributed_table --------------------------- - -(1 row) - - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id), FOREIGN KEY(id) REFERENCES tt4(id)); - SELECT create_distributed_table('test_table_2', 'id'); -ERROR: cannot distribute relation "test_table_2" in this transaction because it has a foreign key to a reference table -DETAIL: If a hash distributed table has a foreign key to a reference table, it has to be created in sequential mode before any parallel commands have been executed in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; -ERROR: current transaction is aborted, commands ignored until end of transaction block - DROP TABLE test_table_1 CASCADE; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- same test with the above, but this time using --- sequential mode, succeeds -BEGIN; - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - CREATE TABLE tt4(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES tt4(id)); - SELECT create_distributed_table('tt4', 'id'); - create_distributed_table --------------------------- - -(1 row) - - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id), FOREIGN KEY(id) REFERENCES tt4(id)); - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; - DROP TABLE test_table_1 CASCADE; -ROLLBACK; --- another test with ALTER TABLE fails since we're already opened --- parallel connection via create_distributed_table(), later --- adding foreign key to reference table fails -BEGIN; - - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE test_table_2 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_1(id); -ERROR: cannot modify table "test_table_2" because there was a parallel operation on a distributed table in the transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; -ERROR: current transaction is aborted, commands ignored until end of transaction block - DROP TABLE test_table_1, test_table_2; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; --- same test with the above on sequential mode should work fine -BEGIN; - - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE test_table_2 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_1(id); - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; - DROP TABLE test_table_1, test_table_2; -COMMIT; --- similar test with the above, but this time the order of --- create_distributed_table and create_reference_table is --- changed -BEGIN; - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - ALTER TABLE test_table_2 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_1(id); -ERROR: cannot modify table "test_table_2" because there was a parallel operation on a distributed table in the transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; -ERROR: current transaction is aborted, commands ignored until end of transaction block - DROP TABLE test_table_1 CASCADE; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- same test in sequential mode should succeed -BEGIN; - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - ALTER TABLE test_table_2 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_1(id); - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; - DROP TABLE test_table_1 CASCADE; -ROLLBACK; --- again a very similar test, but this time --- a parallel SELECT is already executed before --- setting the mode to sequential should fail -BEGIN; - SELECT count(*) FROM on_update_fkey_table; - count -------- - 1001 -(1 row) - - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); - SELECT create_distributed_table('test_table_2', 'id'); -ERROR: cannot distribute relation "test_table_2" in this transaction because it has a foreign key to a reference table -DETAIL: If a hash distributed table has a foreign key to a reference table, it has to be created in sequential mode before any parallel commands have been executed in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" - CREATE TABLE test_table_1(id int PRIMARY KEY); -ERROR: current transaction is aborted, commands ignored until end of transaction block - SELECT create_reference_table('test_table_1'); -ERROR: current transaction is aborted, commands ignored until end of transaction block - ALTER TABLE test_table_2 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_1(id); -ERROR: current transaction is aborted, commands ignored until end of transaction block - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; -ERROR: current transaction is aborted, commands ignored until end of transaction block - DROP TABLE test_table_1 CASCADE; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- make sure that we cannot create hash distributed tables with --- foreign keys to reference tables when they have data in it -BEGIN; - - CREATE TABLE test_table_1(id int PRIMARY KEY); - INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); - INSERT INTO test_table_2 SELECT i, i FROM generate_series(0,100) i; - SELECT create_reference_table('test_table_1'); -NOTICE: Copying data from local table... - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); -ERROR: cannot distribute "test_table_2" in sequential mode because it is not empty -HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; -ERROR: current transaction is aborted, commands ignored until end of transaction block - DROP TABLE test_table_2, test_table_1; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; --- the same test with above in sequential mode would still not work --- since COPY cannot be executed in sequential mode -BEGIN; - - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - CREATE TABLE test_table_1(id int PRIMARY KEY); - INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); - INSERT INTO test_table_2 SELECT i, i FROM generate_series(0,100) i; - SELECT create_reference_table('test_table_1'); -NOTICE: Copying data from local table... - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); -ERROR: cannot distribute "test_table_2" in sequential mode because it is not empty -HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. - - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; -ERROR: current transaction is aborted, commands ignored until end of transaction block - DROP TABLE test_table_2, test_table_1; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; --- we should be able to execute and DML/DDL/SELECT after we've --- switched to sequential via create_distributed_table -BEGIN; - - CREATE TABLE test_table_1(id int PRIMARY KEY); - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - -- and maybe some other test - CREATE INDEX i1 ON test_table_1(id); - ALTER TABLE test_table_2 ADD CONSTRAINT check_val CHECK (id > 0); - SELECT count(*) FROM test_table_2; - count -------- - 0 -(1 row) - - SELECT count(*) FROM test_table_1; - count -------- - 0 -(1 row) - - UPDATE test_table_2 SET value_1 = 15; - -- make sure that the output isn't too verbose - SET LOCAL client_min_messages TO ERROR; - DROP TABLE test_table_2, test_table_1; -COMMIT; -SET client_min_messages TO ERROR; -DROP TABLE reference_table CASCADE; -SET client_min_messages TO DEBUG1; --- make sure that modifications to reference tables in a CTE can --- set the mode to sequential for the next operations -CREATE TABLE reference_table(id int PRIMARY KEY); -DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "reference_table_pkey" for table "reference_table" -DEBUG: building index "reference_table_pkey" on table "reference_table" -SELECT create_reference_table('reference_table'); - create_reference_table ------------------------- - -(1 row) - -CREATE TABLE distributed_table(id int PRIMARY KEY, value_1 int); -DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "distributed_table_pkey" for table "distributed_table" -DEBUG: building index "distributed_table_pkey" on table "distributed_table" -SELECT create_distributed_table('distributed_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE - distributed_table -ADD CONSTRAINT - fkey_delete FOREIGN KEY(value_1) -REFERENCES - reference_table(id) ON DELETE CASCADE; -INSERT INTO reference_table SELECT i FROM generate_series(0, 10) i; -DEBUG: distributed INSERT ... SELECT can only select from distributed tables -DEBUG: Collecting INSERT ... SELECT results on coordinator -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode -INSERT INTO distributed_table SELECT i, i % 10 FROM generate_series(0, 100) i; -DEBUG: distributed INSERT ... SELECT can only select from distributed tables -DEBUG: Collecting INSERT ... SELECT results on coordinator --- this query returns 100 rows in Postgres, but not in Citus --- see https://github.com/citusdata/citus_docs/issues/664 for the discussion -WITH t1 AS (DELETE FROM reference_table RETURNING id) - DELETE FROM distributed_table USING t1 WHERE value_1 = t1.id RETURNING *; -DEBUG: generating subplan 170_1 for CTE t1: DELETE FROM test_fkey_to_ref_in_tx.reference_table RETURNING id -DEBUG: Plan 170 query after replacing subqueries and CTEs: DELETE FROM test_fkey_to_ref_in_tx.distributed_table USING (SELECT intermediate_result.id FROM read_intermediate_result('170_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) t1 WHERE (distributed_table.value_1 OPERATOR(pg_catalog.=) t1.id) RETURNING distributed_table.id, distributed_table.value_1, t1.id -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - id | value_1 | id -----+---------+---- -(0 rows) - --- load some more data for one more test with real-time selects -INSERT INTO reference_table SELECT i FROM generate_series(0, 10) i; -DEBUG: distributed INSERT ... SELECT can only select from distributed tables -DEBUG: Collecting INSERT ... SELECT results on coordinator -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode -INSERT INTO distributed_table SELECT i, i % 10 FROM generate_series(0, 100) i; -DEBUG: distributed INSERT ... SELECT can only select from distributed tables -DEBUG: Collecting INSERT ... SELECT results on coordinator --- this query returns 100 rows in Postgres, but not in Citus --- see https://github.com/citusdata/citus_docs/issues/664 for the discussion -WITH t1 AS (DELETE FROM reference_table RETURNING id) - SELECT count(*) FROM distributed_table, t1 WHERE value_1 = t1.id; -DEBUG: generating subplan 174_1 for CTE t1: DELETE FROM test_fkey_to_ref_in_tx.reference_table RETURNING id -DEBUG: Plan 174 query after replacing subqueries and CTEs: SELECT count(*) AS count FROM test_fkey_to_ref_in_tx.distributed_table, (SELECT intermediate_result.id FROM read_intermediate_result('174_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) t1 WHERE (distributed_table.value_1 OPERATOR(pg_catalog.=) t1.id) -DEBUG: switching to sequential query execution mode -DETAIL: Reference relation "reference_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed relations due to foreign keys. Any parallel modification to those hash distributed relations in the same transaction can only be executed in sequential query execution mode - count -------- - 0 -(1 row) - --- this query should fail since we first to a parallel access to a distributed table --- with t1, and then access to t2 -WITH t1 AS (DELETE FROM distributed_table RETURNING id), - t2 AS (DELETE FROM reference_table RETURNING id) - SELECT count(*) FROM distributed_table, t1, t2 WHERE value_1 = t1.id AND value_1 = t2.id; -DEBUG: generating subplan 176_1 for CTE t1: DELETE FROM test_fkey_to_ref_in_tx.distributed_table RETURNING id -DEBUG: generating subplan 176_2 for CTE t2: DELETE FROM test_fkey_to_ref_in_tx.reference_table RETURNING id -DEBUG: Plan 176 query after replacing subqueries and CTEs: SELECT count(*) AS count FROM test_fkey_to_ref_in_tx.distributed_table, (SELECT intermediate_result.id FROM read_intermediate_result('176_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) t1, (SELECT intermediate_result.id FROM read_intermediate_result('176_2'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) t2 WHERE ((distributed_table.value_1 OPERATOR(pg_catalog.=) t1.id) AND (distributed_table.value_1 OPERATOR(pg_catalog.=) t2.id)) -ERROR: cannot execute DML on reference relation "reference_table" because there was a parallel DML access to distributed relation "distributed_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" --- similarly this should fail since we first access to a distributed --- table via t1, and then access to the reference table in the main query -WITH t1 AS (DELETE FROM distributed_table RETURNING id) - DELETE FROM reference_table RETURNING id; -DEBUG: generating subplan 179_1 for CTE t1: DELETE FROM test_fkey_to_ref_in_tx.distributed_table RETURNING id -DEBUG: Plan 179 query after replacing subqueries and CTEs: DELETE FROM test_fkey_to_ref_in_tx.reference_table RETURNING id -ERROR: cannot execute DML on reference relation "reference_table" because there was a parallel DML access to distributed relation "distributed_table" in the same transaction -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" --- finally, make sure that we can execute the same queries --- in the sequential mode -BEGIN; - - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - WITH t1 AS (DELETE FROM distributed_table RETURNING id), - t2 AS (DELETE FROM reference_table RETURNING id) - SELECT count(*) FROM distributed_table, t1, t2 WHERE value_1 = t1.id AND value_1 = t2.id; -DEBUG: generating subplan 181_1 for CTE t1: DELETE FROM test_fkey_to_ref_in_tx.distributed_table RETURNING id -DEBUG: generating subplan 181_2 for CTE t2: DELETE FROM test_fkey_to_ref_in_tx.reference_table RETURNING id -DEBUG: Plan 181 query after replacing subqueries and CTEs: SELECT count(*) AS count FROM test_fkey_to_ref_in_tx.distributed_table, (SELECT intermediate_result.id FROM read_intermediate_result('181_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) t1, (SELECT intermediate_result.id FROM read_intermediate_result('181_2'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) t2 WHERE ((distributed_table.value_1 OPERATOR(pg_catalog.=) t1.id) AND (distributed_table.value_1 OPERATOR(pg_catalog.=) t2.id)) - count -------- - 0 -(1 row) - -ROLLBACK; -BEGIN; - - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - WITH t1 AS (DELETE FROM distributed_table RETURNING id) - DELETE FROM reference_table RETURNING id; -DEBUG: generating subplan 184_1 for CTE t1: DELETE FROM test_fkey_to_ref_in_tx.distributed_table RETURNING id -DEBUG: Plan 184 query after replacing subqueries and CTEs: DELETE FROM test_fkey_to_ref_in_tx.reference_table RETURNING id - id ----- -(0 rows) - -ROLLBACK; -RESET client_min_messages; -\set VERBOSITY terse -DROP SCHEMA test_fkey_to_ref_in_tx CASCADE; -NOTICE: drop cascades to 5 other objects -\set VERBOSITY default -SET search_path TO public; diff --git a/src/test/regress/expected/foreign_key_to_reference_table.out b/src/test/regress/expected/foreign_key_to_reference_table.out index eee29f719..2737d3d52 100644 --- a/src/test/regress/expected/foreign_key_to_reference_table.out +++ b/src/test/regress/expected/foreign_key_to_reference_table.out @@ -1,13 +1,6 @@ -- -- FOREIGN_KEY_TO_REFERENCE_TABLE -- -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - version_above_nine --------------------- - t -(1 row) - CREATE SCHEMA fkey_reference_table; SET search_path TO 'fkey_reference_table'; SET citus.shard_replication_factor TO 1; @@ -15,12 +8,12 @@ SET citus.shard_count TO 8; SET citus.next_shard_id TO 7000000; SET citus.next_placement_id TO 7000000; CREATE TYPE foreign_details AS (name text, relid text, refd_relid text); -CREATE VIEW table_fkeys_in_workers AS +CREATE VIEW table_fkeys_in_workers AS SELECT -(json_populate_record(NULL::foreign_details, - json_array_elements_text((run_command_on_workers( $$ +(json_populate_record(NULL::foreign_details, + json_array_elements_text((run_command_on_workers( $$ SELECT - COALESCE(json_agg(row_to_json(d)), '[]'::json) + COALESCE(json_agg(row_to_json(d)), '[]'::json) FROM ( SELECT @@ -405,7 +398,7 @@ SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' A (8 rows) DROP TABLE referencing_table; --- foreign keys are supported either in between distributed tables including the +-- foreign keys are supported either in between distributed tables including the -- distribution column or from distributed tables to reference tables. CREATE TABLE referencing_table(id int, ref_id int); SELECT create_distributed_table('referencing_table', 'ref_id', 'append'); @@ -568,7 +561,7 @@ SELECT count(*) FROM referencing_table WHERE ref_id = 1; DROP TABLE referencing_table; DROP TABLE referenced_table; -- foreign key as composite key -CREATE TYPE fkey_reference_table.composite AS (key1 int, key2 int); +CREATE TYPE fkey_reference_table.composite AS (key1 int, key2 int); CREATE TABLE referenced_table(test_column composite, PRIMARY KEY(test_column)); CREATE TABLE referencing_table(id int, referencing_composite composite); SELECT create_reference_table('referenced_table'); @@ -597,7 +590,7 @@ DROP TABLE referenced_table CASCADE; NOTICE: drop cascades to constraint fkey_ref on table referencing_table DROP TABLE referencing_table CASCADE; -- In the following test, we'll use a SERIAL column as the referenced column --- in the foreign constraint. We'll first show that and insert on non-serial +-- in the foreign constraint. We'll first show that and insert on non-serial -- column successfully inserts into the serial and referenced column. -- Accordingly, the inserts into the referencing table which references to the -- serial column will be successful. @@ -629,9 +622,9 @@ DROP TABLE referenced_table CASCADE; NOTICE: drop cascades to constraint fkey_ref on table referencing_table DROP TABLE referencing_table CASCADE; -- In the following test, we'll use a SERIAL column as the referencing column --- in the foreign constraint. We'll first show that the values that exist +-- in the foreign constraint. We'll first show that the values that exist -- in the referenced tables are successfully generated by the serial column --- and inserted to the distributed table. However, if the values that are generated +-- and inserted to the distributed table. However, if the values that are generated -- by serial column do not exist on the referenced table, the query fails. CREATE TABLE referenced_table(test_column int PRIMARY KEY, test_column2 int); CREATE TABLE referencing_table(id int, ref_id SERIAL); @@ -653,16 +646,16 @@ INSERT INTO referenced_table SELECT x,x FROM generate_series(1,1000) AS f(x); INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,1000) AS f(x); -- Fails for non existing value inserts (serial is already incremented) INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,10) AS f(x); -ERROR: insert or update on table "referencing_table_7000195" violates foreign key constraint "fkey_ref_7000195" -DETAIL: Key (ref_id)=(1009) is not present in table "referenced_table_7000187". +ERROR: insert or update on table "referencing_table_7000190" violates foreign key constraint "fkey_ref_7000190" +DETAIL: Key (ref_id)=(1004) is not present in table "referenced_table_7000187". DROP TABLE referenced_table CASCADE; NOTICE: drop cascades to constraint fkey_ref on table referencing_table DROP TABLE referencing_table CASCADE; -- In the following test, we'll use a SERIAL column as the referencing column --- and referenced columns in a foreign constraint. We'll first show that the --- the inserts into referenced column will successfully generate and insert +-- and referenced columns in a foreign constraint. We'll first show that the +-- the inserts into referenced column will successfully generate and insert -- data into serial column. Then, we will be successfully insert the same amount --- of data into referencing table. However, if the values that are generated +-- of data into referencing table. However, if the values that are generated -- by serial column do not exist on the referenced table, the query fails. CREATE TABLE referenced_table(test_column SERIAL PRIMARY KEY, test_column2 int); CREATE TABLE referencing_table(id int, ref_id SERIAL); @@ -684,8 +677,8 @@ INSERT INTO referenced_table(test_column2) SELECT x FROM generate_series(1,1000) INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,1000) AS f(x); -- Fails for non existing value inserts (serial is already incremented) INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,10) AS f(x); -ERROR: insert or update on table "referencing_table_7000200" violates foreign key constraint "fkey_ref_7000200" -DETAIL: Key (ref_id)=(1003) is not present in table "referenced_table_7000196". +ERROR: insert or update on table "referencing_table_7000199" violates foreign key constraint "fkey_ref_7000199" +DETAIL: Key (ref_id)=(1004) is not present in table "referenced_table_7000196". DROP TABLE referenced_table CASCADE; NOTICE: drop cascades to constraint fkey_ref on table referencing_table DROP TABLE referencing_table CASCADE; @@ -713,7 +706,7 @@ INSERT INTO referencing_table SELECT x,(random()*1000)::int FROM generate_series DROP TABLE referenced_table CASCADE; NOTICE: drop cascades to constraint fkey_ref on table referencing_table DROP TABLE referencing_table CASCADE; --- In the following tests, we create a foreign constraint with +-- In the following tests, we create a foreign constraint with -- ON UPDATE CASCADE and see if it works properly with cascading upsert CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1); @@ -771,11 +764,11 @@ COMMIT; DROP TABLE referenced_table CASCADE; NOTICE: drop cascades to constraint referencing_table_ref_id_fkey on table referencing_table DROP TABLE referencing_table CASCADE; --- Chained references +-- Chained references -- In the following test, we create foreign keys from one column in a distributed -- table to two reference tables. We expect to see that even if a data exist in -- one reference table, it is not going to be inserted in to referencing table --- because of lack of the key in the other table. Data can only be inserted into +-- because of lack of the key in the other table. Data can only be inserted into -- referencing table if it exists in both referenced tables. -- Additionally, delete or update in one referenced table should cascade properly. CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); @@ -826,16 +819,16 @@ INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); INSERT INTO referenced_table2 SELECT x, x+1 FROM generate_series(500,1500) AS f(x); -- should fail INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,1500) AS f(x); -ERROR: insert or update on table "referencing_table_7000229" violates foreign key constraint "foreign_key_2_7000229" -DETAIL: Key (id)=(0) is not present in table "referenced_table2_7000225". +ERROR: insert or update on table "referencing_table_7000230" violates foreign key constraint "foreign_key_2_7000230" +DETAIL: Key (id)=(28) is not present in table "referenced_table2_7000225". -- should fail INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); -ERROR: insert or update on table "referencing_table_7000229" violates foreign key constraint "foreign_key_2_7000229" -DETAIL: Key (id)=(0) is not present in table "referenced_table2_7000225". +ERROR: insert or update on table "referencing_table_7000226" violates foreign key constraint "foreign_key_2_7000226" +DETAIL: Key (id)=(1) is not present in table "referenced_table2_7000225". -- should fail INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(1000,1400) AS f(x); -ERROR: insert or update on table "referencing_table_7000231" violates foreign key constraint "fkey_ref_7000231" -DETAIL: Key (id)=(1001) is not present in table "referenced_table_7000224". +ERROR: insert or update on table "referencing_table_7000228" violates foreign key constraint "fkey_ref_7000228" +DETAIL: Key (id)=(1015) is not present in table "referenced_table_7000224". -- should succeed INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(600,900) AS f(x); SELECT count(*) FROM referencing_table; @@ -898,10 +891,10 @@ DROP TABLE referenced_table2 CASCADE; NOTICE: drop cascades to constraint referencing_table_id_fkey1 on table referencing_table DROP TABLE referencing_table CASCADE; -- In the following test, we create foreign keys from two columns in a distributed --- table to two reference tables separately. We expect to see that even if a data +-- table to two reference tables separately. We expect to see that even if a data -- exist in one reference table for one column, it is not going to be inserted in --- to referencing table because the other constraint doesn't hold. Data can only --- be inserted into referencing table if both columns exist in respective columns +-- to referencing table because the other constraint doesn't hold. Data can only +-- be inserted into referencing table if both columns exist in respective columns -- in referenced tables. -- Additionally, delete or update in one referenced table should cascade properly. CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); @@ -954,13 +947,13 @@ INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); INSERT INTO referenced_table2 SELECT x, x+1 FROM generate_series(500,1500) AS f(x); -- should fail INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,1500) AS f(x); -ERROR: insert or update on table "referencing_table_7000249" violates foreign key constraint "foreign_key_2_7000249" +ERROR: insert or update on table "referencing_table_7000246" violates foreign key constraint "foreign_key_2_7000246" -- should fail INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); -ERROR: insert or update on table "referencing_table_7000249" violates foreign key constraint "foreign_key_2_7000249" +ERROR: insert or update on table "referencing_table_7000246" violates foreign key constraint "foreign_key_2_7000246" -- should fail INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(1000,1400) AS f(x); -ERROR: insert or update on table "referencing_table_7000251" violates foreign key constraint "fkey_ref_7000251" +ERROR: insert or update on table "referencing_table_7000248" violates foreign key constraint "fkey_ref_7000248" -- should succeed INSERT INTO referencing_table SELECT x, x+501 FROM generate_series(0,1000) AS f(x); SELECT count(*) FROM referencing_table; @@ -1026,8 +1019,8 @@ NOTICE: drop cascades to constraint foreign_key_2 on table referencing_table DROP TABLE referencing_table CASCADE; \set VERBOSITY default -- two distributed tables are referencing to one reference table and --- in the same time the distributed table 2 is referencing to --- distributed table 1. Thus, we have a triangular +-- in the same time the distributed table 2 is referencing to +-- distributed table 1. Thus, we have a triangular -- distributed table 1 has a foreign key from the distribution column to reference table -- distributed table 2 has a foreign key from a non-distribution column to reference table -- distributed table 2 has a foreign key to distributed table 1 on the distribution column @@ -1092,14 +1085,14 @@ SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' A INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); -- should fail INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(0,100) AS f(x); -ERROR: insert or update on table "referencing_table2_7000276" violates foreign key constraint "fkey_ref_to_dist_7000276" -DETAIL: Key (id)=(0) is not present in table "referencing_table_7000268". +ERROR: insert or update on table "referencing_table2_7000273" violates foreign key constraint "fkey_ref_to_dist_7000273" +DETAIL: Key (id)=(1) is not present in table "referencing_table_7000265". -- should succeed INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); -- should fail INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(200,500) AS f(x); -ERROR: insert or update on table "referencing_table2_7000274" violates foreign key constraint "fkey_ref_to_dist_7000274" -DETAIL: Key (id)=(403) is not present in table "referencing_table_7000266". +ERROR: insert or update on table "referencing_table2_7000273" violates foreign key constraint "fkey_ref_to_dist_7000273" +DETAIL: Key (id)=(401) is not present in table "referencing_table_7000265". -- should succeed INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(0,300) AS f(x); DELETE FROM referenced_table WHERE test_column < 200; @@ -1167,7 +1160,7 @@ DROP TABLE referencing_table CASCADE; NOTICE: drop cascades to constraint referencing_table2_id_fkey on table referencing_table2 DROP TABLE referencing_table2 CASCADE; \set VERBOSITY default --- In this test we have a chained relationship in form of +-- In this test we have a chained relationship in form of -- distributed table (referencing_referencing_table) has a foreign key with two columns -- to another distributed table (referencing_table) -- referencing_table has another foreign key with 2 columns to referenced_table. @@ -1231,11 +1224,11 @@ DROP TABLE referencing_table CASCADE; NOTICE: drop cascades to constraint referencing_referencing_table_id_fkey on table referencing_referencing_table DROP TABLE referencing_referencing_table; -- test if create_distributed_table works in transactions with some edge cases --- the following checks if create_distributed_table works on foreign keys when +-- the following checks if create_distributed_table works on foreign keys when -- one of them is a self-referencing table of multiple distributed tables BEGIN; CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); + SELECT create_reference_table('test_table_1'); create_reference_table ------------------------ @@ -1308,7 +1301,6 @@ ERROR: current transaction is aborted, commands ignored until end of transactio ROLLBACK; -- make sure that we fail if we need parallel data load BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); @@ -1633,7 +1625,7 @@ SELECT create_distributed_table('test_table_2', 'id'); (1 row) INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); TRUNCATE test_table_1 CASCADE; NOTICE: truncate cascades to table "test_table_2" SELECT * FROM test_table_2; @@ -1658,7 +1650,7 @@ SELECT create_distributed_table('test_table_2', 'id'); (1 row) INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); BEGIN; TRUNCATE test_table_1 CASCADE; NOTICE: truncate cascades to table "test_table_2" @@ -1712,7 +1704,7 @@ SELECT create_distributed_table('test_table_2', 'id'); (1 row) INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); TRUNCATE test_table_2 CASCADE; SELECT * FROM test_table_2; id | value_1 @@ -1744,7 +1736,7 @@ SELECT create_distributed_table('test_table_2', 'id'); (1 row) INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); BEGIN; TRUNCATE test_table_2 CASCADE; COMMIT; @@ -1763,7 +1755,7 @@ SELECT * FROM test_table_1; DROP TABLE test_table_1, test_table_2; -- check if we successfuly set multi_shard_modify_mode to sequential after sequentially running DDLs --- in transaction since the upcoming DDLs need to run sequentially. +-- in transaction since the upcoming DDLs need to run sequentially. CREATE TABLE test_table_1(id int PRIMARY KEY); CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); CREATE TABLE test_table_3(id int PRIMARY KEY, value_1 int); diff --git a/src/test/regress/expected/foreign_key_to_reference_table_0.out b/src/test/regress/expected/foreign_key_to_reference_table_0.out deleted file mode 100644 index 19a2a1092..000000000 --- a/src/test/regress/expected/foreign_key_to_reference_table_0.out +++ /dev/null @@ -1,1912 +0,0 @@ --- --- FOREIGN_KEY_TO_REFERENCE_TABLE --- -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - version_above_nine --------------------- - f -(1 row) - -CREATE SCHEMA fkey_reference_table; -SET search_path TO 'fkey_reference_table'; -SET citus.shard_replication_factor TO 1; -SET citus.shard_count TO 8; -SET citus.next_shard_id TO 7000000; -SET citus.next_placement_id TO 7000000; -CREATE TYPE foreign_details AS (name text, relid text, refd_relid text); -SELECT run_command_on_workers($$CREATE TYPE foreign_details AS (name text, relid text, refd_relid text)$$); - run_command_on_workers ------------------------------------ - (localhost,57637,t,"CREATE TYPE") - (localhost,57638,t,"CREATE TYPE") -(2 rows) - -CREATE VIEW table_fkeys_in_workers AS -SELECT -(json_populate_record(NULL::foreign_details, - json_array_elements_text((run_command_on_workers( $$ - SELECT - COALESCE(json_agg(row_to_json(d)), '[]'::json) - FROM - ( - SELECT - distinct name, - relid::regclass::text, - refd_relid::regclass::text - FROM - table_fkey_cols - WHERE - "schema" = 'fkey_reference_table' - ) - d $$ )).RESULT::json )::json )).* ; -CREATE TABLE referenced_table(id int UNIQUE, test_column int); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - --- we still do not support update/delete operations through foreign constraints if the foreign key includes the distribution column --- All should fail -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON DELETE SET NULL; -ERROR: cannot create foreign key constraint -DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON DELETE SET NULL); -SELECT create_distributed_table('referencing_table', 'ref_id'); -ERROR: cannot create foreign key constraint -DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON DELETE SET DEFAULT; -ERROR: cannot create foreign key constraint -DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON DELETE SET DEFAULT); -SELECT create_distributed_table('referencing_table', 'ref_id'); -ERROR: cannot create foreign key constraint -DETAIL: SET NULL or SET DEFAULT is not supported in ON DELETE operation when distribution key is included in the foreign key constraint -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE SET NULL; -ERROR: cannot create foreign key constraint -DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. -DROP TABLE referencing_table; -BEGIN; - CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(ref_id) REFERENCES referenced_table(id) ON UPDATE SET NULL); - SELECT create_distributed_table('referencing_table', 'ref_id'); -ERROR: cannot create foreign key constraint -DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. -ROLLBACK; --- try with multiple columns including the distribution column -DROP TABLE referenced_table; -CREATE TABLE referenced_table(id int, test_column int, PRIMARY KEY(id, test_column)); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id, ref_id) REFERENCES referenced_table(id, test_column) ON UPDATE SET DEFAULT; -ERROR: cannot create foreign key constraint -DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(id, ref_id) REFERENCES referenced_table(id, test_column) ON UPDATE SET DEFAULT); -SELECT create_distributed_table('referencing_table', 'ref_id'); -ERROR: cannot create foreign key constraint -DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id, ref_id) REFERENCES referenced_table(id, test_column) ON UPDATE CASCADE; -ERROR: cannot create foreign key constraint -DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. -DROP TABLE referencing_table; -BEGIN; - CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(id, ref_id) REFERENCES referenced_table(id, test_column) ON UPDATE CASCADE); - SELECT create_distributed_table('referencing_table', 'ref_id'); -ERROR: cannot create foreign key constraint -DETAIL: SET NULL, SET DEFAULT or CASCADE is not supported in ON UPDATE operation when distribution key included in the foreign constraint. -ROLLBACK; --- all of the above is supported if the foreign key does not include distribution column -DROP TABLE referenced_table; -CREATE TABLE referenced_table(id int, test_column int, PRIMARY KEY(id)); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON DELETE SET NULL; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid -------------------+------------------------------------------------+----------------------------------------------- - fkey_ref_7000043 | fkey_reference_table.referencing_table_7000043 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000044 | fkey_reference_table.referencing_table_7000044 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000045 | fkey_reference_table.referencing_table_7000045 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000046 | fkey_reference_table.referencing_table_7000046 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000047 | fkey_reference_table.referencing_table_7000047 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000048 | fkey_reference_table.referencing_table_7000048 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000049 | fkey_reference_table.referencing_table_7000049 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000050 | fkey_reference_table.referencing_table_7000050 | fkey_reference_table.referenced_table_7000042 -(8 rows) - -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(id) REFERENCES referenced_table(id) ON DELETE SET NULL); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid ------------------------------------+------------------------------------------------+----------------------------------------------- - referencing_table_id_fkey_7000051 | fkey_reference_table.referencing_table_7000051 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000052 | fkey_reference_table.referencing_table_7000052 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000053 | fkey_reference_table.referencing_table_7000053 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000054 | fkey_reference_table.referencing_table_7000054 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000055 | fkey_reference_table.referencing_table_7000055 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000056 | fkey_reference_table.referencing_table_7000056 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000057 | fkey_reference_table.referencing_table_7000057 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000058 | fkey_reference_table.referencing_table_7000058 | fkey_reference_table.referenced_table_7000042 -(8 rows) - -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON DELETE SET DEFAULT; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid -------------------+------------------------------------------------+----------------------------------------------- - fkey_ref_7000059 | fkey_reference_table.referencing_table_7000059 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000060 | fkey_reference_table.referencing_table_7000060 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000061 | fkey_reference_table.referencing_table_7000061 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000062 | fkey_reference_table.referencing_table_7000062 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000063 | fkey_reference_table.referencing_table_7000063 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000064 | fkey_reference_table.referencing_table_7000064 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000065 | fkey_reference_table.referencing_table_7000065 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000066 | fkey_reference_table.referencing_table_7000066 | fkey_reference_table.referenced_table_7000042 -(8 rows) - -DROP TABLE referencing_table; -BEGIN; - CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY(id) REFERENCES referenced_table(id) ON DELETE SET DEFAULT); - SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -COMMIT; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid ------------------------------------+------------------------------------------------+----------------------------------------------- - referencing_table_id_fkey_7000067 | fkey_reference_table.referencing_table_7000067 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000068 | fkey_reference_table.referencing_table_7000068 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000069 | fkey_reference_table.referencing_table_7000069 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000070 | fkey_reference_table.referencing_table_7000070 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000071 | fkey_reference_table.referencing_table_7000071 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000072 | fkey_reference_table.referencing_table_7000072 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000073 | fkey_reference_table.referencing_table_7000073 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000074 | fkey_reference_table.referencing_table_7000074 | fkey_reference_table.referenced_table_7000042 -(8 rows) - -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON UPDATE SET NULL; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid -------------------+------------------------------------------------+----------------------------------------------- - fkey_ref_7000075 | fkey_reference_table.referencing_table_7000075 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000076 | fkey_reference_table.referencing_table_7000076 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000077 | fkey_reference_table.referencing_table_7000077 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000078 | fkey_reference_table.referencing_table_7000078 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000079 | fkey_reference_table.referencing_table_7000079 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000080 | fkey_reference_table.referencing_table_7000080 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000081 | fkey_reference_table.referencing_table_7000081 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000082 | fkey_reference_table.referencing_table_7000082 | fkey_reference_table.referenced_table_7000042 -(8 rows) - -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON UPDATE SET DEFAULT; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid -------------------+------------------------------------------------+----------------------------------------------- - fkey_ref_7000083 | fkey_reference_table.referencing_table_7000083 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000084 | fkey_reference_table.referencing_table_7000084 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000085 | fkey_reference_table.referencing_table_7000085 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000086 | fkey_reference_table.referencing_table_7000086 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000087 | fkey_reference_table.referencing_table_7000087 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000088 | fkey_reference_table.referencing_table_7000088 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000089 | fkey_reference_table.referencing_table_7000089 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000090 | fkey_reference_table.referencing_table_7000090 | fkey_reference_table.referenced_table_7000042 -(8 rows) - -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON UPDATE CASCADE; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid -------------------+------------------------------------------------+----------------------------------------------- - fkey_ref_7000091 | fkey_reference_table.referencing_table_7000091 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000092 | fkey_reference_table.referencing_table_7000092 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000093 | fkey_reference_table.referencing_table_7000093 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000094 | fkey_reference_table.referencing_table_7000094 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000095 | fkey_reference_table.referencing_table_7000095 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000096 | fkey_reference_table.referencing_table_7000096 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000097 | fkey_reference_table.referencing_table_7000097 | fkey_reference_table.referenced_table_7000042 - fkey_ref_7000098 | fkey_reference_table.referencing_table_7000098 | fkey_reference_table.referenced_table_7000042 -(8 rows) - -DROP TABLE referencing_table; --- check if we can add the foreign key while adding the column -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD COLUMN referencing int REFERENCES referenced_table(id) ON UPDATE CASCADE; -ERROR: cannot execute ADD COLUMN command with PRIMARY KEY, UNIQUE, FOREIGN and CHECK constraints -DETAIL: Adding a column with a constraint in one command is not supported because all constraints in Citus must have explicit names -HINT: You can issue each command separately such as ALTER TABLE referencing_table ADD COLUMN referencing data_type; ALTER TABLE referencing_table ADD CONSTRAINT constraint_name FOREIGN KEY (referencing) REFERENCES referenced_table(id) ON UPDATE CASCADE; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid -------+-------+------------ -(0 rows) - -DROP TABLE referencing_table; --- foreign keys are only supported when the replication factor = 1 -SET citus.shard_replication_factor TO 2; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(id); -ERROR: cannot create foreign key constraint -DETAIL: Citus Community Edition currently supports foreign key constraints only for "citus.shard_replication_factor = 1". -HINT: Please change "citus.shard_replication_factor to 1". To learn more about using foreign keys with other replication factors, please contact us at https://citusdata.com/about/contact_us. -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid -------+-------+------------ -(0 rows) - -DROP TABLE referencing_table; --- should fail when we add the column as well -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD COLUMN referencing_col int REFERENCES referenced_table(id) ON DELETE SET NULL; -ERROR: cannot create foreign key constraint -DETAIL: Citus Community Edition currently supports foreign key constraints only for "citus.shard_replication_factor = 1". -HINT: Please change "citus.shard_replication_factor to 1". To learn more about using foreign keys with other replication factors, please contact us at https://citusdata.com/about/contact_us. -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid -------+-------+------------ -(0 rows) - -DROP TABLE referencing_table; -SET citus.shard_replication_factor TO 1; --- simple create_distributed_table should work in/out transactions on tables with foreign key to reference tables -CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(id)); -SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid ------------------------------------+------------------------------------------------+----------------------------------------------- - referencing_table_id_fkey_7000123 | fkey_reference_table.referencing_table_7000123 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000124 | fkey_reference_table.referencing_table_7000124 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000125 | fkey_reference_table.referencing_table_7000125 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000126 | fkey_reference_table.referencing_table_7000126 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000127 | fkey_reference_table.referencing_table_7000127 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000128 | fkey_reference_table.referencing_table_7000128 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000129 | fkey_reference_table.referencing_table_7000129 | fkey_reference_table.referenced_table_7000042 - referencing_table_id_fkey_7000130 | fkey_reference_table.referencing_table_7000130 | fkey_reference_table.referenced_table_7000042 -(8 rows) - -DROP TABLE referencing_table; -DROP TABLE referenced_table; -BEGIN; - CREATE TABLE referenced_table(id int, test_column int, PRIMARY KEY(id)); - SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - - CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(id)); - SELECT create_distributed_table('referencing_table', 'ref_id'); - create_distributed_table --------------------------- - -(1 row) - -COMMIT; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid ------------------------------------+------------------------------------------------+----------------------------------------------- - referencing_table_id_fkey_7000132 | fkey_reference_table.referencing_table_7000132 | fkey_reference_table.referenced_table_7000131 - referencing_table_id_fkey_7000133 | fkey_reference_table.referencing_table_7000133 | fkey_reference_table.referenced_table_7000131 - referencing_table_id_fkey_7000134 | fkey_reference_table.referencing_table_7000134 | fkey_reference_table.referenced_table_7000131 - referencing_table_id_fkey_7000135 | fkey_reference_table.referencing_table_7000135 | fkey_reference_table.referenced_table_7000131 - referencing_table_id_fkey_7000136 | fkey_reference_table.referencing_table_7000136 | fkey_reference_table.referenced_table_7000131 - referencing_table_id_fkey_7000137 | fkey_reference_table.referencing_table_7000137 | fkey_reference_table.referenced_table_7000131 - referencing_table_id_fkey_7000138 | fkey_reference_table.referencing_table_7000138 | fkey_reference_table.referenced_table_7000131 - referencing_table_id_fkey_7000139 | fkey_reference_table.referencing_table_7000139 | fkey_reference_table.referenced_table_7000131 -(8 rows) - -DROP TABLE referencing_table; --- foreign keys are supported either in between distributed tables including the --- distribution column or from distributed tables to reference tables. -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id', 'append'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(id); -ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table -DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table -SELECT * FROM table_fkeys_in_workers WHERE name LIKE 'fkey_ref%' ORDER BY 1,2,3; - name | relid | refd_relid -------+-------+------------ -(0 rows) - -DROP TABLE referencing_table; -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_distributed_table('referencing_table', 'ref_id', 'range'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(id); -ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table -DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table -SELECT * FROM table_fkeys_in_workers WHERE name LIKE 'fkey_ref%' ORDER BY 1,2,3; - name | relid | refd_relid -------+-------+------------ -(0 rows) - -DROP TABLE referencing_table; -DROP TABLE referenced_table; --- test foreign constraint with correct conditions -CREATE TABLE referenced_table(id int UNIQUE, test_column int, PRIMARY KEY(id, test_column)); -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(id); --- test inserts --- test insert to referencing table while there is NO corresponding value in referenced table -INSERT INTO referencing_table VALUES(1, 1); -ERROR: insert or update on table "referencing_table_7000141" violates foreign key constraint "fkey_ref_7000141" -DETAIL: Key (ref_id)=(1) is not present in table "referenced_table_7000140". -CONTEXT: while executing command on localhost:57637 --- test insert to referencing while there is corresponding value in referenced table -INSERT INTO referenced_table SELECT x, x from generate_series(1,1000) as f(x); -INSERT INTO referencing_table SELECT x, x from generate_series(1,500) as f(x); --- test deletes --- test delete from referenced table while there is corresponding value in referencing table -DELETE FROM referenced_table WHERE id > 3; -ERROR: update or delete on table "referenced_table_7000140" violates foreign key constraint "fkey_ref_7000143" on table "referencing_table_7000143" -DETAIL: Key (id)=(4) is still referenced from table "referencing_table_7000143". -CONTEXT: while executing command on localhost:57637 --- test delete from referenced table while there is NO corresponding value in referencing table -DELETE FROM referenced_table WHERE id = 501; --- test cascading truncate -TRUNCATE referenced_table CASCADE; -NOTICE: truncate cascades to table "referencing_table" -SELECT count(*) FROM referencing_table; - count -------- - 0 -(1 row) - --- drop table for next tests -DROP TABLE referencing_table; -DROP TABLE referenced_table; --- self referencing foreign key on reference tables are not allowed --- TODO try create_reference_table with already created foreign key. -CREATE TABLE referenced_table(id int, test_column int, PRIMARY KEY(id)); -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_reference_table('referencing_table'); - create_reference_table ------------------------- - -(1 row) - --- self referencing foreign key -ALTER TABLE referenced_table ADD CONSTRAINT fkey_ref FOREIGN KEY (test_column) REFERENCES referenced_table(id); -ERROR: cannot create foreign key constraint because reference tables are not supported as the referencing table of a foreign constraint -DETAIL: Reference tables are only supported as the referenced table of a foreign key when the referencing table is a hash distributed table --- foreign Keys from reference table to reference table are not allowed -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY(id) REFERENCES referenced_table(id) ON UPDATE CASCADE; -ERROR: cannot create foreign key constraint because reference tables are not supported as the referencing table of a foreign constraint -DETAIL: Reference tables are only supported as the referenced table of a foreign key when the referencing table is a hash distributed table -DROP TABLE referenced_table; -DROP TABLE referencing_table; --- cascades on delete with different schemas -CREATE SCHEMA referenced_schema; -CREATE SCHEMA referencing_schema; -CREATE TABLE referenced_schema.referenced_table(id int UNIQUE, test_column int, PRIMARY KEY(id, test_column)); -CREATE TABLE referencing_schema.referencing_table(id int, ref_id int); -SELECT create_reference_table('referenced_schema.referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_schema.referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_schema.referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_schema.referenced_table(id) ON DELETE CASCADE; -INSERT INTO referenced_schema.referenced_table SELECT x, x from generate_series(1,1000) as f(x); -INSERT INTO referencing_schema.referencing_table SELECT x, x from generate_series(1,1000) as f(x); -DELETE FROM referenced_schema.referenced_table WHERE id > 800; -SELECT count(*) FROM referencing_schema.referencing_table; - count -------- - 800 -(1 row) - -DROP SCHEMA referenced_schema CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table referenced_schema.referenced_table -drop cascades to constraint fkey_ref on table referencing_schema.referencing_table -DROP SCHEMA referencing_schema CASCADE; -NOTICE: drop cascades to table referencing_schema.referencing_table --- on delete set update cascades properly -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); -CREATE TABLE referencing_table(id int, ref_id int DEFAULT 1); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE SET DEFAULT; -INSERT INTO referenced_table SELECT x, x FROM generate_series(1,1000) AS f(x); -INSERT INTO referencing_table SELECT x, x FROM generate_series(1,1000) AS f(x); -DELETE FROM referenced_table WHERE test_column > 800; -SELECT count(*) FROM referencing_table WHERE ref_id = 1; - count -------- - 201 -(1 row) - -DROP TABLE referencing_table; -DROP TABLE referenced_table; --- foreign key as composite key -CREATE TYPE fkey_reference_table.composite AS (key1 int, key2 int); -SELECT run_command_on_workers($$CREATE TYPE fkey_reference_table.composite AS (key1 int, key2 int)$$) ORDER BY 1; - run_command_on_workers ------------------------------------ - (localhost,57637,t,"CREATE TYPE") - (localhost,57638,t,"CREATE TYPE") -(2 rows) - -CREATE TABLE referenced_table(test_column composite, PRIMARY KEY(test_column)); -CREATE TABLE referencing_table(id int, referencing_composite composite); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (referencing_composite) REFERENCES referenced_table(test_column) ON DELETE CASCADE; -INSERT INTO referenced_table SELECT (x+1, x+1)::composite FROM generate_series(1,1000) AS f(x); -INSERT INTO referencing_table SELECT x, (x+1, x+1)::composite FROM generate_series(1,1000) AS f(x); -DELETE FROM referenced_table WHERE (test_column).key1 > 900; -SELECT count(*) FROM referencing_table; - count -------- - 899 -(1 row) - -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referencing_table CASCADE; --- In the following test, we'll use a SERIAL column as the referenced column --- in the foreign constraint. We'll first show that and insert on non-serial --- column successfully inserts into the serial and referenced column. --- Accordingly, the inserts into the referencing table which references to the --- serial column will be successful. -CREATE TABLE referenced_table(test_column SERIAL PRIMARY KEY, test_column2 int); -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; -INSERT INTO referenced_table(test_column2) SELECT x FROM generate_series(1,1000) AS f(x); -INSERT INTO referencing_table SELECT x, x FROM generate_series(1,1000) AS f(x); -DELETE FROM referenced_table WHERE test_column2 > 10; -SELECT count(*) FROM referencing_table; - count -------- - 10 -(1 row) - -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referencing_table CASCADE; --- In the following test, we'll use a SERIAL column as the referencing column --- in the foreign constraint. We'll first show that the values that exist --- in the referenced tables are successfully generated by the serial column --- and inserted to the distributed table. However, if the values that are generated --- by serial column do not exist on the referenced table, the query fails. -CREATE TABLE referenced_table(test_column int PRIMARY KEY, test_column2 int); -CREATE TABLE referencing_table(id int, ref_id SERIAL); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; -INSERT INTO referenced_table SELECT x,x FROM generate_series(1,1000) AS f(x); --- Success for existing inserts -INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,1000) AS f(x); --- Fails for non existing value inserts (serial is already incremented) -INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,10) AS f(x); -ERROR: insert or update on table "referencing_table_7000195" violates foreign key constraint "fkey_ref_7000195" -DETAIL: Key (ref_id)=(1009) is not present in table "referenced_table_7000187". -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referencing_table CASCADE; --- In the following test, we'll use a SERIAL column as the referencing column --- and referenced columns in a foreign constraint. We'll first show that the --- the inserts into referenced column will successfully generate and insert --- data into serial column. Then, we will be successfully insert the same amount --- of data into referencing table. However, if the values that are generated --- by serial column do not exist on the referenced table, the query fails. -CREATE TABLE referenced_table(test_column SERIAL PRIMARY KEY, test_column2 int); -CREATE TABLE referencing_table(id int, ref_id SERIAL); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; -INSERT INTO referenced_table(test_column2) SELECT x FROM generate_series(1,1000) AS f(x); --- Success for existing values -INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,1000) AS f(x); --- Fails for non existing value inserts (serial is already incremented) -INSERT INTO referencing_table(id) SELECT x FROM generate_series(1,10) AS f(x); -ERROR: insert or update on table "referencing_table_7000197" violates foreign key constraint "fkey_ref_7000197" -DETAIL: Key (ref_id)=(1001) is not present in table "referenced_table_7000196". -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referencing_table CASCADE; --- In the following test, we use a volatile function in the referencing --- column in a foreign constraint. We show that if the data exists in the --- referenced table, we can successfully use volatile functions with --- foreign constraints. -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); -CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON DELETE SET DEFAULT; -INSERT INTO referenced_table SELECT x, x FROM generate_series(0,1000) AS f(x); -INSERT INTO referencing_table SELECT x,(random()*1000)::int FROM generate_series(0,1000) AS f(x); -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referencing_table CASCADE; --- In the following tests, we create a foreign constraint with --- ON UPDATE CASCADE and see if it works properly with cascading upsert -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); -CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON UPDATE CASCADE; -INSERT INTO referenced_table SELECT x, x FROM generate_series(0,1000) AS f(x); -INSERT INTO referencing_table SELECT x, x FROM generate_series(0,1000) AS f(x); -INSERT INTO referenced_table VALUES (1,2), (2,3), (3,4), (4,5) -ON CONFLICT (test_column) -DO UPDATE - SET test_column = -1 * EXCLUDED.test_column; -SELECT * FROM referencing_table WHERE ref_id < 0 ORDER BY 1; - id | ref_id -----+-------- - 1 | -1 - 2 | -2 - 3 | -3 - 4 | -4 -(4 rows) - -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referencing_table CASCADE; --- create_distributed_table should fail for tables with data if fkey exists to reference table -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); -CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1, FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column) ON UPDATE CASCADE); -INSERT INTO referenced_table VALUES (1,1), (2,2), (3,3); -INSERT INTO referencing_table VALUES (1,1), (2,2), (3,3); -SELECT create_reference_table('referenced_table'); -NOTICE: Copying data from local table... - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); -ERROR: cannot distribute "referencing_table" in sequential mode because it is not empty -HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. -BEGIN; - SELECT create_distributed_table('referencing_table', 'id'); -ERROR: cannot distribute "referencing_table" in sequential mode because it is not empty -HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. -COMMIT; -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint referencing_table_ref_id_fkey on table referencing_table -DROP TABLE referencing_table CASCADE; --- Chained references --- In the following test, we create foreign keys from one column in a distributed --- table to two reference tables. We expect to see that even if a data exist in --- one reference table, it is not going to be inserted in to referencing table --- because of lack of the key in the other table. Data can only be inserted into --- referencing table if it exists in both referenced tables. --- Additionally, delete or update in one referenced table should cascade properly. -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); -CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_reference_table('referenced_table2'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; -ALTER TABLE referencing_table ADD CONSTRAINT foreign_key_2 FOREIGN KEY (id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid ------------------------+------------------------------------------------+------------------------------------------------ - fkey_ref_7000226 | fkey_reference_table.referencing_table_7000226 | fkey_reference_table.referenced_table_7000224 - fkey_ref_7000227 | fkey_reference_table.referencing_table_7000227 | fkey_reference_table.referenced_table_7000224 - fkey_ref_7000228 | fkey_reference_table.referencing_table_7000228 | fkey_reference_table.referenced_table_7000224 - fkey_ref_7000229 | fkey_reference_table.referencing_table_7000229 | fkey_reference_table.referenced_table_7000224 - fkey_ref_7000230 | fkey_reference_table.referencing_table_7000230 | fkey_reference_table.referenced_table_7000224 - fkey_ref_7000231 | fkey_reference_table.referencing_table_7000231 | fkey_reference_table.referenced_table_7000224 - fkey_ref_7000232 | fkey_reference_table.referencing_table_7000232 | fkey_reference_table.referenced_table_7000224 - fkey_ref_7000233 | fkey_reference_table.referencing_table_7000233 | fkey_reference_table.referenced_table_7000224 - foreign_key_2_7000226 | fkey_reference_table.referencing_table_7000226 | fkey_reference_table.referenced_table2_7000225 - foreign_key_2_7000227 | fkey_reference_table.referencing_table_7000227 | fkey_reference_table.referenced_table2_7000225 - foreign_key_2_7000228 | fkey_reference_table.referencing_table_7000228 | fkey_reference_table.referenced_table2_7000225 - foreign_key_2_7000229 | fkey_reference_table.referencing_table_7000229 | fkey_reference_table.referenced_table2_7000225 - foreign_key_2_7000230 | fkey_reference_table.referencing_table_7000230 | fkey_reference_table.referenced_table2_7000225 - foreign_key_2_7000231 | fkey_reference_table.referencing_table_7000231 | fkey_reference_table.referenced_table2_7000225 - foreign_key_2_7000232 | fkey_reference_table.referencing_table_7000232 | fkey_reference_table.referenced_table2_7000225 - foreign_key_2_7000233 | fkey_reference_table.referencing_table_7000233 | fkey_reference_table.referenced_table2_7000225 -(16 rows) - -INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); -INSERT INTO referenced_table2 SELECT x, x+1 FROM generate_series(500,1500) AS f(x); --- should fail -INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,1500) AS f(x); -ERROR: insert or update on table "referencing_table_7000227" violates foreign key constraint "foreign_key_2_7000227" -DETAIL: Key (id)=(5) is not present in table "referenced_table2_7000225". --- should fail -INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); -ERROR: insert or update on table "referencing_table_7000227" violates foreign key constraint "foreign_key_2_7000227" -DETAIL: Key (id)=(5) is not present in table "referenced_table2_7000225". --- should fail -INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(1000,1400) AS f(x); -ERROR: insert or update on table "referencing_table_7000227" violates foreign key constraint "fkey_ref_7000227" -DETAIL: Key (id)=(1005) is not present in table "referenced_table_7000224". --- should succeed -INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(600,900) AS f(x); -SELECT count(*) FROM referencing_table; - count -------- - 301 -(1 row) - -DELETE FROM referenced_table WHERE test_column < 700; -SELECT count(*) FROM referencing_table; - count -------- - 201 -(1 row) - -DELETE FROM referenced_table2 WHERE test_column2 > 800; -SELECT count(*) FROM referencing_table; - count -------- - 101 -(1 row) - -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referenced_table2 CASCADE; -NOTICE: drop cascades to constraint foreign_key_2 on table referencing_table -DROP TABLE referencing_table CASCADE; --- check if the above fkeys are created with create_distributed_table -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); -CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); -CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE, FOREIGN KEY (id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_reference_table('referenced_table2'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 16 -(1 row) - -\set VERBOSITY terse -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint referencing_table_id_fkey on table referencing_table -DROP TABLE referenced_table2 CASCADE; -NOTICE: drop cascades to constraint referencing_table_id_fkey1 on table referencing_table -DROP TABLE referencing_table CASCADE; --- In the following test, we create foreign keys from two columns in a distributed --- table to two reference tables separately. We expect to see that even if a data --- exist in one reference table for one column, it is not going to be inserted in --- to referencing table because the other constraint doesn't hold. Data can only --- be inserted into referencing table if both columns exist in respective columns --- in referenced tables. --- Additionally, delete or update in one referenced table should cascade properly. -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); -CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); -CREATE TABLE referencing_table(id int, ref_id int); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_reference_table('referenced_table2'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -BEGIN; - ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; - ALTER TABLE referencing_table ADD CONSTRAINT foreign_key_2 FOREIGN KEY (ref_id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE; -COMMIT; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid ------------------------+------------------------------------------------+------------------------------------------------ - fkey_ref_7000246 | fkey_reference_table.referencing_table_7000246 | fkey_reference_table.referenced_table_7000244 - fkey_ref_7000247 | fkey_reference_table.referencing_table_7000247 | fkey_reference_table.referenced_table_7000244 - fkey_ref_7000248 | fkey_reference_table.referencing_table_7000248 | fkey_reference_table.referenced_table_7000244 - fkey_ref_7000249 | fkey_reference_table.referencing_table_7000249 | fkey_reference_table.referenced_table_7000244 - fkey_ref_7000250 | fkey_reference_table.referencing_table_7000250 | fkey_reference_table.referenced_table_7000244 - fkey_ref_7000251 | fkey_reference_table.referencing_table_7000251 | fkey_reference_table.referenced_table_7000244 - fkey_ref_7000252 | fkey_reference_table.referencing_table_7000252 | fkey_reference_table.referenced_table_7000244 - fkey_ref_7000253 | fkey_reference_table.referencing_table_7000253 | fkey_reference_table.referenced_table_7000244 - foreign_key_2_7000246 | fkey_reference_table.referencing_table_7000246 | fkey_reference_table.referenced_table2_7000245 - foreign_key_2_7000247 | fkey_reference_table.referencing_table_7000247 | fkey_reference_table.referenced_table2_7000245 - foreign_key_2_7000248 | fkey_reference_table.referencing_table_7000248 | fkey_reference_table.referenced_table2_7000245 - foreign_key_2_7000249 | fkey_reference_table.referencing_table_7000249 | fkey_reference_table.referenced_table2_7000245 - foreign_key_2_7000250 | fkey_reference_table.referencing_table_7000250 | fkey_reference_table.referenced_table2_7000245 - foreign_key_2_7000251 | fkey_reference_table.referencing_table_7000251 | fkey_reference_table.referenced_table2_7000245 - foreign_key_2_7000252 | fkey_reference_table.referencing_table_7000252 | fkey_reference_table.referenced_table2_7000245 - foreign_key_2_7000253 | fkey_reference_table.referencing_table_7000253 | fkey_reference_table.referenced_table2_7000245 -(16 rows) - -INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); -INSERT INTO referenced_table2 SELECT x, x+1 FROM generate_series(500,1500) AS f(x); --- should fail -INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,1500) AS f(x); -ERROR: insert or update on table "referencing_table_7000251" violates foreign key constraint "foreign_key_2_7000251" --- should fail -INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); -ERROR: insert or update on table "referencing_table_7000251" violates foreign key constraint "foreign_key_2_7000251" --- should fail -INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(1000,1400) AS f(x); -ERROR: insert or update on table "referencing_table_7000251" violates foreign key constraint "fkey_ref_7000251" --- should succeed -INSERT INTO referencing_table SELECT x, x+501 FROM generate_series(0,1000) AS f(x); -SELECT count(*) FROM referencing_table; - count -------- - 1001 -(1 row) - -DELETE FROM referenced_table WHERE test_column < 700; -SELECT count(*) FROM referencing_table; - count -------- - 301 -(1 row) - -DELETE FROM referenced_table2 WHERE test_column2 > 800; -SELECT count(*) FROM referencing_table; - count -------- - 0 -(1 row) - -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referenced_table2 CASCADE; -NOTICE: drop cascades to constraint foreign_key_2 on table referencing_table -DROP TABLE referencing_table CASCADE; --- check if the above fkeys are created when create_distributed_table is used for 1 foreign key and alter table for the other -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); -CREATE TABLE referenced_table2(test_column int, test_column2 int, PRIMARY KEY(test_column2)); -CREATE TABLE referencing_table(id int, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE); -BEGIN; - SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_reference_table('referenced_table2'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE referencing_table ADD CONSTRAINT foreign_key_2 FOREIGN KEY (ref_id) REFERENCES referenced_table2(test_column2) ON DELETE CASCADE; -COMMIT; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 16 -(1 row) - -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint referencing_table_id_fkey on table referencing_table -DROP TABLE referenced_table2 CASCADE; -NOTICE: drop cascades to constraint foreign_key_2 on table referencing_table -DROP TABLE referencing_table CASCADE; -\set VERBOSITY default --- two distributed tables are referencing to one reference table and --- in the same time the distributed table 2 is referencing to --- distributed table 1. Thus, we have a triangular --- distributed table 1 has a foreign key from the distribution column to reference table --- distributed table 2 has a foreign key from a non-distribution column to reference table --- distributed table 2 has a foreign key to distributed table 1 on the distribution column --- We show that inserts into distributed table 2 will fail if the data does not exist in distributed table 1 --- Delete from reference table cascades to both of the distributed tables properly -CREATE TABLE referenced_table(test_column int, test_column2 int UNIQUE, PRIMARY KEY(test_column)); -CREATE TABLE referencing_table(id int PRIMARY KEY, ref_id int); -CREATE TABLE referencing_table2(id int, ref_id int); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -BEGIN; -SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE; -ALTER TABLE referencing_table2 ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column2) ON DELETE CASCADE; -ALTER TABLE referencing_table2 ADD CONSTRAINT fkey_ref_to_dist FOREIGN KEY (id) REFERENCES referencing_table(id) ON DELETE CASCADE; -COMMIT; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; - name | relid | refd_relid ---------------------------+-------------------------------------------------+------------------------------------------------ - fkey_ref_7000265 | fkey_reference_table.referencing_table_7000265 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000266 | fkey_reference_table.referencing_table_7000266 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000267 | fkey_reference_table.referencing_table_7000267 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000268 | fkey_reference_table.referencing_table_7000268 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000269 | fkey_reference_table.referencing_table_7000269 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000270 | fkey_reference_table.referencing_table_7000270 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000271 | fkey_reference_table.referencing_table_7000271 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000272 | fkey_reference_table.referencing_table_7000272 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000273 | fkey_reference_table.referencing_table2_7000273 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000274 | fkey_reference_table.referencing_table2_7000274 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000275 | fkey_reference_table.referencing_table2_7000275 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000276 | fkey_reference_table.referencing_table2_7000276 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000277 | fkey_reference_table.referencing_table2_7000277 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000278 | fkey_reference_table.referencing_table2_7000278 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000279 | fkey_reference_table.referencing_table2_7000279 | fkey_reference_table.referenced_table_7000264 - fkey_ref_7000280 | fkey_reference_table.referencing_table2_7000280 | fkey_reference_table.referenced_table_7000264 - fkey_ref_to_dist_7000273 | fkey_reference_table.referencing_table2_7000273 | fkey_reference_table.referencing_table_7000265 - fkey_ref_to_dist_7000274 | fkey_reference_table.referencing_table2_7000274 | fkey_reference_table.referencing_table_7000266 - fkey_ref_to_dist_7000275 | fkey_reference_table.referencing_table2_7000275 | fkey_reference_table.referencing_table_7000267 - fkey_ref_to_dist_7000276 | fkey_reference_table.referencing_table2_7000276 | fkey_reference_table.referencing_table_7000268 - fkey_ref_to_dist_7000277 | fkey_reference_table.referencing_table2_7000277 | fkey_reference_table.referencing_table_7000269 - fkey_ref_to_dist_7000278 | fkey_reference_table.referencing_table2_7000278 | fkey_reference_table.referencing_table_7000270 - fkey_ref_to_dist_7000279 | fkey_reference_table.referencing_table2_7000279 | fkey_reference_table.referencing_table_7000271 - fkey_ref_to_dist_7000280 | fkey_reference_table.referencing_table2_7000280 | fkey_reference_table.referencing_table_7000272 -(24 rows) - -INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(0,1000) AS f(x); --- should fail -INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(0,100) AS f(x); -ERROR: insert or update on table "referencing_table2_7000274" violates foreign key constraint "fkey_ref_to_dist_7000274" -DETAIL: Key (id)=(5) is not present in table "referencing_table_7000266". --- should succeed -INSERT INTO referencing_table SELECT x, x+1 FROM generate_series(0,400) AS f(x); --- should fail -INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(200,500) AS f(x); -ERROR: insert or update on table "referencing_table2_7000274" violates foreign key constraint "fkey_ref_to_dist_7000274" -DETAIL: Key (id)=(403) is not present in table "referencing_table_7000266". --- should succeed -INSERT INTO referencing_table2 SELECT x, x+1 FROM generate_series(0,300) AS f(x); -DELETE FROM referenced_table WHERE test_column < 200; -SELECT count(*) FROM referencing_table; - count -------- - 201 -(1 row) - -SELECT count(*) FROM referencing_table2; - count -------- - 101 -(1 row) - -DELETE FROM referencing_table WHERE id > 200; -SELECT count(*) FROM referencing_table2; - count -------- - 1 -(1 row) - -\set VERBOSITY terse -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to 2 other objects -DROP TABLE referencing_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref_to_dist on table referencing_table2 -DROP TABLE referencing_table2 CASCADE; -\set VERBOSITY default --- Check if the above fkeys are created with create_distributed_table -CREATE TABLE referenced_table(test_column int, test_column2 int UNIQUE, PRIMARY KEY(test_column)); -CREATE TABLE referencing_table(id int PRIMARY KEY, ref_id int, FOREIGN KEY (id) REFERENCES referenced_table(test_column) ON DELETE CASCADE); -CREATE TABLE referencing_table2(id int, ref_id int, FOREIGN KEY (ref_id) REFERENCES referenced_table(test_column2) ON DELETE CASCADE, FOREIGN KEY (id) REFERENCES referencing_table(id) ON DELETE CASCADE); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -BEGIN; - SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - - SELECT create_distributed_table('referencing_table2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -COMMIT; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 24 -(1 row) - -\set VERBOSITY terse -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to 2 other objects -DROP TABLE referencing_table CASCADE; -NOTICE: drop cascades to constraint referencing_table2_id_fkey on table referencing_table2 -DROP TABLE referencing_table2 CASCADE; -\set VERBOSITY default --- In this test we have a chained relationship in form of --- distributed table (referencing_referencing_table) has a foreign key with two columns --- to another distributed table (referencing_table) --- referencing_table has another foreign key with 2 columns to referenced_table. --- We will show that a cascading delete on referenced_table reaches to referencing_referencing_table. -CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column, test_column2)); -CREATE TABLE referencing_table(id int, ref_id int, ref_id2 int, PRIMARY KEY(id, ref_id)); -CREATE TABLE referencing_referencing_table(id int, ref_id int, FOREIGN KEY (id, ref_id) REFERENCES referencing_table(id, ref_id) ON DELETE CASCADE); -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_referencing_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE referencing_table ADD CONSTRAINT fkey_ref FOREIGN KEY (ref_id, ref_id2) REFERENCES referenced_table(test_column, test_column2) ON DELETE CASCADE; -SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.referencing%' ORDER BY 1,2,3; - name | relid | refd_relid ------------------------------------------------+------------------------------------------------------------+------------------------------------------------ - fkey_ref_7000299 | fkey_reference_table.referencing_table_7000299 | fkey_reference_table.referenced_table_7000298 - fkey_ref_7000300 | fkey_reference_table.referencing_table_7000300 | fkey_reference_table.referenced_table_7000298 - fkey_ref_7000301 | fkey_reference_table.referencing_table_7000301 | fkey_reference_table.referenced_table_7000298 - fkey_ref_7000302 | fkey_reference_table.referencing_table_7000302 | fkey_reference_table.referenced_table_7000298 - fkey_ref_7000303 | fkey_reference_table.referencing_table_7000303 | fkey_reference_table.referenced_table_7000298 - fkey_ref_7000304 | fkey_reference_table.referencing_table_7000304 | fkey_reference_table.referenced_table_7000298 - fkey_ref_7000305 | fkey_reference_table.referencing_table_7000305 | fkey_reference_table.referenced_table_7000298 - fkey_ref_7000306 | fkey_reference_table.referencing_table_7000306 | fkey_reference_table.referenced_table_7000298 - referencing_referencing_table_id_fkey_7000307 | fkey_reference_table.referencing_referencing_table_7000307 | fkey_reference_table.referencing_table_7000299 - referencing_referencing_table_id_fkey_7000308 | fkey_reference_table.referencing_referencing_table_7000308 | fkey_reference_table.referencing_table_7000300 - referencing_referencing_table_id_fkey_7000309 | fkey_reference_table.referencing_referencing_table_7000309 | fkey_reference_table.referencing_table_7000301 - referencing_referencing_table_id_fkey_7000310 | fkey_reference_table.referencing_referencing_table_7000310 | fkey_reference_table.referencing_table_7000302 - referencing_referencing_table_id_fkey_7000311 | fkey_reference_table.referencing_referencing_table_7000311 | fkey_reference_table.referencing_table_7000303 - referencing_referencing_table_id_fkey_7000312 | fkey_reference_table.referencing_referencing_table_7000312 | fkey_reference_table.referencing_table_7000304 - referencing_referencing_table_id_fkey_7000313 | fkey_reference_table.referencing_referencing_table_7000313 | fkey_reference_table.referencing_table_7000305 - referencing_referencing_table_id_fkey_7000314 | fkey_reference_table.referencing_referencing_table_7000314 | fkey_reference_table.referencing_table_7000306 -(16 rows) - -INSERT INTO referenced_table SELECT x, x+1 FROM generate_series(1,1000) AS f(x); -INSERT INTO referencing_table SELECT x, x+1, x+2 FROM generate_series(1,999) AS f(x); -INSERT INTO referencing_referencing_table SELECT x, x+1 FROM generate_series(1,999) AS f(x); -DELETE FROM referenced_table WHERE test_column > 800; -SELECT max(ref_id) FROM referencing_referencing_table; - max ------ - 800 -(1 row) - -DROP TABLE referenced_table CASCADE; -NOTICE: drop cascades to constraint fkey_ref on table referencing_table -DROP TABLE referencing_table CASCADE; -NOTICE: drop cascades to constraint referencing_referencing_table_id_fkey on table referencing_referencing_table -DROP TABLE referencing_referencing_table; --- test if create_distributed_table works in transactions with some edge cases --- the following checks if create_distributed_table works on foreign keys when --- one of them is a self-referencing table of multiple distributed tables -BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES test_table_1(id)); - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - CREATE TABLE test_table_3(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id), FOREIGN KEY(id) REFERENCES test_table_2(id)); - SELECT create_distributed_table('test_table_3', 'id'); - create_distributed_table --------------------------- - -(1 row) - - DROP TABLE test_table_1 CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to constraint test_table_2_id_fkey on table test_table_2 -drop cascades to constraint test_table_3_value_1_fkey on table test_table_3 -ROLLBACK; --- create_reference_table, create_distributed_table and ALTER TABLE in the same transaction -BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE test_table_2 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_1(id); -ERROR: cannot modify table "test_table_2" because there was a parallel operation on a distributed table in the transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" - DROP TABLE test_table_1, test_table_2; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; --- the order of create_reference_table and create_distributed_table is changed -BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY, value_1 int); - SELECT create_distributed_table('test_table_1', 'id'); - create_distributed_table --------------------------- - -(1 row) - - CREATE TABLE test_table_2(id int PRIMARY KEY); - SELECT create_reference_table('test_table_2'); - create_reference_table ------------------------- - -(1 row) - - ALTER TABLE test_table_1 ADD CONSTRAINT c_check FOREIGN KEY (value_1) REFERENCES test_table_2(id); -ERROR: cannot modify table "test_table_1" because there was a parallel operation on a distributed table in the transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" - DROP TABLE test_table_2 CASCADE; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; --- make sure that we fail if we need parallel data load -BEGIN; - - CREATE TABLE test_table_1(id int PRIMARY KEY); - INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); - INSERT INTO test_table_2 SELECT i, i FROM generate_series(0,100) i; - SELECT create_reference_table('test_table_1'); -NOTICE: Copying data from local table... - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); -ERROR: cannot distribute "test_table_2" in sequential mode because it is not empty -HINT: If you have manually set citus.multi_shard_modify_mode to 'sequential', try with 'parallel' option. If that is not the case, try distributing local tables when they are empty. - DROP TABLE test_table_2, test_table_1; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; --- make sure that other DDLs/DMLs also work fine -BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - CREATE INDEX i1 ON test_table_1(id); - ALTER TABLE test_table_2 ADD CONSTRAINT check_val CHECK (id > 0); - DROP TABLE test_table_2, test_table_1; -COMMIT; --- The following tests check if the DDLs affecting foreign keys work as expected --- check if we can drop the foreign constraint -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 8 -(1 row) - -ALTER TABLE test_table_2 DROP CONSTRAINT test_table_2_value_1_fkey; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; --- check if we can drop the foreign constraint in a transaction right after ADD CONSTRAINT -BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE test_table_2 ADD CONSTRAINT foreign_key FOREIGN KEY(value_1) REFERENCES test_table_1(id); -ERROR: cannot modify table "test_table_2" because there was a parallel operation on a distributed table in the transaction -DETAIL: When there is a foreign key to a reference table, Citus needs to perform all operations over a single connection per node to ensure consistency. -HINT: Try re-running the transaction with "SET LOCAL citus.multi_shard_modify_mode TO 'sequential';" - ALTER TABLE test_table_2 DROP CONSTRAINT test_table_2_value_1_fkey; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; -ERROR: table "test_table_1" does not exist --- check if we can drop the primary key which cascades to the foreign key -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE test_table_1 DROP CONSTRAINT test_table_1_pkey CASCADE; -NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; --- check if we can drop the primary key which cascades to the foreign key in a transaction block -BEGIN; - CREATE TABLE test_table_1(id int PRIMARY KEY); - CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE test_table_1 DROP CONSTRAINT test_table_1_pkey CASCADE; -NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 -COMMIT; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; --- check if we can drop the column which foreign key is referencing from -CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE test_table_2 DROP COLUMN value_1; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; --- check if we can drop the column which foreign key is referencing from in a transaction block -CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -BEGIN; - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE test_table_2 DROP COLUMN value_1; -COMMIT; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; --- check if we can drop the column which foreign key is referencing to -CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE test_table_1 DROP COLUMN id CASCADE; -NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; --- check if we can drop the column which foreign key is referencing from in a transaction block -CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -BEGIN; - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE test_table_1 DROP COLUMN id CASCADE; -NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 -COMMIT; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; --- check if we can alter the column type which foreign key is referencing to -CREATE TABLE test_table_1(id int PRIMARY KEY, id2 int); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO test_table_1 VALUES (1,1), (2,2), (3,3); -INSERT INTO test_table_2 VALUES (1,1), (2,2), (3,3); --- should succeed -ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE bigint; -ALTER TABLE test_table_1 ALTER COLUMN id SET DATA TYPE bigint; -INSERT INTO test_table_1 VALUES (2147483648,4); -INSERT INTO test_table_2 VALUES (4,2147483648); --- should fail since there is a bigint out of integer range > (2^32 - 1) -ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE int; -ERROR: integer out of range -CONTEXT: while executing command on localhost:57637 -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 8 -(1 row) - -DROP TABLE test_table_1 CASCADE; -NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 -DROP TABLE test_table_2; --- check if we can alter the column type and drop it which foreign key is referencing to in a transaction block -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -BEGIN; - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - ALTER TABLE test_table_2 ALTER COLUMN value_1 SET DATA TYPE bigint; - ALTER TABLE test_table_1 DROP COLUMN id CASCADE; -NOTICE: drop cascades to constraint test_table_2_value_1_fkey on table test_table_2 -COMMIT; -SELECT count(*) FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%'; - count -------- - 0 -(1 row) - -DROP TABLE test_table_1, test_table_2; --- check if we can TRUNCATE the referenced table -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); -TRUNCATE test_table_1 CASCADE; -NOTICE: truncate cascades to table "test_table_2" -SELECT * FROM test_table_2; - id | value_1 -----+--------- -(0 rows) - -DROP TABLE test_table_1, test_table_2; --- check if we can TRUNCATE the referenced table in a transaction -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); -BEGIN; - TRUNCATE test_table_1 CASCADE; -NOTICE: truncate cascades to table "test_table_2" -COMMIT; -SELECT * FROM test_table_2; - id | value_1 -----+--------- -(0 rows) - -DROP TABLE test_table_1, test_table_2; --- check if we can TRUNCATE the referenced table in a transaction after inserts -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -BEGIN; - SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - - SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - - INSERT INTO test_table_1 VALUES (1),(2),(3); - INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); - TRUNCATE test_table_1 CASCADE; -NOTICE: truncate cascades to table "test_table_2" -COMMIT; -SELECT * FROM test_table_2; - id | value_1 -----+--------- -(0 rows) - -DROP TABLE test_table_1, test_table_2; --- check if we can TRUNCATE the referencing table -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); -TRUNCATE test_table_2 CASCADE; -SELECT * FROM test_table_2; - id | value_1 -----+--------- -(0 rows) - -SELECT * FROM test_table_1; - id ----- - 1 - 2 - 3 -(3 rows) - -DROP TABLE test_table_1, test_table_2; --- check if we can TRUNCATE the referencing table in a transaction -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); -BEGIN; - TRUNCATE test_table_2 CASCADE; -COMMIT; -SELECT * FROM test_table_2; - id | value_1 -----+--------- -(0 rows) - -SELECT * FROM test_table_1; - id ----- - 1 - 2 - 3 -(3 rows) - -DROP TABLE test_table_1, test_table_2; --- check if we successfuly set multi_shard_modify_mode to sequential after sequentially running DDLs --- in transaction since the upcoming DDLs need to run sequentially. -CREATE TABLE test_table_1(id int PRIMARY KEY); -CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); -CREATE TABLE test_table_3(id int PRIMARY KEY, value_1 int); -SELECT create_reference_table('test_table_1'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT create_distributed_table('test_table_3', 'id'); - create_distributed_table --------------------------- - -(1 row) - -BEGIN; - ALTER TABLE test_table_2 ADD CONSTRAINT fkey FOREIGN KEY (value_1) REFERENCES test_table_1(id); - ALTER TABLE test_table_3 ADD COLUMN test_column int; - ALTER TABLE test_table_1 DROP COLUMN id CASCADE; -NOTICE: drop cascades to constraint fkey on table test_table_2 - ALTER TABLE test_table_1 ADD COLUMN id int; -COMMIT; -DROP TABLE test_table_1, test_table_2, test_table_3; --- NOTE: Postgres does not support foreign keys on partitioned tables currently. --- However, we can create foreign keys to/from the partitions themselves. --- The following tests chech if we create the foreign constraints in partitions properly. -CREATE TABLE referenced_table(id int PRIMARY KEY, test_column int); -CREATE TABLE referencing_table(id int, value_1 int) PARTITION BY RANGE (value_1); -ERROR: syntax error at or near "PARTITION" -LINE 1: ...EATE TABLE referencing_table(id int, value_1 int) PARTITION ... - ^ -CREATE TABLE referencing_table_0 PARTITION OF referencing_table FOR VALUES FROM (0) TO (2); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE referencing_table_0 PARTITION OF referencing_ta... - ^ -CREATE TABLE referencing_table_2 PARTITION OF referencing_table FOR VALUES FROM (2) TO (4); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE referencing_table_2 PARTITION OF referencing_ta... - ^ -CREATE TABLE referencing_table_4 PARTITION OF referencing_table FOR VALUES FROM (4) TO (6); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE referencing_table_4 PARTITION OF referencing_ta... - ^ --- partitioned tables are not supported as reference tables -select create_reference_table('referencing_table'); -ERROR: relation "referencing_table" does not exist -LINE 1: select create_reference_table('referencing_table'); - ^ --- partitioned tables are supported as hash distributed table -SELECT create_reference_table('referenced_table'); - create_reference_table ------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_table', 'id'); -ERROR: relation "referencing_table" does not exist -LINE 1: SELECT create_distributed_table('referencing_table', 'id'); - ^ --- add foreign constraints in between partitions -ALTER TABLE referencing_table_0 ADD CONSTRAINT pkey PRIMARY KEY (id); -ERROR: relation "referencing_table_0" does not exist -ALTER TABLE referencing_table_4 ADD CONSTRAINT fkey FOREIGN KEY (id) REFERENCES referencing_table_0; -ERROR: relation "referencing_table_4" does not exist --- add foreign constraint from a partition to reference table -ALTER TABLE referencing_table_4 ADD CONSTRAINT fkey_to_ref FOREIGN KEY (value_1) REFERENCES referenced_table; -ERROR: relation "referencing_table_4" does not exist --- should fail since the data will flow to partitioning_test_4 and it has a foreign constraint to partitioning_test_0 on id column -INSERT INTO referencing_table VALUES (0, 5); -ERROR: relation "referencing_table" does not exist -LINE 1: INSERT INTO referencing_table VALUES (0, 5); - ^ --- should succeed on partitioning_test_0 -INSERT INTO referencing_table VALUES (0, 1); -ERROR: relation "referencing_table" does not exist -LINE 1: INSERT INTO referencing_table VALUES (0, 1); - ^ -SELECT * FROM referencing_table; -ERROR: relation "referencing_table" does not exist -LINE 1: SELECT * FROM referencing_table; - ^ --- should fail since partitioning_test_4 has foreign constraint to referenced_table on value_1 column -INSERT INTO referencing_table VALUES (0, 5); -ERROR: relation "referencing_table" does not exist -LINE 1: INSERT INTO referencing_table VALUES (0, 5); - ^ -INSERT INTO referenced_table VALUES(5,5); --- should succeed since both of the foreign constraints are positive -INSERT INTO referencing_table VALUES (0, 5); -ERROR: relation "referencing_table" does not exist -LINE 1: INSERT INTO referencing_table VALUES (0, 5); - ^ --- TRUNCATE should work in any way -TRUNCATE referencing_table, referenced_table; -ERROR: relation "referencing_table" does not exist -TRUNCATE referenced_table, referencing_table; -ERROR: relation "referencing_table" does not exist -BEGIN; - TRUNCATE referencing_table, referenced_table; -ERROR: relation "referencing_table" does not exist - ALTER TABLE referencing_table ADD COLUMN x INT; -ERROR: current transaction is aborted, commands ignored until end of transaction block - SELECT * FROM referencing_table; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; -BEGIN; - TRUNCATE referenced_table, referencing_table; -ERROR: relation "referencing_table" does not exist - ALTER TABLE referencing_table ADD COLUMN x INT; -ERROR: current transaction is aborted, commands ignored until end of transaction block - SELECT * FROM referencing_table; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ROLLBACK; -DROP TABLE referenced_table CASCADE; -DROP TABLE referencing_table; -ERROR: table "referencing_table" does not exist -DROP SCHEMA fkey_reference_table CASCADE; -NOTICE: drop cascades to 3 other objects -DETAIL: drop cascades to type foreign_details -drop cascades to view table_fkeys_in_workers -drop cascades to type composite -SET search_path TO DEFAULT; diff --git a/src/test/regress/expected/isolation_drop_vs_all_0.out b/src/test/regress/expected/isolation_drop_vs_all_0.out deleted file mode 100644 index 3ecd51940..000000000 --- a/src/test/regress/expected/isolation_drop_vs_all_0.out +++ /dev/null @@ -1,400 +0,0 @@ -Parsed test spec with 2 sessions - -starting permutation: s1-initialize s1-begin s2-begin s1-drop s2-drop s1-commit s2-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-drop: <... completed> -error in steps s1-commit s2-drop: ERROR: table "drop_hash" does not exist -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-drop s2-ddl-create-index s1-commit s2-commit s1-select-count s1-show-indexes -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-ddl-create-index: CREATE INDEX drop_hash_index ON drop_hash(id); -step s1-commit: COMMIT; -step s2-ddl-create-index: <... completed> -error in steps s1-commit s2-ddl-create-index: ERROR: relation "drop_hash" does not exist -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-indexes: SELECT run_command_on_workers('SELECT COUNT(*) FROM pg_indexes WHERE tablename LIKE ''drop_hash%'''); -run_command_on_workers - -(localhost,57637,t,0) -(localhost,57638,t,0) -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-ddl-create-index s1-begin s2-begin s1-drop s2-ddl-drop-index s1-commit s2-commit s1-select-count s1-show-indexes -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-ddl-create-index: CREATE INDEX drop_hash_index ON drop_hash(id); -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-ddl-drop-index: DROP INDEX drop_hash_index; -step s1-commit: COMMIT; -step s2-ddl-drop-index: <... completed> -error in steps s1-commit s2-ddl-drop-index: ERROR: index "drop_hash_index" does not exist -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-indexes: SELECT run_command_on_workers('SELECT COUNT(*) FROM pg_indexes WHERE tablename LIKE ''drop_hash%'''); -run_command_on_workers - -(localhost,57637,t,0) -(localhost,57638,t,0) -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s1-drop s2-ddl-create-index-concurrently s1-commit s1-select-count s1-show-indexes -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-ddl-create-index-concurrently: CREATE INDEX CONCURRENTLY drop_hash_index ON drop_hash(id); -step s1-commit: COMMIT; -step s2-ddl-create-index-concurrently: <... completed> -error in steps s1-commit s2-ddl-create-index-concurrently: ERROR: relation "drop_hash" does not exist -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-indexes: SELECT run_command_on_workers('SELECT COUNT(*) FROM pg_indexes WHERE tablename LIKE ''drop_hash%'''); -run_command_on_workers - -(localhost,57637,t,0) -(localhost,57638,t,0) -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-drop s2-ddl-add-column s1-commit s2-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-ddl-add-column: ALTER TABLE drop_hash ADD new_column int DEFAULT 0; -step s1-commit: COMMIT; -step s2-ddl-add-column: <... completed> -error in steps s1-commit s2-ddl-add-column: ERROR: relation "drop_hash" does not exist -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''ddl_hash%'' AND column_name = ''drop_hash'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,"") -(localhost,57638,t,"") -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-ddl-add-column s1-begin s2-begin s1-drop s2-ddl-drop-column s1-commit s2-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-ddl-add-column: ALTER TABLE drop_hash ADD new_column int DEFAULT 0; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-ddl-drop-column: ALTER TABLE drop_hash DROP new_column; -step s1-commit: COMMIT; -step s2-ddl-drop-column: <... completed> -error in steps s1-commit s2-ddl-drop-column: ERROR: relation "drop_hash" does not exist -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''ddl_hash%'' AND column_name = ''drop_hash'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,"") -(localhost,57638,t,"") -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-drop s2-ddl-rename-column s1-commit s2-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-ddl-rename-column: ALTER TABLE drop_hash RENAME data TO new_column; -step s1-commit: COMMIT; -step s2-ddl-rename-column: <... completed> -error in steps s1-commit s2-ddl-rename-column: ERROR: relation "drop_hash" does not exist -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''ddl_hash%'' AND column_name = ''drop_hash'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,"") -(localhost,57638,t,"") -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-drop s2-table-size s1-commit s2-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-table-size: SELECT citus_total_relation_size('drop_hash'); -step s1-commit: COMMIT; -step s2-table-size: <... completed> -error in steps s1-commit s2-table-size: ERROR: could not compute table size: relation does not exist -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-drop s2-master-modify-multiple-shards s1-commit s2-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-master-modify-multiple-shards: SELECT master_modify_multiple_shards('DROP FROM drop_hash;'); -ERROR: syntax error at or near "FROM" -step s1-commit: COMMIT; -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -restore_isolation_tester_func - - - -starting permutation: s1-drop s1-create-non-distributed-table s1-initialize s1-begin s2-begin s1-drop s2-distribute-table s1-commit s2-commit s1-select-count -create_distributed_table - - -step s1-drop: DROP TABLE drop_hash; -step s1-create-non-distributed-table: CREATE TABLE drop_hash(id integer, data text); COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-drop: DROP TABLE drop_hash; -step s2-distribute-table: SELECT create_distributed_table('drop_hash', 'id'); -step s1-commit: COMMIT; -step s2-distribute-table: <... completed> -error in steps s1-commit s2-distribute-table: ERROR: could not create distributed table: relation does not exist -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-ddl-create-index s2-drop s1-commit s2-commit s1-select-count s1-show-indexes -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-ddl-create-index: CREATE INDEX drop_hash_index ON drop_hash(id); -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-drop: <... completed> -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-indexes: SELECT run_command_on_workers('SELECT COUNT(*) FROM pg_indexes WHERE tablename LIKE ''drop_hash%'''); -run_command_on_workers - -(localhost,57637,t,0) -(localhost,57638,t,0) -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-ddl-create-index s1-begin s2-begin s1-ddl-drop-index s2-drop s1-commit s2-commit s1-select-count s1-show-indexes -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-ddl-create-index: CREATE INDEX drop_hash_index ON drop_hash(id); -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-ddl-drop-index: DROP INDEX drop_hash_index; -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-drop: <... completed> -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-indexes: SELECT run_command_on_workers('SELECT COUNT(*) FROM pg_indexes WHERE tablename LIKE ''drop_hash%'''); -run_command_on_workers - -(localhost,57637,t,0) -(localhost,57638,t,0) -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-ddl-add-column s2-drop s1-commit s2-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-ddl-add-column: ALTER TABLE drop_hash ADD new_column int DEFAULT 0; -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-drop: <... completed> -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''ddl_hash%'' AND column_name = ''drop_hash'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,"") -(localhost,57638,t,"") -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-ddl-add-column s1-begin s2-begin s1-ddl-drop-column s2-drop s1-commit s2-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-ddl-add-column: ALTER TABLE drop_hash ADD new_column int DEFAULT 0; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-ddl-drop-column: ALTER TABLE drop_hash DROP new_column; -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-drop: <... completed> -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''ddl_hash%'' AND column_name = ''drop_hash'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,"") -(localhost,57638,t,"") -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-ddl-rename-column s2-drop s1-commit s2-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-ddl-rename-column: ALTER TABLE drop_hash RENAME data TO new_column; -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-drop: <... completed> -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''ddl_hash%'' AND column_name = ''drop_hash'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,"") -(localhost,57638,t,"") -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-table-size s2-drop s1-commit s2-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-table-size: SELECT citus_total_relation_size('drop_hash'); -citus_total_relation_size - -57344 -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -restore_isolation_tester_func - - - -starting permutation: s1-initialize s1-begin s2-begin s1-master-modify-multiple-shards s2-drop s1-commit s2-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-master-modify-multiple-shards: SELECT master_modify_multiple_shards('DROP FROM drop_hash;'); -ERROR: syntax error at or near "FROM" -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -restore_isolation_tester_func - - - -starting permutation: s1-drop s1-create-non-distributed-table s1-initialize s1-begin s2-begin s1-distribute-table s2-drop s1-commit s2-commit s1-select-count -create_distributed_table - - -step s1-drop: DROP TABLE drop_hash; -step s1-create-non-distributed-table: CREATE TABLE drop_hash(id integer, data text); COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-initialize: COPY drop_hash FROM PROGRAM 'echo 0, a && echo 1, b && echo 2, c && echo 3, d && echo 4, e' WITH CSV; -step s1-begin: BEGIN; -step s2-begin: BEGIN; -step s1-distribute-table: SELECT create_distributed_table('drop_hash', 'id'); -create_distributed_table - - -step s2-drop: DROP TABLE drop_hash; -step s1-commit: COMMIT; -step s2-drop: <... completed> -step s2-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM drop_hash; -ERROR: relation "drop_hash" does not exist -restore_isolation_tester_func - - diff --git a/src/test/regress/expected/isolation_partitioned_copy_vs_all_0.out b/src/test/regress/expected/isolation_partitioned_copy_vs_all_0.out deleted file mode 100644 index 63291f178..000000000 --- a/src/test/regress/expected/isolation_partitioned_copy_vs_all_0.out +++ /dev/null @@ -1,570 +0,0 @@ -Parsed test spec with 2 sessions - -starting permutation: s1-initialize s1-begin s1-copy s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -15 - -starting permutation: s1-initialize s1-begin s1-copy s2-router-select s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-router-select: SELECT * FROM partitioned_copy WHERE id = 1; -id data int_data - -1 b 1 -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-copy s2-real-time-select s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-real-time-select: SELECT * FROM partitioned_copy ORDER BY 1, 2; -id data int_data - -0 a 0 -1 b 1 -2 c 2 -3 d 3 -4 e 4 -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-copy s2-task-tracker-select s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-task-tracker-select: - SET citus.task_executor_type TO "task-tracker"; - SELECT * FROM partitioned_copy AS t1 JOIN partitioned_copy AS t2 ON t1.id = t2.int_data ORDER BY 1, 2, 3, 4; - -id data int_data id data int_data - -0 a 0 0 a 0 -1 b 1 1 b 1 -2 c 2 2 c 2 -3 d 3 3 d 3 -4 e 4 4 e 4 -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-copy s2-insert s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-insert: INSERT INTO partitioned_copy VALUES(0, 'k', 0); -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -11 - -starting permutation: s1-initialize s1-begin s1-copy s2-insert-select s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-insert-select: INSERT INTO partitioned_copy SELECT * FROM partitioned_copy; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -15 - -starting permutation: s1-initialize s1-begin s1-copy s2-update s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-update: UPDATE partitioned_copy SET data = 'l' WHERE id = 0; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-copy s2-delete s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-delete: DELETE FROM partitioned_copy WHERE id = 1; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -9 - -starting permutation: s1-initialize s1-begin s1-copy s2-truncate s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-truncate: TRUNCATE partitioned_copy; -step s1-commit: COMMIT; -step s2-truncate: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -0 - -starting permutation: s1-initialize s1-begin s1-copy s2-drop s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-drop: DROP TABLE partitioned_copy; -step s1-commit: COMMIT; -step s2-drop: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -ERROR: relation "partitioned_copy" does not exist - -starting permutation: s1-initialize s1-begin s1-copy s2-ddl-add-column s1-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-ddl-add-column: ALTER TABLE partitioned_copy ADD new_column int DEFAULT 0; -step s1-commit: COMMIT; -step s2-ddl-add-column: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''partitioned_copy%'' AND column_name = ''new_column'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,new_column) -(localhost,57638,t,new_column) - -starting permutation: s1-initialize s1-ddl-add-column s1-begin s1-copy-additional-column s2-ddl-drop-column s1-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-ddl-add-column: ALTER TABLE partitioned_copy ADD new_column int DEFAULT 0; -step s1-begin: BEGIN; -step s1-copy-additional-column: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5, 5 && echo 6, g, 6, 6 && echo 7, h, 7, 7 && echo 8, i, 8, 8 && echo 9, j, 9, 9' WITH CSV; -step s2-ddl-drop-column: ALTER TABLE partitioned_copy DROP new_column; -step s1-commit: COMMIT; -step s2-ddl-drop-column: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''partitioned_copy%'' AND column_name = ''new_column'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,"") -(localhost,57638,t,"") - -starting permutation: s1-initialize s1-begin s1-copy s2-ddl-rename-column s1-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-ddl-rename-column: ALTER TABLE partitioned_copy RENAME data TO new_column; -step s1-commit: COMMIT; -step s2-ddl-rename-column: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''partitioned_copy%'' AND column_name = ''new_column'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,new_column) -(localhost,57638,t,new_column) - -starting permutation: s1-initialize s1-begin s1-copy s2-table-size s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-table-size: SELECT citus_total_relation_size('partitioned_copy'); -citus_total_relation_size - -32768 -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-copy s2-master-modify-multiple-shards s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-master-modify-multiple-shards: DELETE FROM partitioned_copy; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -5 - -starting permutation: s1-initialize s1-begin s1-copy s2-master-drop-all-shards s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-master-drop-all-shards: SELECT master_drop_all_shards('partitioned_copy'::regclass, 'public', 'partitioned_copy'); -step s1-commit: COMMIT; -step s2-master-drop-all-shards: <... completed> -master_drop_all_shards - -4 -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -0 - -starting permutation: s1-drop s1-create-non-distributed-table s1-initialize s1-begin s1-copy s2-distribute-table s1-commit s1-select-count -create_distributed_table - - -step s1-drop: DROP TABLE partitioned_copy; -step s1-create-non-distributed-table: CREATE TABLE partitioned_copy(id integer, data text, int_data int); COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s2-distribute-table: SELECT create_distributed_table('partitioned_copy', 'id'); -step s1-commit: COMMIT; -step s2-distribute-table: <... completed> -create_distributed_table - - -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -15 - -starting permutation: s1-initialize s1-begin s1-router-select s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-router-select: SELECT * FROM partitioned_copy WHERE id = 1; -id data int_data - -1 b 1 -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-real-time-select s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-real-time-select: SELECT * FROM partitioned_copy ORDER BY 1, 2; -id data int_data - -0 a 0 -1 b 1 -2 c 2 -3 d 3 -4 e 4 -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-task-tracker-select s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-task-tracker-select: - SET citus.task_executor_type TO "task-tracker"; - SELECT * FROM partitioned_copy AS t1 JOIN partitioned_copy AS t2 ON t1.id = t2.int_data ORDER BY 1, 2, 3, 4; - -id data int_data id data int_data - -0 a 0 0 a 0 -1 b 1 1 b 1 -2 c 2 2 c 2 -3 d 3 3 d 3 -4 e 4 4 e 4 -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-insert s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-insert: INSERT INTO partitioned_copy VALUES(0, 'k', 0); -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -11 - -starting permutation: s1-initialize s1-begin s1-insert-select s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-insert-select: INSERT INTO partitioned_copy SELECT * FROM partitioned_copy; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -15 - -starting permutation: s1-initialize s1-begin s1-update s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-update: UPDATE partitioned_copy SET data = 'l' WHERE id = 0; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-delete s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-delete: DELETE FROM partitioned_copy WHERE id = 1; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -9 - -starting permutation: s1-initialize s1-begin s1-truncate s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-truncate: TRUNCATE partitioned_copy; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s2-copy: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -5 - -starting permutation: s1-initialize s1-begin s1-drop s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-drop: DROP TABLE partitioned_copy; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s2-copy: <... completed> -error in steps s1-commit s2-copy: ERROR: relation "partitioned_copy" does not exist -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -ERROR: relation "partitioned_copy" does not exist - -starting permutation: s1-initialize s1-begin s1-ddl-add-column s2-copy s1-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-ddl-add-column: ALTER TABLE partitioned_copy ADD new_column int DEFAULT 0; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s2-copy: <... completed> -error in steps s1-commit s2-copy: ERROR: missing data for column "new_column" -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -5 -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''partitioned_copy%'' AND column_name = ''new_column'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,new_column) -(localhost,57638,t,new_column) - -starting permutation: s1-initialize s1-ddl-add-column s1-begin s1-ddl-drop-column s2-copy s1-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-ddl-add-column: ALTER TABLE partitioned_copy ADD new_column int DEFAULT 0; -step s1-begin: BEGIN; -step s1-ddl-drop-column: ALTER TABLE partitioned_copy DROP new_column; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s2-copy: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''partitioned_copy%'' AND column_name = ''new_column'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,"") -(localhost,57638,t,"") - -starting permutation: s1-initialize s1-begin s1-ddl-rename-column s2-copy s1-commit s1-select-count s1-show-columns -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-ddl-rename-column: ALTER TABLE partitioned_copy RENAME data TO new_column; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s2-copy: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 -step s1-show-columns: SELECT run_command_on_workers('SELECT column_name FROM information_schema.columns WHERE table_name LIKE ''partitioned_copy%'' AND column_name = ''new_column'' ORDER BY 1 LIMIT 1'); -run_command_on_workers - -(localhost,57637,t,new_column) -(localhost,57638,t,new_column) - -starting permutation: s1-initialize s1-begin s1-table-size s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-table-size: SELECT citus_total_relation_size('partitioned_copy'); -citus_total_relation_size - -32768 -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -10 - -starting permutation: s1-initialize s1-begin s1-master-modify-multiple-shards s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-master-modify-multiple-shards: DELETE FROM partitioned_copy; -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -5 - -starting permutation: s1-initialize s1-begin s1-master-drop-all-shards s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-master-drop-all-shards: SELECT master_drop_all_shards('partitioned_copy'::regclass, 'public', 'partitioned_copy'); -master_drop_all_shards - -4 -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s2-copy: <... completed> -error in steps s1-commit s2-copy: ERROR: could not find any shards into which to copy -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -0 - -starting permutation: s1-drop s1-create-non-distributed-table s1-initialize s1-begin s1-distribute-table s2-copy s1-commit s1-select-count -create_distributed_table - - -step s1-drop: DROP TABLE partitioned_copy; -step s1-create-non-distributed-table: CREATE TABLE partitioned_copy(id integer, data text, int_data int); COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-initialize: COPY partitioned_copy FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH CSV; -step s1-begin: BEGIN; -step s1-distribute-table: SELECT create_distributed_table('partitioned_copy', 'id'); -create_distributed_table - - -step s2-copy: COPY partitioned_copy FROM PROGRAM 'echo 5, f, 5 && echo 6, g, 6 && echo 7, h, 7 && echo 8, i, 8 && echo 9, j, 9' WITH CSV; -step s1-commit: COMMIT; -step s2-copy: <... completed> -step s1-select-count: SELECT COUNT(*) FROM partitioned_copy; -count - -15 diff --git a/src/test/regress/expected/local_shard_execution_0.out b/src/test/regress/expected/local_shard_execution_0.out deleted file mode 100644 index 2b67c40fc..000000000 --- a/src/test/regress/expected/local_shard_execution_0.out +++ /dev/null @@ -1,1123 +0,0 @@ -CREATE SCHEMA local_shard_execution; -SET search_path TO local_shard_execution; -SET citus.shard_count TO 4; -SET citus.shard_replication_factor TO 1; -SET citus.replication_model TO 'streaming'; -SET citus.next_shard_id TO 1470000; -CREATE TABLE reference_table (key int PRIMARY KEY); -SELECT create_reference_table('reference_table'); - create_reference_table ------------------------- - -(1 row) - -CREATE TABLE distributed_table (key int PRIMARY KEY , value text, age bigint CHECK (age > 10), FOREIGN KEY (key) REFERENCES reference_table(key) ON DELETE CASCADE); -SELECT create_distributed_table('distributed_table','key'); - create_distributed_table --------------------------- - -(1 row) - -CREATE TABLE second_distributed_table (key int PRIMARY KEY , value text, FOREIGN KEY (key) REFERENCES distributed_table(key) ON DELETE CASCADE); -SELECT create_distributed_table('second_distributed_table','key'); - create_distributed_table --------------------------- - -(1 row) - --- ingest some data to enable some tests with data -INSERT INTO reference_table VALUES (1); -INSERT INTO distributed_table VALUES (1, '1', 20); -INSERT INTO second_distributed_table VALUES (1, '1'); --- a simple test for -CREATE TABLE collections_list ( - key bigserial, - ser bigserial, - ts timestamptz, - collection_id integer, - value numeric, - PRIMARY KEY(key, collection_id) -) PARTITION BY LIST (collection_id ); -ERROR: primary key constraints are not supported on partitioned tables -LINE 7: PRIMARY KEY(key, collection_id) - ^ -SELECT create_distributed_table('collections_list', 'key'); -ERROR: relation "collections_list" does not exist -LINE 1: SELECT create_distributed_table('collections_list', 'key'); - ^ -CREATE TABLE collections_list_0 - PARTITION OF collections_list (key, ser, ts, collection_id, value) - FOR VALUES IN ( 0 ); -ERROR: relation "collections_list" does not exist --- connection worker and get ready for the tests -\c - - - :worker_1_port -SET search_path TO local_shard_execution; --- returns true of the distribution key filter --- on the distributed tables (e.g., WHERE key = 1), we'll hit a shard --- placement which is local to this not -CREATE OR REPLACE FUNCTION shard_of_distribution_column_is_local(dist_key int) RETURNS bool AS $$ - - DECLARE shard_is_local BOOLEAN := FALSE; - - BEGIN - - WITH local_shard_ids AS (SELECT get_shard_id_for_distribution_column('local_shard_execution.distributed_table', dist_key)), - all_local_shard_ids_on_node AS (SELECT shardid FROM pg_dist_placement WHERE groupid IN (SELECT groupid FROM pg_dist_local_group)) - SELECT - true INTO shard_is_local - FROM - local_shard_ids - WHERE - get_shard_id_for_distribution_column IN (SELECT * FROM all_local_shard_ids_on_node); - - IF shard_is_local IS NULL THEN - shard_is_local = FALSE; - END IF; - - RETURN shard_is_local; - END; -$$ LANGUAGE plpgsql; --- pick some example values that reside on the shards locally and remote --- distribution key values of 1,6, 500 and 701 are LOCAL to shards, --- we'll use these values in the tests -SELECT shard_of_distribution_column_is_local(1); - shard_of_distribution_column_is_local ---------------------------------------- - t -(1 row) - -SELECT shard_of_distribution_column_is_local(6); - shard_of_distribution_column_is_local ---------------------------------------- - t -(1 row) - -SELECT shard_of_distribution_column_is_local(500); - shard_of_distribution_column_is_local ---------------------------------------- - t -(1 row) - -SELECT shard_of_distribution_column_is_local(701); - shard_of_distribution_column_is_local ---------------------------------------- - t -(1 row) - --- distribution key values of 11 and 12 are REMOTE to shards -SELECT shard_of_distribution_column_is_local(11); - shard_of_distribution_column_is_local ---------------------------------------- - f -(1 row) - -SELECT shard_of_distribution_column_is_local(12); - shard_of_distribution_column_is_local ---------------------------------------- - f -(1 row) - ---- enable logging to see which tasks are executed locally -SET client_min_messages TO LOG; -SET citus.log_local_commands TO ON; --- first, make sure that local execution works fine --- with simple queries that are not in transcation blocks -SELECT count(*) FROM distributed_table WHERE key = 1; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - --- multiple tasks both of which are local should NOT use local execution --- because local execution means executing the tasks locally, so the executor --- favors parallel execution even if everyting is local to node -SELECT count(*) FROM distributed_table WHERE key IN (1,6); - count -------- - 1 -(1 row) - --- queries that hit any remote shards should NOT use local execution -SELECT count(*) FROM distributed_table WHERE key IN (1,11); - count -------- - 1 -(1 row) - -SELECT count(*) FROM distributed_table; - count -------- - 1 -(1 row) - --- modifications also follow the same rules -INSERT INTO reference_table VALUES (1) ON CONFLICT DO NOTHING; -LOG: executing the command locally: INSERT INTO local_shard_execution.reference_table_1470000 AS citus_table_alias (key) VALUES (1) ON CONFLICT DO NOTHING -INSERT INTO distributed_table VALUES (1, '1', 21) ON CONFLICT DO NOTHING; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '1'::text, 21) ON CONFLICT DO NOTHING --- local query -DELETE FROM distributed_table WHERE key = 1 AND age = 21; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((key OPERATOR(pg_catalog.=) 1) AND (age OPERATOR(pg_catalog.=) 21)) --- hitting multiple shards, so should be a distributed execution -DELETE FROM distributed_table WHERE age = 21; --- although both shards are local, the executor choose the parallel execution --- over the wire because as noted above local execution is sequential -DELETE FROM second_distributed_table WHERE key IN (1,6); --- similarly, any multi-shard query just follows distributed execution -DELETE FROM second_distributed_table; --- load some more data for the following tests -INSERT INTO second_distributed_table VALUES (1, '1'); -LOG: executing the command locally: INSERT INTO local_shard_execution.second_distributed_table_1470005 (key, value) VALUES (1, '1'::text) --- INSERT .. SELECT hitting a single single (co-located) shard(s) should --- be executed locally -INSERT INTO distributed_table -SELECT - distributed_table.* -FROM - distributed_table, second_distributed_table -WHERE - distributed_table.key = 1 and distributed_table.key=second_distributed_table.key -ON CONFLICT(key) DO UPDATE SET value = '22' -RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) SELECT distributed_table.key, distributed_table.value, distributed_table.age FROM local_shard_execution.distributed_table_1470001 distributed_table, local_shard_execution.second_distributed_table_1470005 second_distributed_table WHERE (((distributed_table.key OPERATOR(pg_catalog.=) 1) AND (distributed_table.key OPERATOR(pg_catalog.=) second_distributed_table.key)) AND ((worker_hash(distributed_table.key) OPERATOR(pg_catalog.>=) '-2147483648'::integer) AND (worker_hash(distributed_table.key) OPERATOR(pg_catalog.<=) '-1073741825'::integer))) ON CONFLICT(key) DO UPDATE SET value = '22'::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age - key | value | age ------+-------+----- - 1 | 22 | 20 -(1 row) - --- INSERT .. SELECT hitting multi-shards should go thourgh distributed execution -INSERT INTO distributed_table -SELECT - distributed_table.* -FROM - distributed_table, second_distributed_table -WHERE - distributed_table.key != 1 and distributed_table.key=second_distributed_table.key -ON CONFLICT(key) DO UPDATE SET value = '22' -RETURNING *; - key | value | age ------+-------+----- -(0 rows) - --- INSERT..SELECT via coordinator consists of two steps, select + COPY --- that's why it is disallowed to use local execution even if the SELECT --- can be executed locally -INSERT INTO distributed_table SELECT * FROM distributed_table WHERE key = 1 OFFSET 0 ON CONFLICT DO NOTHING; -INSERT INTO distributed_table SELECT 1, '1',15 FROM distributed_table WHERE key = 2 LIMIT 1 ON CONFLICT DO NOTHING; --- sanity check: multi-shard INSERT..SELECT pushdown goes through distributed execution -INSERT INTO distributed_table SELECT * FROM distributed_table ON CONFLICT DO NOTHING; --- EXPLAIN for local execution just works fine --- though going through distributed execution -EXPLAIN (COSTS OFF) SELECT * FROM distributed_table WHERE key = 1 AND age = 20; - QUERY PLAN ------------------------------------------------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 1 - Tasks Shown: All - -> Task - Node: host=localhost port=57637 dbname=regression - -> Index Scan using distributed_table_pkey_1470001 on distributed_table_1470001 distributed_table - Index Cond: (key = 1) - Filter: (age = 20) -(8 rows) - --- TODO: Fix #2922 --- EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) SELECT * FROM distributed_table WHERE key = 1 AND age = 20; -EXPLAIN (COSTS OFF) DELETE FROM distributed_table WHERE key = 1 AND age = 20; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 1 - Tasks Shown: All - -> Task - Node: host=localhost port=57637 dbname=regression - -> Delete on distributed_table_1470001 distributed_table - -> Index Scan using distributed_table_pkey_1470001 on distributed_table_1470001 distributed_table - Index Cond: (key = 1) - Filter: (age = 20) -(9 rows) - --- TODO: Fix #2922 --- EXPLAIN ANALYZE DELETE FROM distributed_table WHERE key = 1 AND age = 20; --- show that EXPLAIN ANALYZE deleted the row -SELECT * FROM distributed_table WHERE key = 1 AND age = 20 ORDER BY 1,2,3; -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((key OPERATOR(pg_catalog.=) 1) AND (age OPERATOR(pg_catalog.=) 20)) ORDER BY key, value, age - key | value | age ------+-------+----- - 1 | 22 | 20 -(1 row) - --- copy always happens via distributed execution irrespective of the --- shards that are accessed -COPY reference_table FROM STDIN; -COPY distributed_table FROM STDIN WITH CSV; -COPY second_distributed_table FROM STDIN WITH CSV; --- the behaviour in transaction blocks is the following: - -- (a) Unless the first query is a local query, always use distributed execution. - -- (b) If the executor has used local execution, it has to use local execution - -- for the remaining of the transaction block. If that's not possible, the - -- executor has to error out (e.g., TRUNCATE is a utility command and we - -- currently do not support local execution of utility commands) --- rollback should be able to rollback local execution -BEGIN; - INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '29' RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '11'::text, 21) ON CONFLICT(key) DO UPDATE SET value = '29'::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age - key | value | age ------+-------+----- - 1 | 29 | 20 -(1 row) - - SELECT * FROM distributed_table WHERE key = 1 ORDER BY 1,2,3; -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) ORDER BY key, value, age - key | value | age ------+-------+----- - 1 | 29 | 20 -(1 row) - -ROLLBACK; --- make sure that the value is rollbacked -SELECT * FROM distributed_table WHERE key = 1 ORDER BY 1,2,3; -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) ORDER BY key, value, age - key | value | age ------+-------+----- - 1 | 22 | 20 -(1 row) - --- rollback should be able to rollback both the local and distributed executions -BEGIN; - INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '29' RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '11'::text, 21) ON CONFLICT(key) DO UPDATE SET value = '29'::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age - key | value | age ------+-------+----- - 1 | 29 | 20 -(1 row) - - DELETE FROM distributed_table; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470001 distributed_table -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table - -- DELETE should cascade, and we should not see any rows - SELECT count(*) FROM second_distributed_table; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.second_distributed_table_1470005 second_distributed_table WHERE true -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.second_distributed_table_1470007 second_distributed_table WHERE true - count -------- - 0 -(1 row) - -ROLLBACK; --- make sure that everything is rollbacked -SELECT * FROM distributed_table WHERE key = 1 ORDER BY 1,2,3; -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) ORDER BY key, value, age - key | value | age ------+-------+----- - 1 | 22 | 20 -(1 row) - -SELECT count(*) FROM second_distributed_table; - count -------- - 2 -(1 row) - --- very simple examples, an SELECTs should see the modifications --- that has done before -BEGIN; - -- INSERT is executed locally - INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '23' RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '11'::text, 21) ON CONFLICT(key) DO UPDATE SET value = '23'::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age - key | value | age ------+-------+----- - 1 | 23 | 20 -(1 row) - - -- since the INSERT is executed locally, the SELECT should also be - -- executed locally and see the changes - SELECT * FROM distributed_table WHERE key = 1 ORDER BY 1,2,3; -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) ORDER BY key, value, age - key | value | age ------+-------+----- - 1 | 23 | 20 -(1 row) - - -- multi-shard SELECTs are now forced to use local execution on - -- the shards that reside on this node - SELECT * FROM distributed_table WHERE value = '23' ORDER BY 1,2,3; -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (value OPERATOR(pg_catalog.=) '23'::text) -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (value OPERATOR(pg_catalog.=) '23'::text) - key | value | age ------+-------+----- - 1 | 23 | 20 -(1 row) - - -- similarly, multi-shard modifications should use local exection - -- on the shards that reside on this node - DELETE FROM distributed_table WHERE value = '23'; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (value OPERATOR(pg_catalog.=) '23'::text) -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (value OPERATOR(pg_catalog.=) '23'::text) - -- make sure that the value is deleted - SELECT * FROM distributed_table WHERE value = '23' ORDER BY 1,2,3; -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (value OPERATOR(pg_catalog.=) '23'::text) -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (value OPERATOR(pg_catalog.=) '23'::text) - key | value | age ------+-------+----- -(0 rows) - -COMMIT; --- make sure that we've committed everything -SELECT * FROM distributed_table WHERE key = 1 ORDER BY 1,2,3; -LOG: executing the command locally: SELECT key, value, age FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) ORDER BY key, value, age - key | value | age ------+-------+----- -(0 rows) - --- if we start with a distributed execution, we should keep --- using that and never switch back to local execution -BEGIN; - DELETE FROM distributed_table WHERE value = '11'; - -- although this command could have been executed - -- locally, it is not going to be executed locally - SELECT * FROM distributed_table WHERE key = 1 ORDER BY 1,2,3; - key | value | age ------+-------+----- -(0 rows) - - -- but we can still execute parallel queries, even if - -- they are utility commands - TRUNCATE distributed_table CASCADE; -NOTICE: truncate cascades to table "second_distributed_table" - -- TRUNCATE cascaded into second_distributed_table - SELECT count(*) FROM second_distributed_table; - count -------- - 0 -(1 row) - -ROLLBACK; --- load some data so that foreign keys won't complain with the next tests -INSERT INTO reference_table SELECT i FROM generate_series(500, 600) i; --- a very similar set of operation, but this time use --- COPY as the first command -BEGIN; - INSERT INTO distributed_table SELECT i, i::text, i % 10 + 25 FROM generate_series(500, 600) i; - -- this could go through local execution, but doesn't because we've already - -- done distributed execution - SELECT * FROM distributed_table WHERE key = 500 ORDER BY 1,2,3; - key | value | age ------+-------+----- - 500 | 500 | 25 -(1 row) - - -- utility commands could still use distributed execution - TRUNCATE distributed_table CASCADE; -NOTICE: truncate cascades to table "second_distributed_table" - -- ensure that TRUNCATE made it - SELECT * FROM distributed_table WHERE key = 500 ORDER BY 1,2,3; - key | value | age ------+-------+----- -(0 rows) - -ROLLBACK; --- show that cascading foreign keys just works fine with local execution -BEGIN; - INSERT INTO reference_table VALUES (701); -LOG: executing the command locally: INSERT INTO local_shard_execution.reference_table_1470000 (key) VALUES (701) - INSERT INTO distributed_table VALUES (701, '701', 701); -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 (key, value, age) VALUES (701, '701'::text, 701) - INSERT INTO second_distributed_table VALUES (701, '701'); -LOG: executing the command locally: INSERT INTO local_shard_execution.second_distributed_table_1470005 (key, value) VALUES (701, '701'::text) - DELETE FROM reference_table WHERE key = 701; -LOG: executing the command locally: DELETE FROM local_shard_execution.reference_table_1470000 reference_table WHERE (key OPERATOR(pg_catalog.=) 701) - SELECT count(*) FROM distributed_table WHERE key = 701; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 701) - count -------- - 0 -(1 row) - - SELECT count(*) FROM second_distributed_table WHERE key = 701; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.second_distributed_table_1470005 second_distributed_table WHERE (key OPERATOR(pg_catalog.=) 701) - count -------- - 0 -(1 row) - - -- multi-shard commands should also see the changes - SELECT count(*) FROM distributed_table WHERE key > 700; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.>) 700) -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.>) 700) - count -------- - 0 -(1 row) - - -- we can still do multi-shard commands - DELETE FROM distributed_table; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470001 distributed_table -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table -ROLLBACK; --- multiple queries hitting different shards can be executed locally -BEGIN; - SELECT count(*) FROM distributed_table WHERE key = 1; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 0 -(1 row) - - SELECT count(*) FROM distributed_table WHERE key = 6; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 6) - count -------- - 1 -(1 row) - - SELECT count(*) FROM distributed_table WHERE key = 500; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 500) - count -------- - 0 -(1 row) - -ROLLBACK; --- a local query is followed by a command that cannot be executed locally -BEGIN; - SELECT count(*) FROM distributed_table WHERE key = 1; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 0 -(1 row) - - TRUNCATE distributed_table CASCADE; -NOTICE: truncate cascades to table "second_distributed_table" -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; --- a local query is followed by a command that cannot be executed locally -BEGIN; - SELECT count(*) FROM distributed_table WHERE key = 1; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 0 -(1 row) - - -- even no need to supply any data - COPY distributed_table FROM STDIN WITH CSV; -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; --- a local query is followed by a command that cannot be executed locally -BEGIN; - SELECT count(*) FROM distributed_table WHERE key = 1; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 0 -(1 row) - - - INSERT INTO distributed_table (key) SELECT i FROM generate_series(1,10)i; -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; --- a local query is followed by a command that cannot be executed locally -BEGIN; - SELECT count(*) FROM distributed_table WHERE key = 1; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 0 -(1 row) - - - INSERT INTO distributed_table (key) SELECT key+1 FROM distributed_table; -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; -INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '29' RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '11'::text, 21) ON CONFLICT(key) DO UPDATE SET value = '29'::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age - key | value | age ------+-------+----- - 1 | 11 | 21 -(1 row) - -BEGIN; - DELETE FROM distributed_table WHERE key = 1; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - EXPLAIN ANALYZE DELETE FROM distributed_table WHERE key = 1; -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; -BEGIN; - INSERT INTO distributed_table VALUES (11, '111',29) ON CONFLICT(key) DO UPDATE SET value = '29' RETURNING *; - key | value | age ------+-------+----- - 11 | 29 | 121 -(1 row) - - -- this is already disallowed on the nodes, adding it in case we - -- support DDLs from the worker nodes in the future - ALTER TABLE distributed_table ADD COLUMN x INT; -ERROR: operation is not allowed on this node -HINT: Connect to the coordinator and run it again. -ROLLBACK; -BEGIN; - INSERT INTO distributed_table VALUES (11, '111',29) ON CONFLICT(key) DO UPDATE SET value = '29' RETURNING *; - key | value | age ------+-------+----- - 11 | 29 | 121 -(1 row) - - -- this is already disallowed because VACUUM cannot be executed in tx block - -- adding in case this is supported some day - VACUUM second_distributed_table; -ERROR: VACUUM cannot run inside a transaction block -ROLLBACK; --- make sure that functions can use local execution -CREATE OR REPLACE PROCEDURE only_local_execution() AS $$ - DECLARE cnt INT; - BEGIN - INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '29'; - SELECT count(*) INTO cnt FROM distributed_table WHERE key = 1; - DELETE FROM distributed_table WHERE key = 1; - END; -$$ LANGUAGE plpgsql; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE OR REPLACE PROCEDURE only_local_execution() AS $$ - ^ -CALL only_local_execution(); -ERROR: syntax error at or near "CALL" -LINE 1: CALL only_local_execution(); - ^ -CREATE OR REPLACE PROCEDURE only_local_execution_with_params(int) AS $$ - DECLARE cnt INT; - BEGIN - INSERT INTO distributed_table VALUES ($1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '29'; - SELECT count(*) INTO cnt FROM distributed_table WHERE key = $1; - DELETE FROM distributed_table WHERE key = $1; - END; -$$ LANGUAGE plpgsql; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE OR REPLACE PROCEDURE only_local_execution_with_params... - ^ -CALL only_local_execution_with_params(1); -ERROR: syntax error at or near "CALL" -LINE 1: CALL only_local_execution_with_params(1); - ^ -CREATE OR REPLACE PROCEDURE local_execution_followed_by_dist() AS $$ - DECLARE cnt INT; - BEGIN - INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '29'; - SELECT count(*) INTO cnt FROM distributed_table WHERE key = 1; - DELETE FROM distributed_table; - SELECT count(*) INTO cnt FROM distributed_table; - END; -$$ LANGUAGE plpgsql; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE OR REPLACE PROCEDURE local_execution_followed_by_dist... - ^ -CALL local_execution_followed_by_dist(); -ERROR: syntax error at or near "CALL" -LINE 1: CALL local_execution_followed_by_dist(); - ^ --- test CTEs, including modifying CTEs -WITH local_insert AS (INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '29' RETURNING *), -distributed_local_mixed AS (SELECT * FROM reference_table WHERE key IN (SELECT key FROM local_insert)) -SELECT * FROM local_insert, distributed_local_mixed; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '11'::text, 21) ON CONFLICT(key) DO UPDATE SET value = '29'::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age -LOG: executing the command locally: SELECT key FROM local_shard_execution.reference_table_1470000 reference_table WHERE (key OPERATOR(pg_catalog.=) ANY (SELECT local_insert.key FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.age FROM read_intermediate_result('65_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, age bigint)) local_insert)) - key | value | age | key ------+-------+-----+----- - 1 | 29 | 21 | 1 -(1 row) - --- since we start with parallel execution, we do not switch back to local execution in the --- latter CTEs -WITH distributed_local_mixed AS (SELECT * FROM distributed_table), -local_insert AS (INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '29' RETURNING *) -SELECT * FROM local_insert, distributed_local_mixed ORDER BY 1,2,3,4,5; - key | value | age | key | value | age ------+-------+-----+-----+-------+----- - 1 | 29 | 21 | 1 | 29 | 21 - 1 | 29 | 21 | 6 | '6' | 25 - 1 | 29 | 21 | 11 | '11' | 121 -(3 rows) - --- router CTE pushdown -WITH all_data AS (SELECT * FROM distributed_table WHERE key = 1) -SELECT - count(*) -FROM - distributed_table, all_data -WHERE - distributed_table.key = all_data.key AND distributed_table.key = 1; -LOG: executing the command locally: WITH all_data AS (SELECT distributed_table_1.key, distributed_table_1.value, distributed_table_1.age FROM local_shard_execution.distributed_table_1470001 distributed_table_1 WHERE (distributed_table_1.key OPERATOR(pg_catalog.=) 1)) SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table, all_data WHERE ((distributed_table.key OPERATOR(pg_catalog.=) all_data.key) AND (distributed_table.key OPERATOR(pg_catalog.=) 1)) - count -------- - 1 -(1 row) - -INSERT INTO reference_table VALUES (2); -LOG: executing the command locally: INSERT INTO local_shard_execution.reference_table_1470000 (key) VALUES (2) -INSERT INTO distributed_table VALUES (2, '29', 29); -INSERT INTO second_distributed_table VALUES (2, '29'); --- single shard that is not a local query followed by a local query -WITH all_data AS (SELECT * FROM second_distributed_table WHERE key = 2) -SELECT - distributed_table.key -FROM - distributed_table, all_data -WHERE - distributed_table.value = all_data.value AND distributed_table.key = 1 -ORDER BY - 1 DESC; - key ------ - 1 -(1 row) - --- multi-shard CTE is followed by a query which could be executed locally, but --- since the query started with a parallel query, it doesn't use local execution --- note that if we allow Postgres to inline the CTE (e.g., not have the EXISTS --- subquery), then it'd pushdown the filters and the query becomes single-shard, --- locally executable query -WITH all_data AS (SELECT * FROM distributed_table) -SELECT - count(*) -FROM - distributed_table, all_data -WHERE - distributed_table.key = all_data.key AND distributed_table.key = 1 - AND EXISTS (SELECT * FROM all_data); - count -------- - 1 -(1 row) - --- in pg12, the following CTE can be inlined, still the query becomes --- a subquery that needs to be recursively planned and a parallel --- query, so do not use local execution -WITH all_data AS (SELECT age FROM distributed_table) -SELECT - count(*) -FROM - distributed_table, all_data -WHERE - distributed_table.key = all_data.age AND distributed_table.key = 1; - count -------- - 0 -(1 row) - --- get ready for the next commands -TRUNCATE reference_table, distributed_table, second_distributed_table; --- local execution of returning of reference tables -INSERT INTO reference_table VALUES (1),(2),(3),(4),(5),(6) RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.reference_table_1470000 AS citus_table_alias (key) VALUES (1), (2), (3), (4), (5), (6) RETURNING citus_table_alias.key - key ------ - 1 - 2 - 3 - 4 - 5 - 6 -(6 rows) - --- local execution of multi-row INSERTs -INSERT INTO distributed_table VALUES (1, '11',21), (5,'55',22) ON CONFLICT(key) DO UPDATE SET value = (EXCLUDED.value::int + 1)::text RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1,'11'::text,'21'::bigint), (5,'55'::text,'22'::bigint) ON CONFLICT(key) DO UPDATE SET value = (((excluded.value)::integer OPERATOR(pg_catalog.+) 1))::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age - key | value | age ------+-------+----- - 1 | 11 | 21 - 5 | 55 | 22 -(2 rows) - --- distributed execution of multi-rows INSERTs, where some part of the execution --- could have been done via local execution but the executor choose the other way around --- because the command is a multi-shard query -INSERT INTO distributed_table VALUES (1, '11',21), (2,'22',22), (3,'33',33), (4,'44',44),(5,'55',55) ON CONFLICT(key) DO UPDATE SET value = (EXCLUDED.value::int + 1)::text RETURNING *; - key | value | age ------+-------+----- - 1 | 12 | 21 - 2 | 22 | 22 - 3 | 33 | 33 - 4 | 44 | 44 - 5 | 56 | 22 -(5 rows) - -PREPARE local_prepare_no_param AS SELECT count(*) FROM distributed_table WHERE key = 1; -PREPARE local_prepare_param (int) AS SELECT count(*) FROM distributed_table WHERE key = $1; -PREPARE remote_prepare_param (int) AS SELECT count(*) FROM distributed_table WHERE key != $1; -BEGIN; - -- 6 local execution without params - EXECUTE local_prepare_no_param; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_no_param; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_no_param; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_no_param; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_no_param; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_no_param; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - - -- 6 local executions with params - EXECUTE local_prepare_param(1); -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_param(5); -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 5) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_param(6); -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 6) - count -------- - 0 -(1 row) - - EXECUTE local_prepare_param(1); -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 1) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_param(5); -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.=) 5) - count -------- - 1 -(1 row) - - EXECUTE local_prepare_param(6); -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 6) - count -------- - 0 -(1 row) - - -- followed by a non-local execution - EXECUTE remote_prepare_param(1); -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (key OPERATOR(pg_catalog.<>) 1) -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.<>) 1) - count -------- - 4 -(1 row) - -COMMIT; --- failures of local execution should rollback both the --- local execution and remote executions --- fail on a local execution -BEGIN; - INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '100' RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '11'::text, 21) ON CONFLICT(key) DO UPDATE SET value = '100'::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age - key | value | age ------+-------+----- - 1 | 100 | 21 -(1 row) - - UPDATE distributed_table SET value = '200'; -LOG: executing the command locally: UPDATE local_shard_execution.distributed_table_1470001 distributed_table SET value = '200'::text -LOG: executing the command locally: UPDATE local_shard_execution.distributed_table_1470003 distributed_table SET value = '200'::text - INSERT INTO distributed_table VALUES (1, '100',21) ON CONFLICT(key) DO UPDATE SET value = (1 / (100.0 - EXCLUDED.value::int))::text RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '100'::text, 21) ON CONFLICT(key) DO UPDATE SET value = (((1)::numeric OPERATOR(pg_catalog./) (100.0 OPERATOR(pg_catalog.-) ((excluded.value)::integer)::numeric)))::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age -ERROR: division by zero -ROLLBACK; --- we've rollbacked everything -SELECT count(*) FROM distributed_table WHERE value = '200'; - count -------- - 0 -(1 row) - --- RETURNING should just work fine for reference tables -INSERT INTO reference_table VALUES (500) RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.reference_table_1470000 (key) VALUES (500) RETURNING key - key ------ - 500 -(1 row) - -DELETE FROM reference_table WHERE key = 500 RETURNING *; -LOG: executing the command locally: DELETE FROM local_shard_execution.reference_table_1470000 reference_table WHERE (key OPERATOR(pg_catalog.=) 500) RETURNING key - key ------ - 500 -(1 row) - --- should be able to skip local execution even if in a sequential mode of execution -BEGIN; - SET LOCAL citus.multi_shard_modify_mode TO sequential ; - DELETE FROM distributed_table; - INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '100' RETURNING *; - key | value | age ------+-------+----- - 1 | 11 | 21 -(1 row) - -ROLLBACK; --- sequential execution should just work fine after a local execution -BEGIN; - SET citus.multi_shard_modify_mode TO sequential ; - INSERT INTO distributed_table VALUES (1, '11',21) ON CONFLICT(key) DO UPDATE SET value = '100' RETURNING *; -LOG: executing the command locally: INSERT INTO local_shard_execution.distributed_table_1470001 AS citus_table_alias (key, value, age) VALUES (1, '11'::text, 21) ON CONFLICT(key) DO UPDATE SET value = '100'::text RETURNING citus_table_alias.key, citus_table_alias.value, citus_table_alias.age - key | value | age ------+-------+----- - 1 | 100 | 21 -(1 row) - - DELETE FROM distributed_table; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470001 distributed_table -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table -ROLLBACK; --- load some data so that foreign keys won't complain with the next tests -TRUNCATE reference_table CASCADE; -NOTICE: truncate cascades to table "distributed_table" -NOTICE: truncate cascades to table "second_distributed_table" -INSERT INTO reference_table SELECT i FROM generate_series(500, 600) i; -INSERT INTO distributed_table SELECT i, i::text, i % 10 + 25 FROM generate_series(500, 600) i; --- show that both local, and mixed local-distributed executions --- calculate rows processed correctly -BEGIN; - DELETE FROM distributed_table WHERE key = 500; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 500) - DELETE FROM distributed_table WHERE value != '123123213123213'; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE (value OPERATOR(pg_catalog.<>) '123123213123213'::text) -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (value OPERATOR(pg_catalog.<>) '123123213123213'::text) -ROLLBACK; -BEGIN; - - DELETE FROM reference_table WHERE key = 500 RETURNING *; -LOG: executing the command locally: DELETE FROM local_shard_execution.reference_table_1470000 reference_table WHERE (key OPERATOR(pg_catalog.=) 500) RETURNING key - key ------ - 500 -(1 row) - - DELETE FROM reference_table; -LOG: executing the command locally: DELETE FROM local_shard_execution.reference_table_1470000 reference_table -ROLLBACK; --- mix with other executors should fail --- router modify execution should error -BEGIN; - DELETE FROM distributed_table WHERE key = 500; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 500) - SET LOCAL citus.task_executor_type = 'real-time'; - DELETE FROM distributed_table; -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; --- local execution should not be executed locally --- becase a multi-shard router query has already been executed -BEGIN; - SET LOCAL citus.task_executor_type = 'real-time'; - DELETE FROM distributed_table; - DELETE FROM distributed_table WHERE key = 500; -ROLLBACK; --- router select execution -BEGIN; - DELETE FROM distributed_table WHERE key = 500; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 500) - SET LOCAL citus.task_executor_type = 'real-time'; - SELECT count(*) FROM distributed_table WHERE key = 500; -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; --- local execution should not be executed locally --- becase a single-shard router query has already been executed -BEGIN; - SET LOCAL citus.task_executor_type = 'real-time'; - SELECT count(*) FROM distributed_table WHERE key = 500; - count -------- - 1 -(1 row) - - DELETE FROM distributed_table WHERE key = 500; -ROLLBACK; --- real-time select execution -BEGIN; - DELETE FROM distributed_table WHERE key = 500; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 500) - SET LOCAL citus.task_executor_type = 'real-time'; - SELECT count(*) FROM distributed_table; -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; --- local execution should not be executed locally --- becase a real-time query has already been executed -BEGIN; - SET LOCAL citus.task_executor_type = 'real-time'; - SELECT count(*) FROM distributed_table; - count -------- - 101 -(1 row) - - DELETE FROM distributed_table WHERE key = 500; -ROLLBACK; --- task-tracker select execution -BEGIN; - DELETE FROM distributed_table WHERE key = 500; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 500) - SET LOCAL citus.task_executor_type = 'task-tracker'; - SELECT count(*) FROM distributed_table; -ERROR: cannot execute command because a local execution has already been done in the transaction -DETAIL: Some parallel commands cannot be executed if a previous command has already been executed locally -HINT: Try re-running the transaction with "SET LOCAL citus.enable_local_execution TO OFF;" -ROLLBACK; --- local execution should not be executed locally --- becase a task-tracker query has already been executed -BEGIN; - SET LOCAL citus.task_executor_type = 'task-tracker'; - SET LOCAL client_min_messages TO INFO; - SELECT count(*) FROM distributed_table; - count -------- - 101 -(1 row) - - SET LOCAL client_min_messages TO LOG; - DELETE FROM distributed_table WHERE key = 500; -ROLLBACK; --- probably not a realistic case since views are not very --- well supported with MX -CREATE VIEW v_local_query_execution AS -SELECT * FROM distributed_table WHERE key = 500; -SELECT * FROM v_local_query_execution; -LOG: executing the command locally: SELECT key, value, age FROM (SELECT distributed_table.key, distributed_table.value, distributed_table.age FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (distributed_table.key OPERATOR(pg_catalog.=) 500)) v_local_query_execution - key | value | age ------+-------+----- - 500 | 500 | 25 -(1 row) - --- similar test, but this time the view itself is a non-local --- query, but the query on the view is local -CREATE VIEW v_local_query_execution_2 AS -SELECT * FROM distributed_table; -SELECT * FROM v_local_query_execution_2 WHERE key = 500; -LOG: executing the command locally: SELECT key, value, age FROM (SELECT distributed_table.key, distributed_table.value, distributed_table.age FROM local_shard_execution.distributed_table_1470003 distributed_table) v_local_query_execution_2 WHERE (key OPERATOR(pg_catalog.=) 500) - key | value | age ------+-------+----- - 500 | 500 | 25 -(1 row) - --- even if we switch from remote execution -> local execution, --- we are able to use remote execution after rollback -BEGIN; - SAVEPOINT my_savepoint; - SELECT count(*) FROM distributed_table; - count -------- - 101 -(1 row) - - DELETE FROM distributed_table WHERE key = 500; - - ROLLBACK TO SAVEPOINT my_savepoint; - - DELETE FROM distributed_table WHERE key = 500; -COMMIT; --- even if we switch from local execution -> remote execution, --- we are able to use local execution after rollback -BEGIN; - - SAVEPOINT my_savepoint; - DELETE FROM distributed_table WHERE key = 500; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 500) - - SELECT count(*) FROM distributed_table; -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE true -LOG: executing the command locally: SELECT count(*) AS count FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE true - count -------- - 100 -(1 row) - - ROLLBACK TO SAVEPOINT my_savepoint; - - DELETE FROM distributed_table WHERE key = 500; -LOG: executing the command locally: DELETE FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE (key OPERATOR(pg_catalog.=) 500) -COMMIT; --- sanity check: local execution on partitions -INSERT INTO collections_list (collection_id) VALUES (0) RETURNING *; -ERROR: relation "collections_list" does not exist -LINE 1: INSERT INTO collections_list (collection_id) VALUES (0) RETU... - ^ -BEGIN; - INSERT INTO collections_list (key, collection_id) VALUES (1,0); -ERROR: relation "collections_list" does not exist -LINE 1: INSERT INTO collections_list (key, collection_id) VALUES (1,... - ^ - SELECT count(*) FROM collections_list_0 WHERE key = 1; -ERROR: current transaction is aborted, commands ignored until end of transaction block - SELECT count(*) FROM collections_list; -ERROR: current transaction is aborted, commands ignored until end of transaction block - SELECT * FROM collections_list ORDER BY 1,2,3,4; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; --- the final queries for the following CTEs are going to happen on the intermediate results only --- one of them will be executed remotely, and the other is locally --- Citus currently doesn't allow using task_assignment_policy for intermediate results -WITH distributed_local_mixed AS (INSERT INTO reference_table VALUES (1000) RETURNING *) SELECT * FROM distributed_local_mixed; -LOG: executing the command locally: INSERT INTO local_shard_execution.reference_table_1470000 (key) VALUES (1000) RETURNING key - key ------- - 1000 -(1 row) - -\c - - - :master_port -SET client_min_messages TO ERROR; -SET search_path TO public; -DROP SCHEMA local_shard_execution CASCADE; diff --git a/src/test/regress/expected/multi_create_table_new_features.out b/src/test/regress/expected/multi_create_table_new_features.out index 1029ae8f9..c3f707787 100644 --- a/src/test/regress/expected/multi_create_table_new_features.out +++ b/src/test/regress/expected/multi_create_table_new_features.out @@ -1,14 +1,6 @@ -- -- MULTI_CREATE_TABLE_NEW_FEATURES -- --- print major version to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+') AS major_version; - major_version ---------------- - 10 -(1 row) - -- Verify that the GENERATED ... AS IDENTITY feature in PostgreSQL 10 -- is forbidden in distributed tables. CREATE TABLE table_identity_col ( diff --git a/src/test/regress/expected/multi_deparse_procedure.out b/src/test/regress/expected/multi_deparse_procedure.out index d17865bc1..1e370a15e 100644 --- a/src/test/regress/expected/multi_deparse_procedure.out +++ b/src/test/regress/expected/multi_deparse_procedure.out @@ -1,10 +1,3 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above - \gset -\if :server_verion_eleven_and_above -\else -\q -\endif -- -- Regression tests for deparsing ALTER/DROP PROCEDURE Queries -- @@ -38,14 +31,6 @@ CREATE SCHEMA procedure_tests; SET search_path TO procedure_tests; SET citus.shard_count TO 4; SET client_min_messages TO INFO; --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - CREATE FUNCTION deparse_test(text) RETURNS text AS 'citus' diff --git a/src/test/regress/expected/multi_deparse_procedure_0.out b/src/test/regress/expected/multi_deparse_procedure_0.out deleted file mode 100644 index a5ea9fa80..000000000 --- a/src/test/regress/expected/multi_deparse_procedure_0.out +++ /dev/null @@ -1,6 +0,0 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above - \gset -\if :server_verion_eleven_and_above -\else -\q diff --git a/src/test/regress/expected/multi_explain.out b/src/test/regress/expected/multi_explain.out index 5f5180c05..c4107a1a1 100644 --- a/src/test/regress/expected/multi_explain.out +++ b/src/test/regress/expected/multi_explain.out @@ -2,14 +2,6 @@ -- MULTI_EXPLAIN -- SET citus.next_shard_id TO 570000; --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - version_above_nine --------------------- - t -(1 row) - \a\t RESET citus.task_executor_type; SET citus.explain_distributed_queries TO on; @@ -786,7 +778,6 @@ t SELECT true AS valid FROM explain_json($$ SELECT avg(l_linenumber) FROM lineitem WHERE l_orderkey > 9030$$); t - -- Test multi shard update EXPLAIN (COSTS FALSE) UPDATE lineitem_hash_part @@ -810,7 +801,6 @@ Custom Scan (Citus Adaptive) Node: host=localhost port=57638 dbname=regression -> Update on lineitem_hash_part_360044 lineitem_hash_part -> Seq Scan on lineitem_hash_part_360044 lineitem_hash_part - EXPLAIN (COSTS FALSE) UPDATE lineitem_hash_part SET l_suppkey = 12 @@ -1034,8 +1024,8 @@ SELECT true AS valid FROM explain_xml($$ AND o_custkey = c_custkey AND l_suppkey = s_suppkey$$); t --- make sure that EXPLAIN works without --- problems for queries that inlvolves only +-- make sure that EXPLAIN works without +-- problems for queries that inlvolves only -- reference tables SELECT true AS valid FROM explain_xml($$ SELECT count(*) diff --git a/src/test/regress/expected/multi_index_statements.out b/src/test/regress/expected/multi_index_statements.out index f64d20041..1c4e5b7d2 100644 --- a/src/test/regress/expected/multi_index_statements.out +++ b/src/test/regress/expected/multi_index_statements.out @@ -3,13 +3,6 @@ -- -- Check that we can run CREATE INDEX and DROP INDEX statements on distributed -- tables. -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS server_version_above_ten; - server_version_above_ten --------------------------- - t -(1 row) - -- -- CREATE TEST TABLES -- diff --git a/src/test/regress/expected/multi_index_statements_0.out b/src/test/regress/expected/multi_index_statements_0.out deleted file mode 100644 index 02e4ae61b..000000000 --- a/src/test/regress/expected/multi_index_statements_0.out +++ /dev/null @@ -1,312 +0,0 @@ --- --- MULTI_INDEX_STATEMENTS --- --- Check that we can run CREATE INDEX and DROP INDEX statements on distributed --- tables. -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS server_version_above_ten; - server_version_above_ten --------------------------- - f -(1 row) - --- --- CREATE TEST TABLES --- -SET citus.next_shard_id TO 102080; -CREATE TABLE index_test_range(a int, b int, c int); -SELECT create_distributed_table('index_test_range', 'a', 'range'); - create_distributed_table --------------------------- - -(1 row) - -SELECT master_create_empty_shard('index_test_range'); - master_create_empty_shard ---------------------------- - 102080 -(1 row) - -SELECT master_create_empty_shard('index_test_range'); - master_create_empty_shard ---------------------------- - 102081 -(1 row) - -SET citus.shard_count TO 8; -SET citus.shard_replication_factor TO 2; -CREATE TABLE index_test_hash(a int, b int, c int); -SELECT create_distributed_table('index_test_hash', 'a', 'hash'); - create_distributed_table --------------------------- - -(1 row) - -CREATE TABLE index_test_append(a int, b int, c int); -SELECT create_distributed_table('index_test_append', 'a', 'append'); - create_distributed_table --------------------------- - -(1 row) - -SELECT master_create_empty_shard('index_test_append'); - master_create_empty_shard ---------------------------- - 102090 -(1 row) - -SELECT master_create_empty_shard('index_test_append'); - master_create_empty_shard ---------------------------- - 102091 -(1 row) - --- --- CREATE INDEX --- --- Verify that we can create different types of indexes -CREATE INDEX lineitem_orderkey_index ON lineitem (l_orderkey); -CREATE INDEX lineitem_partkey_desc_index ON lineitem (l_partkey DESC); -CREATE INDEX lineitem_partial_index ON lineitem (l_shipdate) - WHERE l_shipdate < '1995-01-01'; -CREATE INDEX lineitem_colref_index ON lineitem (record_ne(lineitem.*, NULL)); -SET client_min_messages = ERROR; -- avoid version dependant warning about WAL -CREATE INDEX lineitem_orderkey_hash_index ON lineitem USING hash (l_partkey); -CREATE UNIQUE INDEX index_test_range_index_a ON index_test_range(a); -CREATE UNIQUE INDEX index_test_range_index_a_b ON index_test_range(a,b); -CREATE UNIQUE INDEX index_test_hash_index_a ON index_test_hash(a); -CREATE UNIQUE INDEX index_test_hash_index_a_b ON index_test_hash(a,b); -CREATE UNIQUE INDEX index_test_hash_index_a_b_partial ON index_test_hash(a,b) WHERE c IS NOT NULL; -CREATE UNIQUE INDEX index_test_range_index_a_b_partial ON index_test_range(a,b) WHERE c IS NOT NULL; -CREATE UNIQUE INDEX index_test_hash_index_a_b_c ON index_test_hash(a) INCLUDE (b,c); -ERROR: syntax error at or near "INCLUDE" -LINE 1: ...index_test_hash_index_a_b_c ON index_test_hash(a) INCLUDE (b... - ^ -RESET client_min_messages; --- Verify that we handle if not exists statements correctly -CREATE INDEX lineitem_orderkey_index on lineitem(l_orderkey); -ERROR: relation "lineitem_orderkey_index" already exists -CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on lineitem(l_orderkey); -NOTICE: relation "lineitem_orderkey_index" already exists, skipping -CREATE INDEX IF NOT EXISTS lineitem_orderkey_index_new on lineitem(l_orderkey); --- Verify if not exists behavior with an index with same name on a different table -CREATE INDEX lineitem_orderkey_index on index_test_hash(a); -ERROR: relation "lineitem_orderkey_index" already exists -CREATE INDEX IF NOT EXISTS lineitem_orderkey_index on index_test_hash(a); -NOTICE: relation "lineitem_orderkey_index" already exists, skipping --- Verify that we can create indexes concurrently -CREATE INDEX CONCURRENTLY lineitem_concurrently_index ON lineitem (l_orderkey); --- Verify that we warn out on CLUSTER command for distributed tables and no parameter -CLUSTER index_test_hash USING index_test_hash_index_a; -WARNING: not propagating CLUSTER command to worker nodes -CLUSTER; -WARNING: not propagating CLUSTER command to worker nodes --- Verify that no-name local CREATE INDEX CONCURRENTLY works -CREATE TABLE local_table (id integer, name text); -CREATE INDEX CONCURRENTLY local_table_index ON local_table(id); --- Vefify we don't warn out on CLUSTER command for local tables -CLUSTER local_table USING local_table_index; -DROP TABLE local_table; --- Verify that all indexes got created on the master node and one of the workers -SELECT * FROM pg_indexes WHERE tablename = 'lineitem' or tablename like 'index_test_%' ORDER BY indexname; - schemaname | tablename | indexname | tablespace | indexdef -------------+------------------+------------------------------------+------------+---------------------------------------------------------------------------------------------------------------------------- - public | index_test_hash | index_test_hash_index_a | | CREATE UNIQUE INDEX index_test_hash_index_a ON public.index_test_hash USING btree (a) - public | index_test_hash | index_test_hash_index_a_b | | CREATE UNIQUE INDEX index_test_hash_index_a_b ON public.index_test_hash USING btree (a, b) - public | index_test_hash | index_test_hash_index_a_b_partial | | CREATE UNIQUE INDEX index_test_hash_index_a_b_partial ON public.index_test_hash USING btree (a, b) WHERE (c IS NOT NULL) - public | index_test_range | index_test_range_index_a | | CREATE UNIQUE INDEX index_test_range_index_a ON public.index_test_range USING btree (a) - public | index_test_range | index_test_range_index_a_b | | CREATE UNIQUE INDEX index_test_range_index_a_b ON public.index_test_range USING btree (a, b) - public | index_test_range | index_test_range_index_a_b_partial | | CREATE UNIQUE INDEX index_test_range_index_a_b_partial ON public.index_test_range USING btree (a, b) WHERE (c IS NOT NULL) - public | lineitem | lineitem_colref_index | | CREATE INDEX lineitem_colref_index ON public.lineitem USING btree (record_ne(lineitem.*, NULL::record)) - public | lineitem | lineitem_concurrently_index | | CREATE INDEX lineitem_concurrently_index ON public.lineitem USING btree (l_orderkey) - public | lineitem | lineitem_orderkey_hash_index | | CREATE INDEX lineitem_orderkey_hash_index ON public.lineitem USING hash (l_partkey) - public | lineitem | lineitem_orderkey_index | | CREATE INDEX lineitem_orderkey_index ON public.lineitem USING btree (l_orderkey) - public | lineitem | lineitem_orderkey_index_new | | CREATE INDEX lineitem_orderkey_index_new ON public.lineitem USING btree (l_orderkey) - public | lineitem | lineitem_partial_index | | CREATE INDEX lineitem_partial_index ON public.lineitem USING btree (l_shipdate) WHERE (l_shipdate < '01-01-1995'::date) - public | lineitem | lineitem_partkey_desc_index | | CREATE INDEX lineitem_partkey_desc_index ON public.lineitem USING btree (l_partkey DESC) - public | lineitem | lineitem_pkey | | CREATE UNIQUE INDEX lineitem_pkey ON public.lineitem USING btree (l_orderkey, l_linenumber) - public | lineitem | lineitem_time_index | | CREATE INDEX lineitem_time_index ON public.lineitem USING btree (l_shipdate) -(15 rows) - -\c - - - :worker_1_port -SELECT count(*) FROM pg_indexes WHERE tablename = (SELECT relname FROM pg_class WHERE relname LIKE 'lineitem%' ORDER BY relname LIMIT 1); - count -------- - 9 -(1 row) - -SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_hash%'; - count -------- - 24 -(1 row) - -SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_range%'; - count -------- - 6 -(1 row) - -SELECT count(*) FROM pg_indexes WHERE tablename LIKE 'index_test_append%'; - count -------- - 0 -(1 row) - -\c - - - :master_port --- Verify that we error out on unsupported statement types -CREATE UNIQUE INDEX try_index ON lineitem (l_orderkey); -ERROR: creating unique indexes on append-partitioned tables is currently unsupported -CREATE INDEX try_index ON lineitem (l_orderkey) TABLESPACE newtablespace; -ERROR: specifying tablespaces with CREATE INDEX statements is currently unsupported -CREATE UNIQUE INDEX try_unique_range_index ON index_test_range(b); -ERROR: creating unique indexes on non-partition columns is currently unsupported -CREATE UNIQUE INDEX try_unique_range_index_partial ON index_test_range(b) WHERE c IS NOT NULL; -ERROR: creating unique indexes on non-partition columns is currently unsupported -CREATE UNIQUE INDEX try_unique_hash_index ON index_test_hash(b); -ERROR: creating unique indexes on non-partition columns is currently unsupported -CREATE UNIQUE INDEX try_unique_hash_index_partial ON index_test_hash(b) WHERE c IS NOT NULL; -ERROR: creating unique indexes on non-partition columns is currently unsupported -CREATE UNIQUE INDEX try_unique_append_index ON index_test_append(b); -ERROR: creating unique indexes on append-partitioned tables is currently unsupported -CREATE UNIQUE INDEX try_unique_append_index ON index_test_append(a); -ERROR: creating unique indexes on append-partitioned tables is currently unsupported -CREATE UNIQUE INDEX try_unique_append_index_a_b ON index_test_append(a,b); -ERROR: creating unique indexes on append-partitioned tables is currently unsupported --- Verify that we error out in case of postgres errors on supported statement --- types. -CREATE INDEX lineitem_orderkey_index ON lineitem (l_orderkey); -ERROR: relation "lineitem_orderkey_index" already exists -CREATE INDEX try_index ON lineitem USING gist (l_orderkey); -ERROR: data type bigint has no default operator class for access method "gist" -HINT: You must specify an operator class for the index or define a default operator class for the data type. -CREATE INDEX try_index ON lineitem (non_existent_column); -ERROR: column "non_existent_column" does not exist -CREATE INDEX ON lineitem (l_orderkey); -ERROR: creating index without a name on a distributed table is currently unsupported --- Verify that none of failed indexes got created on the master node -SELECT * FROM pg_indexes WHERE tablename = 'lineitem' or tablename like 'index_test_%' ORDER BY indexname; - schemaname | tablename | indexname | tablespace | indexdef -------------+------------------+------------------------------------+------------+---------------------------------------------------------------------------------------------------------------------------- - public | index_test_hash | index_test_hash_index_a | | CREATE UNIQUE INDEX index_test_hash_index_a ON public.index_test_hash USING btree (a) - public | index_test_hash | index_test_hash_index_a_b | | CREATE UNIQUE INDEX index_test_hash_index_a_b ON public.index_test_hash USING btree (a, b) - public | index_test_hash | index_test_hash_index_a_b_partial | | CREATE UNIQUE INDEX index_test_hash_index_a_b_partial ON public.index_test_hash USING btree (a, b) WHERE (c IS NOT NULL) - public | index_test_range | index_test_range_index_a | | CREATE UNIQUE INDEX index_test_range_index_a ON public.index_test_range USING btree (a) - public | index_test_range | index_test_range_index_a_b | | CREATE UNIQUE INDEX index_test_range_index_a_b ON public.index_test_range USING btree (a, b) - public | index_test_range | index_test_range_index_a_b_partial | | CREATE UNIQUE INDEX index_test_range_index_a_b_partial ON public.index_test_range USING btree (a, b) WHERE (c IS NOT NULL) - public | lineitem | lineitem_colref_index | | CREATE INDEX lineitem_colref_index ON public.lineitem USING btree (record_ne(lineitem.*, NULL::record)) - public | lineitem | lineitem_concurrently_index | | CREATE INDEX lineitem_concurrently_index ON public.lineitem USING btree (l_orderkey) - public | lineitem | lineitem_orderkey_hash_index | | CREATE INDEX lineitem_orderkey_hash_index ON public.lineitem USING hash (l_partkey) - public | lineitem | lineitem_orderkey_index | | CREATE INDEX lineitem_orderkey_index ON public.lineitem USING btree (l_orderkey) - public | lineitem | lineitem_orderkey_index_new | | CREATE INDEX lineitem_orderkey_index_new ON public.lineitem USING btree (l_orderkey) - public | lineitem | lineitem_partial_index | | CREATE INDEX lineitem_partial_index ON public.lineitem USING btree (l_shipdate) WHERE (l_shipdate < '01-01-1995'::date) - public | lineitem | lineitem_partkey_desc_index | | CREATE INDEX lineitem_partkey_desc_index ON public.lineitem USING btree (l_partkey DESC) - public | lineitem | lineitem_pkey | | CREATE UNIQUE INDEX lineitem_pkey ON public.lineitem USING btree (l_orderkey, l_linenumber) - public | lineitem | lineitem_time_index | | CREATE INDEX lineitem_time_index ON public.lineitem USING btree (l_shipdate) -(15 rows) - --- --- REINDEX --- -REINDEX INDEX lineitem_orderkey_index; -REINDEX TABLE lineitem; -REINDEX SCHEMA public; -REINDEX DATABASE regression; -REINDEX SYSTEM regression; --- --- DROP INDEX --- --- Verify that we can't drop multiple indexes in a single command -DROP INDEX lineitem_orderkey_index, lineitem_partial_index; -ERROR: cannot drop multiple distributed objects in a single command -HINT: Try dropping each object in a separate DROP command. --- Verify that we can succesfully drop indexes -DROP INDEX lineitem_orderkey_index; -DROP INDEX lineitem_orderkey_index_new; -DROP INDEX lineitem_partkey_desc_index; -DROP INDEX lineitem_partial_index; -DROP INDEX lineitem_colref_index; --- Verify that we handle if exists statements correctly -DROP INDEX non_existent_index; -ERROR: index "non_existent_index" does not exist -DROP INDEX IF EXISTS non_existent_index; -NOTICE: index "non_existent_index" does not exist, skipping -DROP INDEX IF EXISTS lineitem_orderkey_hash_index; -DROP INDEX lineitem_orderkey_hash_index; -ERROR: index "lineitem_orderkey_hash_index" does not exist -DROP INDEX index_test_range_index_a; -DROP INDEX index_test_range_index_a_b; -DROP INDEX index_test_range_index_a_b_partial; -DROP INDEX index_test_hash_index_a; -DROP INDEX index_test_hash_index_a_b; -DROP INDEX index_test_hash_index_a_b_partial; --- Verify that we can drop indexes concurrently -DROP INDEX CONCURRENTLY lineitem_concurrently_index; --- Verify that all the indexes are dropped from the master and one worker node. --- As there's a primary key, so exclude those from this check. -SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indrelid = (SELECT relname FROM pg_class WHERE relname LIKE 'lineitem%' ORDER BY relname LIMIT 1)::regclass AND NOT indisprimary AND indexrelid::regclass::text NOT LIKE 'lineitem_time_index%'; - indrelid | indexrelid -----------+------------ -(0 rows) - -SELECT * FROM pg_indexes WHERE tablename LIKE 'index_test_%' ORDER BY indexname; - schemaname | tablename | indexname | tablespace | indexdef -------------+-----------+-----------+------------+---------- -(0 rows) - -\c - - - :worker_1_port -SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indrelid = (SELECT relname FROM pg_class WHERE relname LIKE 'lineitem%' ORDER BY relname LIMIT 1)::regclass AND NOT indisprimary AND indexrelid::regclass::text NOT LIKE 'lineitem_time_index%'; - indrelid | indexrelid -----------+------------ -(0 rows) - -SELECT * FROM pg_indexes WHERE tablename LIKE 'index_test_%' ORDER BY indexname; - schemaname | tablename | indexname | tablespace | indexdef -------------+-----------+-----------+------------+---------- -(0 rows) - --- create index that will conflict with master operations -CREATE INDEX CONCURRENTLY ith_b_idx_102089 ON index_test_hash_102089(b); -\c - - - :master_port --- should fail because worker index already exists -CREATE INDEX CONCURRENTLY ith_b_idx ON index_test_hash(b); -ERROR: CONCURRENTLY-enabled index command failed -DETAIL: CONCURRENTLY-enabled index commands can fail partially, leaving behind an INVALID index. -HINT: Use DROP INDEX CONCURRENTLY IF EXISTS to remove the invalid index, then retry the original command. --- the failure results in an INVALID index -SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx'::regclass; - Index Valid? --------------- - f -(1 row) - --- we can clean it up and recreate with an DROP IF EXISTS -DROP INDEX CONCURRENTLY IF EXISTS ith_b_idx; -CREATE INDEX CONCURRENTLY ith_b_idx ON index_test_hash(b); -SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx'::regclass; - Index Valid? --------------- - t -(1 row) - -\c - - - :worker_1_port --- now drop shard index to test partial master DROP failure -DROP INDEX CONCURRENTLY ith_b_idx_102089; -\c - - - :master_port -DROP INDEX CONCURRENTLY ith_b_idx; -ERROR: CONCURRENTLY-enabled index command failed -DETAIL: CONCURRENTLY-enabled index commands can fail partially, leaving behind an INVALID index. -HINT: Use DROP INDEX CONCURRENTLY IF EXISTS to remove the invalid index, then retry the original command. --- the failure results in an INVALID index -SELECT indisvalid AS "Index Valid?" FROM pg_index WHERE indexrelid='ith_b_idx'::regclass; - Index Valid? --------------- - f -(1 row) - --- final clean up -DROP INDEX CONCURRENTLY IF EXISTS ith_b_idx; --- Drop created tables -DROP TABLE index_test_range; -DROP TABLE index_test_hash; -DROP TABLE index_test_append; diff --git a/src/test/regress/expected/multi_metadata_attributes.out b/src/test/regress/expected/multi_metadata_attributes.out index fc2d793d2..1309c510e 100644 --- a/src/test/regress/expected/multi_metadata_attributes.out +++ b/src/test/regress/expected/multi_metadata_attributes.out @@ -1,6 +1,5 @@ -- if the output of following query changes, we might need to change --- some heap_getattr() calls to heap_deform_tuple(). This errors out in --- postgres versions before 11. +-- some heap_getattr() calls to heap_deform_tuple(). SELECT attrelid::regclass, attname, atthasmissing, attmissingval FROM pg_attribute WHERE atthasmissing diff --git a/src/test/regress/expected/multi_metadata_attributes_0.out b/src/test/regress/expected/multi_metadata_attributes_0.out deleted file mode 100644 index 55e60f1f7..000000000 --- a/src/test/regress/expected/multi_metadata_attributes_0.out +++ /dev/null @@ -1,10 +0,0 @@ --- if the output of following query changes, we might need to change --- some heap_getattr() calls to heap_deform_tuple(). This errors out in --- postgres versions before 11. -SELECT attrelid::regclass, attname, atthasmissing, attmissingval -FROM pg_attribute -WHERE atthasmissing -ORDER BY attrelid, attname; -ERROR: column "atthasmissing" does not exist -LINE 1: SELECT attrelid::regclass, attname, atthasmissing, attmissin... - ^ diff --git a/src/test/regress/expected/multi_multiuser.out b/src/test/regress/expected/multi_multiuser.out index 98efdb6bf..4edf6ab7f 100644 --- a/src/test/regress/expected/multi_multiuser.out +++ b/src/test/regress/expected/multi_multiuser.out @@ -3,14 +3,6 @@ -- -- Test user permissions. -- --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - SET citus.next_shard_id TO 1420000; SET citus.shard_replication_factor TO 1; ALTER SYSTEM SET citus.metadata_sync_interval TO 3000; diff --git a/src/test/regress/expected/multi_multiuser_0.out b/src/test/regress/expected/multi_multiuser_0.out deleted file mode 100644 index 3f19d83d1..000000000 --- a/src/test/regress/expected/multi_multiuser_0.out +++ /dev/null @@ -1,693 +0,0 @@ --- --- MULTI_MULTIUSERS --- --- Test user permissions. --- --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - f -(1 row) - -SET citus.next_shard_id TO 1420000; -SET citus.shard_replication_factor TO 1; -CREATE TABLE test (id integer, val integer); -SELECT create_distributed_table('test', 'id'); - create_distributed_table --------------------------- - -(1 row) - -CREATE TABLE test_coloc (id integer, val integer); -SELECT create_distributed_table('test_coloc', 'id', colocate_with := 'none'); - create_distributed_table --------------------------- - -(1 row) - -SET citus.shard_count TO 1; -CREATE TABLE singleshard (id integer, val integer); -SELECT create_distributed_table('singleshard', 'id'); - create_distributed_table --------------------------- - -(1 row) - --- turn off propagation to avoid Enterprise processing the following section -SET citus.enable_ddl_propagation TO off; -CREATE USER full_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER usage_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER read_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER no_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE ROLE some_role; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -GRANT some_role TO full_access; -GRANT some_role TO read_access; -GRANT ALL ON TABLE test TO full_access; -GRANT SELECT ON TABLE test TO read_access; -CREATE SCHEMA full_access_user_schema; -REVOKE ALL ON SCHEMA full_access_user_schema FROM PUBLIC; -GRANT ALL ON SCHEMA full_access_user_schema TO full_access; -GRANT USAGE ON SCHEMA full_access_user_schema TO usage_access; -SET citus.enable_ddl_propagation TO DEFAULT; -\c - - - :worker_1_port -CREATE USER full_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER usage_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER read_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER no_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE ROLE some_role; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -GRANT some_role TO full_access; -GRANT some_role TO read_access; -GRANT ALL ON TABLE test_1420000 TO full_access; -GRANT SELECT ON TABLE test_1420000 TO read_access; -GRANT ALL ON TABLE test_1420002 TO full_access; -GRANT SELECT ON TABLE test_1420002 TO read_access; -CREATE SCHEMA full_access_user_schema; -REVOKE ALL ON SCHEMA full_access_user_schema FROM PUBLIC; -GRANT USAGE ON SCHEMA full_access_user_schema TO full_access; -GRANT ALL ON SCHEMA full_access_user_schema TO full_access; -GRANT USAGE ON SCHEMA full_access_user_schema TO usage_access; -\c - - - :worker_2_port -CREATE USER full_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER usage_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER read_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE USER no_access; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -CREATE ROLE some_role; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -GRANT some_role TO full_access; -GRANT some_role TO read_access; -GRANT ALL ON TABLE test_1420001 TO full_access; -GRANT SELECT ON TABLE test_1420001 TO read_access; -GRANT ALL ON TABLE test_1420003 TO full_access; -GRANT SELECT ON TABLE test_1420003 TO read_access; -CREATE SCHEMA full_access_user_schema; -REVOKE ALL ON SCHEMA full_access_user_schema FROM PUBLIC; -GRANT USAGE ON SCHEMA full_access_user_schema TO full_access; -GRANT ALL ON SCHEMA full_access_user_schema TO full_access; -GRANT USAGE ON SCHEMA full_access_user_schema TO usage_access; -\c - - - :master_port --- create prepare tests -PREPARE prepare_insert AS INSERT INTO test VALUES ($1); -PREPARE prepare_select AS SELECT count(*) FROM test; --- not allowed to read absolute paths, even as superuser -COPY "/etc/passwd" TO STDOUT WITH (format transmit); -ERROR: absolute path not allowed --- not allowed to read paths outside pgsql_job_cache, even as superuser -COPY "postgresql.conf" TO STDOUT WITH (format transmit); -ERROR: path must be in the pgsql_job_cache directory --- check full permission -SET ROLE full_access; -EXECUTE prepare_insert(1); -EXECUTE prepare_select; - count -------- - 1 -(1 row) - -INSERT INTO test VALUES (2); -SELECT count(*) FROM test; - count -------- - 2 -(1 row) - -SELECT count(*) FROM test WHERE id = 1; - count -------- - 1 -(1 row) - -SET citus.task_executor_type TO 'task-tracker'; -SELECT count(*), min(current_user) FROM test; - count | min --------+------------- - 2 | full_access -(1 row) - --- test re-partition query (needs to transmit intermediate results) -SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2; - count -------- - 0 -(1 row) - --- should not be able to transmit directly -COPY "postgresql.conf" TO STDOUT WITH (format transmit); -ERROR: operation is not allowed -HINT: Run the command with a superuser. -RESET citus.task_executor_type; --- should not be able to transmit directly -COPY "postgresql.conf" TO STDOUT WITH (format transmit); -ERROR: operation is not allowed -HINT: Run the command with a superuser. --- create a task that other users should not be able to inspect -SELECT task_tracker_assign_task(1, 1, 'SELECT 1'); - task_tracker_assign_task --------------------------- - -(1 row) - --- check read permission -SET ROLE read_access; -EXECUTE prepare_insert(1); -ERROR: permission denied for relation test -EXECUTE prepare_select; - count -------- - 2 -(1 row) - -INSERT INTO test VALUES (2); -ERROR: permission denied for relation test -SELECT count(*) FROM test; - count -------- - 2 -(1 row) - -SELECT count(*) FROM test WHERE id = 1; - count -------- - 1 -(1 row) - -SET citus.task_executor_type TO 'task-tracker'; -SELECT count(*), min(current_user) FROM test; - count | min --------+------------- - 2 | read_access -(1 row) - --- test re-partition query (needs to transmit intermediate results) -SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2; - count -------- - 0 -(1 row) - --- should not be able to transmit directly -COPY "postgresql.conf" TO STDOUT WITH (format transmit); -ERROR: operation is not allowed -HINT: Run the command with a superuser. --- should not be able to access tasks or jobs belonging to a different user -SELECT task_tracker_task_status(1, 1); -ERROR: could not find the worker task -DETAIL: Task jobId: 1 and taskId: 1 -SELECT task_tracker_assign_task(1, 2, 'SELECT 1'); -ERROR: must be owner of schema pg_merge_job_0001 -SELECT task_tracker_cleanup_job(1); -ERROR: must be owner of schema pg_merge_job_0001 --- should not be allowed to take aggressive locks on table -BEGIN; -SELECT lock_relation_if_exists('test', 'ACCESS SHARE'); - lock_relation_if_exists -------------------------- - t -(1 row) - -SELECT lock_relation_if_exists('test', 'EXCLUSIVE'); -ERROR: permission denied for relation test -ABORT; -RESET citus.task_executor_type; --- check no permission -SET ROLE no_access; -EXECUTE prepare_insert(1); -ERROR: permission denied for relation test -EXECUTE prepare_select; -ERROR: permission denied for relation test -INSERT INTO test VALUES (2); -ERROR: permission denied for relation test -SELECT count(*) FROM test; -ERROR: permission denied for relation test -SELECT count(*) FROM test WHERE id = 1; -ERROR: permission denied for relation test -SET citus.task_executor_type TO 'task-tracker'; -SELECT count(*), min(current_user) FROM test; -ERROR: permission denied for relation test --- test re-partition query -SELECT count(*) FROM test a JOIN test b ON (a.val = b.val) WHERE a.id = 1 AND b.id = 2; -ERROR: permission denied for relation test --- should not be able to transmit directly -COPY "postgresql.conf" TO STDOUT WITH (format transmit); -ERROR: operation is not allowed -HINT: Run the command with a superuser. -RESET citus.task_executor_type; --- should be able to use intermediate results as any user -BEGIN; -SELECT create_intermediate_result('topten', 'SELECT s FROM generate_series(1,10) s'); - create_intermediate_result ----------------------------- - 10 -(1 row) - -SELECT * FROM read_intermediate_result('topten', 'binary'::citus_copy_format) AS res (s int) ORDER BY s; - s ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(10 rows) - -END; --- as long as we don't read from a table -BEGIN; -SELECT create_intermediate_result('topten', 'SELECT count(*) FROM test'); -ERROR: permission denied for relation test -ABORT; -SELECT * FROM citus_stat_statements_reset(); -ERROR: permission denied for function citus_stat_statements_reset --- should not be allowed to upgrade to reference table -SELECT upgrade_to_reference_table('singleshard'); -ERROR: must be owner of relation singleshard --- should not be allowed to co-located tables -SELECT mark_tables_colocated('test', ARRAY['test_coloc'::regclass]); -ERROR: must be owner of relation test --- should not be allowed to take any locks -BEGIN; -SELECT lock_relation_if_exists('test', 'ACCESS SHARE'); -ERROR: permission denied for relation test -ABORT; -BEGIN; -SELECT lock_relation_if_exists('test', 'EXCLUSIVE'); -ERROR: permission denied for relation test -ABORT; --- table owner should be the same on the shards, even when distributing the table as superuser -SET ROLE full_access; -CREATE TABLE my_table (id integer, val integer); -RESET ROLE; -SELECT create_distributed_table('my_table', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT result FROM run_command_on_workers($$SELECT tableowner FROM pg_tables WHERE tablename LIKE 'my_table_%' LIMIT 1$$); - result -------------- - full_access - full_access -(2 rows) - -SELECT task_tracker_cleanup_job(1); - task_tracker_cleanup_job --------------------------- - -(1 row) - --- table should be distributable by super user when it has data in there -SET ROLE full_access; -CREATE TABLE my_table_with_data (id integer, val integer); -INSERT INTO my_table_with_data VALUES (1,2); -RESET ROLE; -SELECT create_distributed_table('my_table_with_data', 'id'); -NOTICE: Copying data from local table... - create_distributed_table --------------------------- - -(1 row) - -SELECT count(*) FROM my_table_with_data; - count -------- - 1 -(1 row) - --- table that is owned by a role should be distributable by a user that has that role granted --- while it should not be if the user has the role not granted -SET ROLE full_access; -CREATE TABLE my_role_table_with_data (id integer, val integer); -ALTER TABLE my_role_table_with_data OWNER TO some_role; -INSERT INTO my_role_table_with_data VALUES (1,2); -RESET ROLE; --- we first try to distribute it with a user that does not have the role so we can reuse the table -SET ROLE no_access; -SELECT create_distributed_table('my_role_table_with_data', 'id'); -ERROR: must be owner of relation my_role_table_with_data -RESET ROLE; --- then we try to distribute it with a user that has the role but different then the one creating -SET ROLE read_access; -SELECT create_distributed_table('my_role_table_with_data', 'id'); -NOTICE: Copying data from local table... - create_distributed_table --------------------------- - -(1 row) - -RESET ROLE; --- lastly we want to verify the table owner is set to the role, not the user that distributed -SELECT result FROM run_command_on_workers($cmd$ - SELECT tableowner FROM pg_tables WHERE tablename LIKE 'my_role_table_with_data%' LIMIT 1; -$cmd$); - result ------------ - some_role - some_role -(2 rows) - --- we want to verify a user without CREATE access cannot distribute its table, but can get --- its table distributed by the super user --- we want to make sure the schema and user are setup in such a way they can't create a --- table -SET ROLE usage_access; -CREATE TABLE full_access_user_schema.t1 (id int); -ERROR: permission denied for schema full_access_user_schema -LINE 1: CREATE TABLE full_access_user_schema.t1 (id int); - ^ -RESET ROLE; --- now we create the table for the user -CREATE TABLE full_access_user_schema.t1 (id int); -ALTER TABLE full_access_user_schema.t1 OWNER TO usage_access; --- make sure we can insert data -SET ROLE usage_access; -INSERT INTO full_access_user_schema.t1 VALUES (1),(2),(3); --- creating the table should fail with a failure on the worker machine since the user is --- not allowed to create a table -SELECT create_distributed_table('full_access_user_schema.t1', 'id'); -ERROR: permission denied for schema full_access_user_schema -CONTEXT: while executing command on localhost:57638 -RESET ROLE; --- now we distribute the table as super user -SELECT create_distributed_table('full_access_user_schema.t1', 'id'); -NOTICE: Copying data from local table... - create_distributed_table --------------------------- - -(1 row) - --- verify the owner of the shards for the distributed tables -SELECT result FROM run_command_on_workers($cmd$ - SELECT tableowner FROM pg_tables WHERE - true - AND schemaname = 'full_access_user_schema' - AND tablename LIKE 't1_%' - LIMIT 1; -$cmd$); - result --------------- - usage_access - usage_access -(2 rows) - --- a user with all privileges on a schema should be able to distribute tables -SET ROLE full_access; -CREATE TABLE full_access_user_schema.t2(id int); -SELECT create_distributed_table('full_access_user_schema.t2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -RESET ROLE; --- a user with all privileges on a schema should be able to upgrade a distributed table to --- a reference table -SET ROLE full_access; -BEGIN; -CREATE TABLE full_access_user_schema.r1(id int); -SET LOCAL citus.shard_count TO 1; -SELECT create_distributed_table('full_access_user_schema.r1', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT upgrade_to_reference_table('full_access_user_schema.r1'); - upgrade_to_reference_table ----------------------------- - -(1 row) - -COMMIT; -RESET ROLE; --- the super user should be able to upgrade a distributed table to a reference table, even --- if it is owned by another user -SET ROLE full_access; -BEGIN; -CREATE TABLE full_access_user_schema.r2(id int); -SET LOCAL citus.shard_count TO 1; -SELECT create_distributed_table('full_access_user_schema.r2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -COMMIT; -RESET ROLE; --- the usage_access should not be able to upgrade the table -SET ROLE usage_access; -SELECT upgrade_to_reference_table('full_access_user_schema.r2'); -ERROR: must be owner of relation r2 -RESET ROLE; --- the super user should be able -SELECT upgrade_to_reference_table('full_access_user_schema.r2'); - upgrade_to_reference_table ----------------------------- - -(1 row) - --- verify the owner of the shards for the reference table -SELECT result FROM run_command_on_workers($cmd$ - SELECT tableowner FROM pg_tables WHERE - true - AND schemaname = 'full_access_user_schema' - AND tablename LIKE 'r2_%' - LIMIT 1; -$cmd$); - result -------------- - full_access - full_access -(2 rows) - --- super user should be the only one being able to call worker_cleanup_job_schema_cache -SELECT worker_cleanup_job_schema_cache(); - worker_cleanup_job_schema_cache ---------------------------------- - -(1 row) - -SET ROLE full_access; -SELECT worker_cleanup_job_schema_cache(); -ERROR: permission denied for function worker_cleanup_job_schema_cache -SET ROLE usage_access; -SELECT worker_cleanup_job_schema_cache(); -ERROR: permission denied for function worker_cleanup_job_schema_cache -SET ROLE read_access; -SELECT worker_cleanup_job_schema_cache(); -ERROR: permission denied for function worker_cleanup_job_schema_cache -SET ROLE no_access; -SELECT worker_cleanup_job_schema_cache(); -ERROR: permission denied for function worker_cleanup_job_schema_cache -RESET ROLE; --- to test access to files created during repartition we will create some on worker 1 -\c - - - :worker_1_port -SET ROLE full_access; -SELECT worker_hash_partition_table(42,1,'SELECT a FROM generate_series(1,100) AS a', 'a', 23, ARRAY[-2147483648, -1073741824, 0, 1073741824]::int4[]); - worker_hash_partition_table ------------------------------ - -(1 row) - -RESET ROLE; --- all attempts for transfer are initiated from other workers -\c - - - :worker_2_port --- super user should not be able to copy files created by a user -SELECT worker_fetch_partition_file(42, 1, 1, 1, 'localhost', :worker_1_port); -WARNING: could not open file "base/pgsql_job_cache/job_0042/task_000001/p_00001.10": No such file or directory -CONTEXT: while executing command on localhost:57637 -ERROR: could not receive file "base/pgsql_job_cache/job_0042/task_000001/p_00001" from localhost:57637 --- different user should not be able to fetch partition file -SET ROLE usage_access; -SELECT worker_fetch_partition_file(42, 1, 1, 1, 'localhost', :worker_1_port); -WARNING: could not open file "base/pgsql_job_cache/job_0042/task_000001/p_00001.18007": No such file or directory -CONTEXT: while executing command on localhost:57637 -ERROR: could not receive file "base/pgsql_job_cache/job_0042/task_000001/p_00001" from localhost:57637 --- only the user whom created the files should be able to fetch -SET ROLE full_access; -SELECT worker_fetch_partition_file(42, 1, 1, 1, 'localhost', :worker_1_port); - worker_fetch_partition_file ------------------------------ - -(1 row) - -RESET ROLE; --- now we will test that only the user who owns the fetched file is able to merge it into --- a table --- test that no other user can merge the downloaded file before the task is being tracked -SET ROLE usage_access; -SELECT worker_merge_files_into_table(42, 1, ARRAY['a'], ARRAY['integer']); -ERROR: job schema does not exist -DETAIL: must be superuser to use public schema -RESET ROLE; -SET ROLE full_access; --- use the side effect of this function to have a schema to use, otherwise only the super --- user could call worker_merge_files_into_table and store the results in public, which is --- not what we want -SELECT task_tracker_assign_task(42, 1, 'SELECT 1'); - task_tracker_assign_task --------------------------- - -(1 row) - -RESET ROLE; --- test that no other user can merge the downloaded file after the task is being tracked -SET ROLE usage_access; -SELECT worker_merge_files_into_table(42, 1, ARRAY['a'], ARRAY['integer']); -ERROR: must be owner of schema pg_merge_job_0042 -RESET ROLE; --- test that the super user is unable to read the contents of the intermediate file, --- although it does create the table -SELECT worker_merge_files_into_table(42, 1, ARRAY['a'], ARRAY['integer']); -WARNING: Task file "task_000001.18003" does not have expected suffix ".10" - worker_merge_files_into_table -------------------------------- - -(1 row) - -SELECT count(*) FROM pg_merge_job_0042.task_000001; - count -------- - 0 -(1 row) - -DROP TABLE pg_merge_job_0042.task_000001; -- drop table so we can reuse the same files for more tests -SET ROLE full_access; -SELECT worker_merge_files_into_table(42, 1, ARRAY['a'], ARRAY['integer']); - worker_merge_files_into_table -------------------------------- - -(1 row) - -SELECT count(*) FROM pg_merge_job_0042.task_000001; - count -------- - 25 -(1 row) - -DROP TABLE pg_merge_job_0042.task_000001; -- drop table so we can reuse the same files for more tests -RESET ROLE; --- test that no other user can merge files and run query on the already fetched files -SET ROLE usage_access; -SELECT worker_merge_files_and_run_query(42, 1, - 'CREATE TABLE task_000001_merge(merge_column_0 int)', - 'CREATE TABLE task_000001 (a) AS SELECT sum(merge_column_0) FROM task_000001_merge' -); -ERROR: must be owner of schema pg_merge_job_0042 -RESET ROLE; --- test that the super user is unable to read the contents of the partitioned files after --- trying to merge with run query -SELECT worker_merge_files_and_run_query(42, 1, - 'CREATE TABLE task_000001_merge(merge_column_0 int)', - 'CREATE TABLE task_000001 (a) AS SELECT sum(merge_column_0) FROM task_000001_merge' -); -WARNING: Task file "task_000001.18003" does not have expected suffix ".10" - worker_merge_files_and_run_query ----------------------------------- - -(1 row) - -SELECT count(*) FROM pg_merge_job_0042.task_000001_merge; - count -------- - 0 -(1 row) - -SELECT count(*) FROM pg_merge_job_0042.task_000001; - count -------- - 1 -(1 row) - -DROP TABLE pg_merge_job_0042.task_000001, pg_merge_job_0042.task_000001_merge; -- drop table so we can reuse the same files for more tests --- test that the owner of the task can merge files and run query correctly -SET ROLE full_access; -SELECT worker_merge_files_and_run_query(42, 1, - 'CREATE TABLE task_000001_merge(merge_column_0 int)', - 'CREATE TABLE task_000001 (a) AS SELECT sum(merge_column_0) FROM task_000001_merge' -); - worker_merge_files_and_run_query ----------------------------------- - -(1 row) - --- test that owner of task cannot execute arbitrary sql -SELECT worker_merge_files_and_run_query(42, 1, - 'CREATE TABLE task_000002_merge(merge_column_0 int)', - 'DROP USER usage_access' -); -ERROR: permission denied to drop role -CONTEXT: SQL statement "DROP USER usage_access" -SELECT worker_merge_files_and_run_query(42, 1, - 'DROP USER usage_access', - 'CREATE TABLE task_000002 (a) AS SELECT sum(merge_column_0) FROM task_000002_merge' -); -ERROR: permission denied to drop role -CONTEXT: SQL statement "DROP USER usage_access" -SELECT count(*) FROM pg_merge_job_0042.task_000001_merge; - count -------- - 25 -(1 row) - -SELECT count(*) FROM pg_merge_job_0042.task_000001; - count -------- - 1 -(1 row) - -DROP TABLE pg_merge_job_0042.task_000001, pg_merge_job_0042.task_000001_merge; -- drop table so we can reuse the same files for more tests -RESET ROLE; -\c - - - :master_port -DROP SCHEMA full_access_user_schema CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table full_access_user_schema.t1 -drop cascades to table full_access_user_schema.t2 -drop cascades to table full_access_user_schema.r1 -drop cascades to table full_access_user_schema.r2 -DROP TABLE - my_table, - my_table_with_data, - my_role_table_with_data, - singleshard, - test, - test_coloc; -DROP USER full_access; -DROP USER read_access; -DROP USER no_access; -DROP ROLE some_role; diff --git a/src/test/regress/expected/multi_mx_call_0.out b/src/test/regress/expected/multi_mx_call_0.out deleted file mode 100644 index 4a040ff18..000000000 --- a/src/test/regress/expected/multi_mx_call_0.out +++ /dev/null @@ -1,372 +0,0 @@ --- Test passing off CALL to mx workers -create schema multi_mx_call; -set search_path to multi_mx_call, public; --- Create worker-local tables to test procedure calls were routed -set citus.shard_replication_factor to 2; -set citus.replication_model to 'statement'; --- This table requires specific settings, create before getting into things -create table mx_call_dist_table_replica(id int, val int); -select create_distributed_table('mx_call_dist_table_replica', 'id'); - create_distributed_table --------------------------- - -(1 row) - -insert into mx_call_dist_table_replica values (9,1),(8,2),(7,3),(6,4),(5,5); -set citus.shard_replication_factor to 1; -set citus.replication_model to 'streaming'; --- --- Create tables and procedures we want to use in tests --- -create table mx_call_dist_table_1(id int, val int); -select create_distributed_table('mx_call_dist_table_1', 'id'); - create_distributed_table --------------------------- - -(1 row) - -insert into mx_call_dist_table_1 values (3,1),(4,5),(9,2),(6,5),(3,5); -create table mx_call_dist_table_2(id int, val int); -select create_distributed_table('mx_call_dist_table_2', 'id'); - create_distributed_table --------------------------- - -(1 row) - -insert into mx_call_dist_table_2 values (1,1),(1,2),(2,2),(3,3),(3,4); -create table mx_call_dist_table_bigint(id bigint, val bigint); -select create_distributed_table('mx_call_dist_table_bigint', 'id'); - create_distributed_table --------------------------- - -(1 row) - -insert into mx_call_dist_table_bigint values (1,1),(1,2),(2,2),(3,3),(3,4); -create table mx_call_dist_table_ref(id int, val int); -select create_reference_table('mx_call_dist_table_ref'); - create_reference_table ------------------------- - -(1 row) - -insert into mx_call_dist_table_ref values (2,7),(1,8),(2,8),(1,8),(2,8); -create type mx_call_enum as enum ('A', 'S', 'D', 'F'); -create table mx_call_dist_table_enum(id int, key mx_call_enum); -select create_distributed_table('mx_call_dist_table_enum', 'key'); - create_distributed_table --------------------------- - -(1 row) - -insert into mx_call_dist_table_enum values (1,'S'),(2,'A'),(3,'D'),(4,'F'); -CREATE PROCEDURE mx_call_proc(x int, INOUT y int) -LANGUAGE plpgsql AS $$ -BEGIN - -- groupid is 0 in coordinator and non-zero in workers, so by using it here - -- we make sure the procedure is being executed in the worker. - y := x + (select case groupid when 0 then 1 else 0 end from pg_dist_local_group); - -- we also make sure that we can run distributed queries in the procedures - -- that are routed to the workers. - y := y + (select sum(t1.val + t2.val) from multi_mx_call.mx_call_dist_table_1 t1 join multi_mx_call.mx_call_dist_table_2 t2 on t1.id = t2.id); -END;$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE mx_call_proc(x int, INOUT y int) - ^ -CREATE PROCEDURE mx_call_proc_bigint(x bigint, INOUT y bigint) -LANGUAGE plpgsql AS $$ -BEGIN - y := x + y * 2; -END;$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE mx_call_proc_bigint(x bigint, INOUT y bigin... - ^ --- create another procedure which verifies: --- 1. we work fine with multiple return columns --- 2. we work fine in combination with custom types -CREATE PROCEDURE mx_call_proc_custom_types(INOUT x mx_call_enum, INOUT y mx_call_enum) -LANGUAGE plpgsql AS $$ -BEGIN - y := x; - x := (select case groupid when 0 then 'F' else 'S' end from pg_dist_local_group); -END;$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE mx_call_proc_custom_types(INOUT x mx_call_e... - ^ --- Test that undistributed procedures have no issue executing -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ -call multi_mx_call.mx_call_proc_custom_types('S', 'A'); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc_custom_types('S', 'A'); - ^ --- Same for unqualified names -call mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call mx_call_proc(2, 0); - ^ -call mx_call_proc_custom_types('S', 'A'); -ERROR: syntax error at or near "call" -LINE 1: call mx_call_proc_custom_types('S', 'A'); - ^ --- Mark both procedures as distributed ... -select create_distributed_function('mx_call_proc(int,int)'); -ERROR: function "mx_call_proc(int,int)" does not exist -LINE 1: select create_distributed_function('mx_call_proc(int,int)'); - ^ -select create_distributed_function('mx_call_proc_bigint(bigint,bigint)'); -ERROR: function "mx_call_proc_bigint(bigint,bigint)" does not exist -LINE 1: select create_distributed_function('mx_call_proc_bigint(bigi... - ^ -select create_distributed_function('mx_call_proc_custom_types(mx_call_enum,mx_call_enum)'); -ERROR: function "mx_call_proc_custom_types(mx_call_enum,mx_call_enum)" does not exist -LINE 1: select create_distributed_function('mx_call_proc_custom_type... - ^ --- We still don't route them to the workers, because they aren't --- colocated with any distributed tables. -SET client_min_messages TO DEBUG1; -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ -call mx_call_proc_bigint(4, 2); -ERROR: syntax error at or near "call" -LINE 1: call mx_call_proc_bigint(4, 2); - ^ -call multi_mx_call.mx_call_proc_custom_types('S', 'A'); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc_custom_types('S', 'A'); - ^ --- Mark them as colocated with a table. Now we should route them to workers. -select colocate_proc_with_table('mx_call_proc', 'mx_call_dist_table_1'::regclass, 1); - colocate_proc_with_table --------------------------- - -(1 row) - -select colocate_proc_with_table('mx_call_proc_bigint', 'mx_call_dist_table_bigint'::regclass, 1); - colocate_proc_with_table --------------------------- - -(1 row) - -select colocate_proc_with_table('mx_call_proc_custom_types', 'mx_call_dist_table_enum'::regclass, 1); - colocate_proc_with_table --------------------------- - -(1 row) - -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ -call multi_mx_call.mx_call_proc_custom_types('S', 'A'); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc_custom_types('S', 'A'); - ^ -call mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call mx_call_proc(2, 0); - ^ -call mx_call_proc_custom_types('S', 'A'); -ERROR: syntax error at or near "call" -LINE 1: call mx_call_proc_custom_types('S', 'A'); - ^ --- Test implicit cast of int to bigint -call mx_call_proc_bigint(4, 2); -ERROR: syntax error at or near "call" -LINE 1: call mx_call_proc_bigint(4, 2); - ^ --- We don't allow distributing calls inside transactions -begin; -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ -commit; --- Drop the table colocated with mx_call_proc_custom_types. Now it shouldn't --- be routed to workers anymore. -SET client_min_messages TO NOTICE; -drop table mx_call_dist_table_enum; -SET client_min_messages TO DEBUG1; -call multi_mx_call.mx_call_proc_custom_types('S', 'A'); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc_custom_types('S', 'A'); - ^ --- Make sure we do bounds checking on distributed argument index --- This also tests that we have cache invalidation for pg_dist_object updates -select colocate_proc_with_table('mx_call_proc', 'mx_call_dist_table_1'::regclass, -1); - colocate_proc_with_table --------------------------- - -(1 row) - -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ -select colocate_proc_with_table('mx_call_proc', 'mx_call_dist_table_1'::regclass, 2); - colocate_proc_with_table --------------------------- - -(1 row) - -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ --- We don't currently support colocating with reference tables -select colocate_proc_with_table('mx_call_proc', 'mx_call_dist_table_ref'::regclass, 1); - colocate_proc_with_table --------------------------- - -(1 row) - -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ --- We don't currently support colocating with replicated tables -select colocate_proc_with_table('mx_call_proc', 'mx_call_dist_table_replica'::regclass, 1); - colocate_proc_with_table --------------------------- - -(1 row) - -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ -SET client_min_messages TO NOTICE; -drop table mx_call_dist_table_replica; -SET client_min_messages TO DEBUG1; -select colocate_proc_with_table('mx_call_proc', 'mx_call_dist_table_1'::regclass, 1); - colocate_proc_with_table --------------------------- - -(1 row) - --- Test that we handle transactional constructs correctly inside a procedure --- that is routed to the workers. -CREATE PROCEDURE mx_call_proc_tx(x int) LANGUAGE plpgsql AS $$ -BEGIN - INSERT INTO multi_mx_call.mx_call_dist_table_1 VALUES (x, -1), (x+1, 4); - COMMIT; - UPDATE multi_mx_call.mx_call_dist_table_1 SET val = val+1 WHERE id >= x; - ROLLBACK; - -- Now do the final update! - UPDATE multi_mx_call.mx_call_dist_table_1 SET val = val-1 WHERE id >= x; -END;$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE mx_call_proc_tx(x int) LANGUAGE plpgsql AS ... - ^ --- before distribution ... -CALL multi_mx_call.mx_call_proc_tx(10); -ERROR: syntax error at or near "CALL" -LINE 1: CALL multi_mx_call.mx_call_proc_tx(10); - ^ --- after distribution ... -select create_distributed_function('mx_call_proc_tx(int)', '$1', 'mx_call_dist_table_1'); -ERROR: function "mx_call_proc_tx(int)" does not exist -LINE 1: select create_distributed_function('mx_call_proc_tx(int)', '... - ^ -CALL multi_mx_call.mx_call_proc_tx(20); -ERROR: syntax error at or near "CALL" -LINE 1: CALL multi_mx_call.mx_call_proc_tx(20); - ^ -SELECT id, val FROM mx_call_dist_table_1 ORDER BY id, val; - id | val -----+----- - 3 | 1 - 3 | 5 - 4 | 5 - 6 | 5 - 9 | 2 -(5 rows) - --- Test that we properly propagate errors raised from procedures. -CREATE PROCEDURE mx_call_proc_raise(x int) LANGUAGE plpgsql AS $$ -BEGIN - RAISE WARNING 'warning'; - RAISE EXCEPTION 'error'; -END;$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE mx_call_proc_raise(x int) LANGUAGE plpgsql ... - ^ -select create_distributed_function('mx_call_proc_raise(int)', '$1', 'mx_call_dist_table_1'); -ERROR: function "mx_call_proc_raise(int)" does not exist -LINE 1: select create_distributed_function('mx_call_proc_raise(int)'... - ^ -\set VERBOSITY terse -call multi_mx_call.mx_call_proc_raise(2); -ERROR: syntax error at or near "call" at character 1 -\set VERBOSITY default --- Test that we don't propagate to non-metadata worker nodes -select stop_metadata_sync_to_node('localhost', :worker_1_port); - stop_metadata_sync_to_node ----------------------------- - -(1 row) - -select stop_metadata_sync_to_node('localhost', :worker_2_port); - stop_metadata_sync_to_node ----------------------------- - -(1 row) - -call multi_mx_call.mx_call_proc(2, 0); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, 0); - ^ -SET client_min_messages TO NOTICE; -select start_metadata_sync_to_node('localhost', :worker_1_port); - start_metadata_sync_to_node ------------------------------ - -(1 row) - -select start_metadata_sync_to_node('localhost', :worker_2_port); - start_metadata_sync_to_node ------------------------------ - -(1 row) - --- stop_metadata_sync_to_node()/start_metadata_sync_to_node() might make --- worker backend caches inconsistent. Reconnect to coordinator to use --- new worker connections, hence new backends. -\c - - - :master_port -SET search_path to multi_mx_call, public; -SET client_min_messages TO DEBUG1; --- --- Test non-const parameter values --- -CREATE FUNCTION mx_call_add(int, int) RETURNS int - AS 'select $1 + $2;' LANGUAGE SQL IMMUTABLE; -SELECT create_distributed_function('mx_call_add(int,int)'); -DEBUG: switching to sequential query execution mode -DETAIL: A distributed function is created. To make sure subsequent commands see the type correctly we need to make sure to use only one connection for all future commands - create_distributed_function ------------------------------ - -(1 row) - --- non-const distribution parameters cannot be pushed down -call multi_mx_call.mx_call_proc(2, mx_call_add(3, 4)); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(2, mx_call_add(3, 4)); - ^ --- non-const parameter can be pushed down -call multi_mx_call.mx_call_proc(multi_mx_call.mx_call_add(3, 4), 2); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(multi_mx_call.mx_call_add(3,... - ^ --- volatile parameter cannot be pushed down -call multi_mx_call.mx_call_proc(floor(random())::int, 2); -ERROR: syntax error at or near "call" -LINE 1: call multi_mx_call.mx_call_proc(floor(random())::int, 2); - ^ -reset client_min_messages; -\set VERBOSITY terse -drop schema multi_mx_call cascade; -NOTICE: drop cascades to 6 other objects diff --git a/src/test/regress/expected/multi_mx_explain_0.out b/src/test/regress/expected/multi_mx_explain_0.out deleted file mode 100644 index e94b2ef1d..000000000 --- a/src/test/regress/expected/multi_mx_explain_0.out +++ /dev/null @@ -1,869 +0,0 @@ --- --- MULTI_MX_EXPLAIN --- -ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 1320000; -\c - - - :worker_1_port -\c - - - :worker_2_port -\c - - - :master_port -\a\t -SET citus.task_executor_type TO 'real-time'; -SET citus.explain_distributed_queries TO on; -VACUUM ANALYZE lineitem_mx; -VACUUM ANALYZE orders_mx; -VACUUM ANALYZE customer_mx; -VACUUM ANALYZE supplier_mx; -\c - - - :worker_1_port --- Function that parses explain output as JSON -CREATE FUNCTION explain_json(query text) -RETURNS jsonb -AS $BODY$ -DECLARE - result jsonb; -BEGIN - EXECUTE format('EXPLAIN (FORMAT JSON) %s', query) INTO result; - RETURN result; -END; -$BODY$ LANGUAGE plpgsql; --- Function that parses explain output as XML -CREATE FUNCTION explain_xml(query text) -RETURNS xml -AS $BODY$ -DECLARE - result xml; -BEGIN - EXECUTE format('EXPLAIN (FORMAT XML) %s', query) INTO result; - RETURN result; -END; -$BODY$ LANGUAGE plpgsql; -\c - - - :worker_2_port --- Function that parses explain output as JSON -CREATE FUNCTION explain_json(query text) -RETURNS jsonb -AS $BODY$ -DECLARE - result jsonb; -BEGIN - EXECUTE format('EXPLAIN (FORMAT JSON) %s', query) INTO result; - RETURN result; -END; -$BODY$ LANGUAGE plpgsql; --- Function that parses explain output as XML -CREATE FUNCTION explain_xml(query text) -RETURNS xml -AS $BODY$ -DECLARE - result xml; -BEGIN - EXECUTE format('EXPLAIN (FORMAT XML) %s', query) INTO result; - RETURN result; -END; -$BODY$ LANGUAGE plpgsql; --- Test Text format -EXPLAIN (COSTS FALSE, FORMAT TEXT) - SELECT l_quantity, count(*) count_quantity FROM lineitem_mx - GROUP BY l_quantity ORDER BY count_quantity, l_quantity; -Sort - Sort Key: COALESCE((pg_catalog.sum((COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))))::bigint, '0'::bigint), remote_scan.l_quantity - -> HashAggregate - Group Key: remote_scan.l_quantity - -> Custom Scan (Citus Real-Time) - Task Count: 16 - Tasks Shown: One of 16 - -> Task - Node: host=localhost port=57637 dbname=regression - -> HashAggregate - Group Key: l_quantity - -> Seq Scan on lineitem_mx_1220052 lineitem_mx --- Test JSON format -EXPLAIN (COSTS FALSE, FORMAT JSON) - SELECT l_quantity, count(*) count_quantity FROM lineitem_mx - GROUP BY l_quantity ORDER BY count_quantity, l_quantity; -[ - { - "Plan": { - "Node Type": "Sort", - "Parallel Aware": false, - "Sort Key": ["COALESCE((pg_catalog.sum((COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))))::bigint, '0'::bigint)", "remote_scan.l_quantity"], - "Plans": [ - { - "Node Type": "Aggregate", - "Strategy": "Hashed", - "Partial Mode": "Simple", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Group Key": ["remote_scan.l_quantity"], - "Plans": [ - { - "Node Type": "Custom Scan", - "Parent Relationship": "Outer", - "Custom Plan Provider": "Citus Real-Time", - "Parallel Aware": false, - "Distributed Query": { - "Job": { - "Task Count": 16, - "Tasks Shown": "One of 16", - "Tasks": [ - { - "Node": "host=localhost port=57637 dbname=regression", - "Remote Plan": [ - [ - { - "Plan": { - "Node Type": "Aggregate", - "Strategy": "Hashed", - "Partial Mode": "Simple", - "Parallel Aware": false, - "Group Key": ["l_quantity"], - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Relation Name": "lineitem_mx_1220052", - "Alias": "lineitem_mx" - } - ] - } - } - ] - - ] - } - ] - } - } - } - ] - } - ] - } - } -] --- Validate JSON format -SELECT true AS valid FROM explain_json($$ - SELECT l_quantity, count(*) count_quantity FROM lineitem_mx - GROUP BY l_quantity ORDER BY count_quantity, l_quantity$$); -t -\c - - - :worker_1_port --- Test XML format -EXPLAIN (COSTS FALSE, FORMAT XML) - SELECT l_quantity, count(*) count_quantity FROM lineitem_mx - GROUP BY l_quantity ORDER BY count_quantity, l_quantity; - - - - Sort - false - - COALESCE((pg_catalog.sum((COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))))::bigint, '0'::bigint) - remote_scan.l_quantity - - - - Aggregate - Hashed - Simple - Outer - false - - remote_scan.l_quantity - - - - Custom Scan - Outer - Citus Real-Time - false - - - 16 - One of 16 - - - host=localhost port=57637 dbname=regression - - - - - Aggregate - Hashed - Simple - false - - l_quantity - - - - Seq Scan - Outer - false - lineitem_mx_1220052 - lineitem_mx - - - - - - - - - - - - - - - - - --- Validate XML format -SELECT true AS valid FROM explain_xml($$ - SELECT l_quantity, count(*) count_quantity FROM lineitem_mx - GROUP BY l_quantity ORDER BY count_quantity, l_quantity$$); -t --- Test YAML format -EXPLAIN (COSTS FALSE, FORMAT YAML) - SELECT l_quantity, count(*) count_quantity FROM lineitem_mx - GROUP BY l_quantity ORDER BY count_quantity, l_quantity; -- Plan: - Node Type: "Sort" - Parallel Aware: false - Sort Key: - - "COALESCE((pg_catalog.sum((COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))))::bigint, '0'::bigint)" - - "remote_scan.l_quantity" - Plans: - - Node Type: "Aggregate" - Strategy: "Hashed" - Partial Mode: "Simple" - Parent Relationship: "Outer" - Parallel Aware: false - Group Key: - - "remote_scan.l_quantity" - Plans: - - Node Type: "Custom Scan" - Parent Relationship: "Outer" - Custom Plan Provider: "Citus Real-Time" - Parallel Aware: false - Distributed Query: - Job: - Task Count: 16 - Tasks Shown: "One of 16" - Tasks: - - Node: "host=localhost port=57637 dbname=regression" - Remote Plan: - - Plan: - Node Type: "Aggregate" - Strategy: "Hashed" - Partial Mode: "Simple" - Parallel Aware: false - Group Key: - - "l_quantity" - Plans: - - Node Type: "Seq Scan" - Parent Relationship: "Outer" - Parallel Aware: false - Relation Name: "lineitem_mx_1220052" - Alias: "lineitem_mx" - --- Test Text format -EXPLAIN (COSTS FALSE, FORMAT TEXT) - SELECT l_quantity, count(*) count_quantity FROM lineitem_mx - GROUP BY l_quantity ORDER BY count_quantity, l_quantity; -Sort - Sort Key: COALESCE((pg_catalog.sum((COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))))::bigint, '0'::bigint), remote_scan.l_quantity - -> HashAggregate - Group Key: remote_scan.l_quantity - -> Custom Scan (Citus Real-Time) - Task Count: 16 - Tasks Shown: One of 16 - -> Task - Node: host=localhost port=57637 dbname=regression - -> HashAggregate - Group Key: l_quantity - -> Seq Scan on lineitem_mx_1220052 lineitem_mx -\c - - - :worker_2_port --- Test verbose -EXPLAIN (COSTS FALSE, VERBOSE TRUE) - SELECT sum(l_quantity) / avg(l_quantity) FROM lineitem_mx; -Aggregate - Output: (sum(remote_scan."?column?") / (sum(remote_scan."?column?_1") / pg_catalog.sum(remote_scan."?column?_2"))) - -> Custom Scan (Citus Real-Time) - Output: remote_scan."?column?", remote_scan."?column?_1", remote_scan."?column?_2" - Task Count: 16 - Tasks Shown: One of 16 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - Output: sum(l_quantity), sum(l_quantity), count(l_quantity) - -> Seq Scan on public.lineitem_mx_1220052 lineitem_mx - Output: l_orderkey, l_partkey, l_suppkey, l_linenumber, l_quantity, l_extendedprice, l_discount, l_tax, l_returnflag, l_linestatus, l_shipdate, l_commitdate, l_receiptdate, l_shipinstruct, l_shipmode, l_comment --- Test join -EXPLAIN (COSTS FALSE) - SELECT * FROM lineitem_mx - JOIN orders_mx ON l_orderkey = o_orderkey AND l_quantity < 5.0 - ORDER BY l_quantity LIMIT 10; -Limit - -> Sort - Sort Key: remote_scan.l_quantity - -> Custom Scan (Citus Real-Time) - Task Count: 16 - Tasks Shown: One of 16 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Limit - -> Sort - Sort Key: lineitem_mx.l_quantity - -> Hash Join - Hash Cond: (lineitem_mx.l_orderkey = orders_mx.o_orderkey) - -> Seq Scan on lineitem_mx_1220052 lineitem_mx - Filter: (l_quantity < 5.0) - -> Hash - -> Seq Scan on orders_mx_1220068 orders_mx --- Test insert -EXPLAIN (COSTS FALSE) - INSERT INTO lineitem_mx VALUES(1,0); -Custom Scan (Citus Router) - Task Count: 1 - Tasks Shown: All - -> Task - Node: host=localhost port=57637 dbname=regression - -> Insert on lineitem_mx_1220052 - -> Result --- Test update -EXPLAIN (COSTS FALSE) - UPDATE lineitem_mx - SET l_suppkey = 12 - WHERE l_orderkey = 1 AND l_partkey = 0; -Custom Scan (Citus Router) - Task Count: 1 - Tasks Shown: All - -> Task - Node: host=localhost port=57637 dbname=regression - -> Update on lineitem_mx_1220052 lineitem_mx - -> Index Scan using lineitem_mx_pkey_1220052 on lineitem_mx_1220052 lineitem_mx - Index Cond: (l_orderkey = 1) - Filter: (l_partkey = 0) --- Test delete -EXPLAIN (COSTS FALSE) - DELETE FROM lineitem_mx - WHERE l_orderkey = 1 AND l_partkey = 0; -Custom Scan (Citus Router) - Task Count: 1 - Tasks Shown: All - -> Task - Node: host=localhost port=57637 dbname=regression - -> Delete on lineitem_mx_1220052 lineitem_mx - -> Index Scan using lineitem_mx_pkey_1220052 on lineitem_mx_1220052 lineitem_mx - Index Cond: (l_orderkey = 1) - Filter: (l_partkey = 0) --- make the outputs more consistent -VACUUM ANALYZE lineitem_mx; -VACUUM ANALYZE orders_mx; -VACUUM ANALYZE customer_mx; -VACUUM ANALYZE supplier_mx; --- Test single-shard SELECT -EXPLAIN (COSTS FALSE) - SELECT l_quantity FROM lineitem_mx WHERE l_orderkey = 5; -Custom Scan (Citus Router) - Task Count: 1 - Tasks Shown: All - -> Task - Node: host=localhost port=57638 dbname=regression - -> Index Scan using lineitem_mx_pkey_1220055 on lineitem_mx_1220055 lineitem_mx - Index Cond: (l_orderkey = 5) -SELECT true AS valid FROM explain_xml($$ - SELECT l_quantity FROM lineitem_mx WHERE l_orderkey = 5$$); -t -SELECT true AS valid FROM explain_json($$ - SELECT l_quantity FROM lineitem_mx WHERE l_orderkey = 5$$); -t --- Test CREATE TABLE ... AS -EXPLAIN (COSTS FALSE) - CREATE TABLE explain_result AS - SELECT * FROM lineitem_mx; -Custom Scan (Citus Real-Time) - Task Count: 16 - Tasks Shown: One of 16 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Seq Scan on lineitem_mx_1220052 lineitem_mx --- Test all tasks output -SET citus.explain_all_tasks TO on; -EXPLAIN (COSTS FALSE) - SELECT avg(l_linenumber) FROM lineitem_mx WHERE l_orderkey > 9030; -Aggregate - -> Custom Scan (Citus Real-Time) - Task Count: 16 - Tasks Shown: All - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220052 on lineitem_mx_1220052 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57638 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220053 on lineitem_mx_1220053 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220054 on lineitem_mx_1220054 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57638 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220055 on lineitem_mx_1220055 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220056 on lineitem_mx_1220056 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57638 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220057 on lineitem_mx_1220057 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220058 on lineitem_mx_1220058 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57638 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220059 on lineitem_mx_1220059 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220060 on lineitem_mx_1220060 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57638 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220061 on lineitem_mx_1220061 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220062 on lineitem_mx_1220062 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57638 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220063 on lineitem_mx_1220063 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220064 on lineitem_mx_1220064 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57638 dbname=regression - -> Aggregate - -> Seq Scan on lineitem_mx_1220065 lineitem_mx - Filter: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220066 on lineitem_mx_1220066 lineitem_mx - Index Cond: (l_orderkey > 9030) - -> Task - Node: host=localhost port=57638 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220067 on lineitem_mx_1220067 lineitem_mx - Index Cond: (l_orderkey > 9030) -SELECT true AS valid FROM explain_xml($$ - SELECT avg(l_linenumber) FROM lineitem_mx WHERE l_orderkey > 9030$$); -t -SELECT true AS valid FROM explain_json($$ - SELECT avg(l_linenumber) FROM lineitem_mx WHERE l_orderkey > 9030$$); -t --- Test track tracker -SET citus.task_executor_type TO 'task-tracker'; -SET citus.explain_all_tasks TO off; -EXPLAIN (COSTS FALSE) - SELECT avg(l_linenumber) FROM lineitem_mx WHERE l_orderkey > 9030; -Aggregate - -> Custom Scan (Citus Task-Tracker) - Task Count: 16 - Tasks Shown: One of 16 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Index Only Scan using lineitem_mx_pkey_1220052 on lineitem_mx_1220052 lineitem_mx - Index Cond: (l_orderkey > 9030) --- Test re-partition join -EXPLAIN (COSTS FALSE) - SELECT count(*) - FROM lineitem_mx, orders_mx, customer_mx, supplier_mx - WHERE l_orderkey = o_orderkey - AND o_custkey = c_custkey - AND l_suppkey = s_suppkey; -Aggregate - -> Custom Scan (Citus Task-Tracker) - Task Count: 16 - Tasks Shown: One of 16 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Aggregate - -> Hash Join - Hash Cond: (lineitem_mx.l_orderkey = orders_mx.o_orderkey) - -> Hash Join - Hash Cond: (supplier_mx.s_suppkey = lineitem_mx.l_suppkey) - -> Seq Scan on supplier_mx_1220087 supplier_mx - -> Hash - -> Seq Scan on lineitem_mx_1220052 lineitem_mx - -> Hash - -> Hash Join - Hash Cond: (customer_mx.c_custkey = orders_mx.o_custkey) - -> Seq Scan on customer_mx_1220084 customer_mx - -> Hash - -> Seq Scan on orders_mx_1220068 orders_mx -EXPLAIN (COSTS FALSE, FORMAT JSON) - SELECT count(*) - FROM lineitem_mx, orders_mx, customer_mx, supplier_mx - WHERE l_orderkey = o_orderkey - AND o_custkey = c_custkey - AND l_suppkey = s_suppkey; -[ - { - "Plan": { - "Node Type": "Aggregate", - "Strategy": "Plain", - "Partial Mode": "Simple", - "Parallel Aware": false, - "Plans": [ - { - "Node Type": "Custom Scan", - "Parent Relationship": "Outer", - "Custom Plan Provider": "Citus Task-Tracker", - "Parallel Aware": false, - "Distributed Query": { - "Job": { - "Task Count": 16, - "Tasks Shown": "One of 16", - "Tasks": [ - { - "Node": "host=localhost port=57637 dbname=regression", - "Remote Plan": [ - [ - { - "Plan": { - "Node Type": "Aggregate", - "Strategy": "Plain", - "Partial Mode": "Simple", - "Parallel Aware": false, - "Plans": [ - { - "Node Type": "Hash Join", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Join Type": "Inner", - "Hash Cond": "(lineitem_mx.l_orderkey = orders_mx.o_orderkey)", - "Plans": [ - { - "Node Type": "Hash Join", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Join Type": "Inner", - "Hash Cond": "(supplier_mx.s_suppkey = lineitem_mx.l_suppkey)", - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Relation Name": "supplier_mx_1220087", - "Alias": "supplier_mx" - }, - { - "Node Type": "Hash", - "Parent Relationship": "Inner", - "Parallel Aware": false, - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Relation Name": "lineitem_mx_1220052", - "Alias": "lineitem_mx" - } - ] - } - ] - }, - { - "Node Type": "Hash", - "Parent Relationship": "Inner", - "Parallel Aware": false, - "Plans": [ - { - "Node Type": "Hash Join", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Join Type": "Inner", - "Hash Cond": "(customer_mx.c_custkey = orders_mx.o_custkey)", - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Relation Name": "customer_mx_1220084", - "Alias": "customer_mx" - }, - { - "Node Type": "Hash", - "Parent Relationship": "Inner", - "Parallel Aware": false, - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Parallel Aware": false, - "Relation Name": "orders_mx_1220068", - "Alias": "orders_mx" - } - ] - } - ] - } - ] - } - ] - } - ] - } - } - ] - - ] - } - ] - } - } - } - ] - } - } -] -SELECT true AS valid FROM explain_json($$ - SELECT count(*) - FROM lineitem_mx, orders_mx, customer_mx, supplier_mx - WHERE l_orderkey = o_orderkey - AND o_custkey = c_custkey - AND l_suppkey = s_suppkey$$); -t -EXPLAIN (COSTS FALSE, FORMAT XML) - SELECT count(*) - FROM lineitem_mx, orders_mx, customer_mx, supplier_mx - WHERE l_orderkey = o_orderkey - AND o_custkey = c_custkey - AND l_suppkey = s_suppkey; - - - - Aggregate - Plain - Simple - false - - - Custom Scan - Outer - Citus Task-Tracker - false - - - 16 - One of 16 - - - host=localhost port=57637 dbname=regression - - - - - Aggregate - Plain - Simple - false - - - Hash Join - Outer - false - Inner - (lineitem_mx.l_orderkey = orders_mx.o_orderkey) - - - Hash Join - Outer - false - Inner - (supplier_mx.s_suppkey = lineitem_mx.l_suppkey) - - - Seq Scan - Outer - false - supplier_mx_1220087 - supplier_mx - - - Hash - Inner - false - - - Seq Scan - Outer - false - lineitem_mx_1220052 - lineitem_mx - - - - - - - Hash - Inner - false - - - Hash Join - Outer - false - Inner - (customer_mx.c_custkey = orders_mx.o_custkey) - - - Seq Scan - Outer - false - customer_mx_1220084 - customer_mx - - - Hash - Inner - false - - - Seq Scan - Outer - false - orders_mx_1220068 - orders_mx - - - - - - - - - - - - - - - - - - - - - - - -SELECT true AS valid FROM explain_xml($$ - SELECT count(*) - FROM lineitem_mx, orders_mx, customer_mx, supplier_mx - WHERE l_orderkey = o_orderkey - AND o_custkey = c_custkey - AND l_suppkey = s_suppkey$$); -t -EXPLAIN (COSTS FALSE, FORMAT YAML) - SELECT count(*) - FROM lineitem_mx, orders_mx, customer_mx, supplier_mx - WHERE l_orderkey = o_orderkey - AND o_custkey = c_custkey - AND l_suppkey = s_suppkey; -- Plan: - Node Type: "Aggregate" - Strategy: "Plain" - Partial Mode: "Simple" - Parallel Aware: false - Plans: - - Node Type: "Custom Scan" - Parent Relationship: "Outer" - Custom Plan Provider: "Citus Task-Tracker" - Parallel Aware: false - Distributed Query: - Job: - Task Count: 16 - Tasks Shown: "One of 16" - Tasks: - - Node: "host=localhost port=57637 dbname=regression" - Remote Plan: - - Plan: - Node Type: "Aggregate" - Strategy: "Plain" - Partial Mode: "Simple" - Parallel Aware: false - Plans: - - Node Type: "Hash Join" - Parent Relationship: "Outer" - Parallel Aware: false - Join Type: "Inner" - Hash Cond: "(lineitem_mx.l_orderkey = orders_mx.o_orderkey)" - Plans: - - Node Type: "Hash Join" - Parent Relationship: "Outer" - Parallel Aware: false - Join Type: "Inner" - Hash Cond: "(supplier_mx.s_suppkey = lineitem_mx.l_suppkey)" - Plans: - - Node Type: "Seq Scan" - Parent Relationship: "Outer" - Parallel Aware: false - Relation Name: "supplier_mx_1220087" - Alias: "supplier_mx" - - Node Type: "Hash" - Parent Relationship: "Inner" - Parallel Aware: false - Plans: - - Node Type: "Seq Scan" - Parent Relationship: "Outer" - Parallel Aware: false - Relation Name: "lineitem_mx_1220052" - Alias: "lineitem_mx" - - Node Type: "Hash" - Parent Relationship: "Inner" - Parallel Aware: false - Plans: - - Node Type: "Hash Join" - Parent Relationship: "Outer" - Parallel Aware: false - Join Type: "Inner" - Hash Cond: "(customer_mx.c_custkey = orders_mx.o_custkey)" - Plans: - - Node Type: "Seq Scan" - Parent Relationship: "Outer" - Parallel Aware: false - Relation Name: "customer_mx_1220084" - Alias: "customer_mx" - - Node Type: "Hash" - Parent Relationship: "Inner" - Parallel Aware: false - Plans: - - Node Type: "Seq Scan" - Parent Relationship: "Outer" - Parallel Aware: false - Relation Name: "orders_mx_1220068" - Alias: "orders_mx" - diff --git a/src/test/regress/expected/multi_mx_partitioning_0.out b/src/test/regress/expected/multi_mx_partitioning_0.out deleted file mode 100644 index 35bde35d1..000000000 --- a/src/test/regress/expected/multi_mx_partitioning_0.out +++ /dev/null @@ -1,270 +0,0 @@ --- --- Distributed Partitioned Table MX Tests --- -SET citus.next_shard_id TO 1700000; -SET citus.shard_count TO 4; -SET citus.shard_replication_factor TO 1; --- make sure wen can create partitioning tables in MX -SET citus.replication_model TO 'streaming'; -SELECT start_metadata_sync_to_node('localhost', :worker_1_port); - start_metadata_sync_to_node ------------------------------ - -(1 row) - --- 1-) Distributing partitioned table --- create partitioned table -CREATE TABLE partitioning_test(id int, time date) PARTITION BY RANGE (time); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE partitioning_test(id int, time date) PARTITION ... - ^ - --- create its partitions -CREATE TABLE partitioning_test_2009 PARTITION OF partitioning_test FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE partitioning_test_2009 PARTITION OF partitionin... - ^ -CREATE TABLE partitioning_test_2010 PARTITION OF partitioning_test FOR VALUES FROM ('2010-01-01') TO ('2011-01-01'); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE partitioning_test_2010 PARTITION OF partitionin... - ^ --- load some data and distribute tables -INSERT INTO partitioning_test VALUES (1, '2009-06-06'); -ERROR: relation "partitioning_test" does not exist -LINE 1: INSERT INTO partitioning_test VALUES (1, '2009-06-06'); - ^ -INSERT INTO partitioning_test VALUES (2, '2010-07-07'); -ERROR: relation "partitioning_test" does not exist -LINE 1: INSERT INTO partitioning_test VALUES (2, '2010-07-07'); - ^ -INSERT INTO partitioning_test_2009 VALUES (3, '2009-09-09'); -ERROR: relation "partitioning_test_2009" does not exist -LINE 1: INSERT INTO partitioning_test_2009 VALUES (3, '2009-09-09'); - ^ -INSERT INTO partitioning_test_2010 VALUES (4, '2010-03-03'); -ERROR: relation "partitioning_test_2010" does not exist -LINE 1: INSERT INTO partitioning_test_2010 VALUES (4, '2010-03-03'); - ^ --- distribute partitioned table -SELECT create_distributed_table('partitioning_test', 'id'); -ERROR: relation "partitioning_test" does not exist -LINE 1: SELECT create_distributed_table('partitioning_test', 'id'); - ^ --- see from MX node, the data is loaded to shards -\c - - - :worker_1_port -SELECT * FROM partitioning_test ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 1: SELECT * FROM partitioning_test ORDER BY 1; - ^ --- see from MX node, partitioned table and its partitions are distributed -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_test', 'partitioning_test_2009', 'partitioning_test_2010') -ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 6: logicalrelid IN ('partitioning_test', 'partitioning_test_20... - ^ -SELECT - logicalrelid, count(*) -FROM pg_dist_shard - WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2009', 'partitioning_test_2010') -GROUP BY - logicalrelid -ORDER BY - 1,2; -ERROR: relation "partitioning_test" does not exist -LINE 4: WHERE logicalrelid IN ('partitioning_test', 'partitioning_t... - ^ --- see from MX node, partitioning hierarchy is built -SELECT inhrelid::regclass FROM pg_inherits WHERE inhparent = 'partitioning_test'::regclass ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 1: ...elid::regclass FROM pg_inherits WHERE inhparent = 'partition... - ^ -\c - - - :master_port -SET citus.replication_model TO 'streaming'; -SET citus.shard_replication_factor TO 1; --- 2-) Creating partition of a distributed table -CREATE TABLE partitioning_test_2011 PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE partitioning_test_2011 PARTITION OF partitionin... - ^ --- see from MX node, new partition is automatically distributed as well -\c - - - :worker_1_port -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_test', 'partitioning_test_2011') -ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 6: logicalrelid IN ('partitioning_test', 'partitioning_test_20... - ^ -SELECT - logicalrelid, count(*) -FROM pg_dist_shard - WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2011') -GROUP BY - logicalrelid -ORDER BY - 1,2; -ERROR: relation "partitioning_test" does not exist -LINE 4: WHERE logicalrelid IN ('partitioning_test', 'partitioning_t... - ^ --- see from MX node, partitioning hierarchy is built -SELECT inhrelid::regclass FROM pg_inherits WHERE inhparent = 'partitioning_test'::regclass ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 1: ...elid::regclass FROM pg_inherits WHERE inhparent = 'partition... - ^ -\c - - - :master_port -SET citus.replication_model TO 'streaming'; -SET citus.shard_replication_factor TO 1; --- 3-) Attaching non distributed table to a distributed table -CREATE TABLE partitioning_test_2012(id int, time date); --- load some data -INSERT INTO partitioning_test_2012 VALUES (5, '2012-06-06'); -INSERT INTO partitioning_test_2012 VALUES (6, '2012-07-07'); -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2012 FOR VALUES FROM ('2012-01-01') TO ('2013-01-01'); -ERROR: syntax error at or near "ATTACH" -LINE 1: ALTER TABLE partitioning_test ATTACH PARTITION partitioning_... - ^ --- see from MX node, attached partition is distributed as well -\c - - - :worker_1_port -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_test', 'partitioning_test_2012') -ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 6: logicalrelid IN ('partitioning_test', 'partitioning_test_20... - ^ -SELECT - logicalrelid, count(*) -FROM pg_dist_shard - WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2012') -GROUP BY - logicalrelid -ORDER BY - 1,2; -ERROR: relation "partitioning_test" does not exist -LINE 4: WHERE logicalrelid IN ('partitioning_test', 'partitioning_t... - ^ --- see from MX node, see the data is loaded to shards -SELECT * FROM partitioning_test ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 1: SELECT * FROM partitioning_test ORDER BY 1; - ^ --- see from MX node, partitioning hierarchy is built -SELECT inhrelid::regclass FROM pg_inherits WHERE inhparent = 'partitioning_test'::regclass ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 1: ...elid::regclass FROM pg_inherits WHERE inhparent = 'partition... - ^ -\c - - - :master_port -SET citus.replication_model TO 'streaming'; -SET citus.shard_replication_factor TO 1; --- 4-) Attaching distributed table to distributed table -CREATE TABLE partitioning_test_2013(id int, time date); -SELECT create_distributed_table('partitioning_test_2013', 'id'); - create_distributed_table --------------------------- - -(1 row) - --- load some data -INSERT INTO partitioning_test_2013 VALUES (7, '2013-06-06'); -INSERT INTO partitioning_test_2013 VALUES (8, '2013-07-07'); -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2013 FOR VALUES FROM ('2013-01-01') TO ('2014-01-01'); -ERROR: syntax error at or near "ATTACH" -LINE 1: ALTER TABLE partitioning_test ATTACH PARTITION partitioning_... - ^ --- see from MX node, see the data is loaded to shards -\c - - - :worker_1_port -SELECT * FROM partitioning_test ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 1: SELECT * FROM partitioning_test ORDER BY 1; - ^ --- see from MX node, partitioning hierarchy is built -SELECT inhrelid::regclass FROM pg_inherits WHERE inhparent = 'partitioning_test'::regclass ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 1: ...elid::regclass FROM pg_inherits WHERE inhparent = 'partition... - ^ -\c - - - :master_port --- 5-) Detaching partition of the partitioned table -ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2009; -ERROR: syntax error at or near "DETACH" -LINE 1: ALTER TABLE partitioning_test DETACH PARTITION partitioning_... - ^ --- see from MX node, partitioning hierarchy is built -\c - - - :worker_1_port -SELECT inhrelid::regclass FROM pg_inherits WHERE inhparent = 'partitioning_test'::regclass ORDER BY 1; -ERROR: relation "partitioning_test" does not exist -LINE 1: ...elid::regclass FROM pg_inherits WHERE inhparent = 'partition... - ^ --- make sure DROPping from worker node is not allowed -DROP TABLE partitioning_test; -ERROR: table "partitioning_test" does not exist -\c - - - :master_port --- make sure we can repeatedly call start_metadata_sync_to_node -SELECT start_metadata_sync_to_node('localhost', :worker_1_port); - start_metadata_sync_to_node ------------------------------ - -(1 row) - -SELECT start_metadata_sync_to_node('localhost', :worker_1_port); - start_metadata_sync_to_node ------------------------------ - -(1 row) - -SELECT start_metadata_sync_to_node('localhost', :worker_1_port); - start_metadata_sync_to_node ------------------------------ - -(1 row) - --- make sure we can drop partitions -DROP TABLE partitioning_test_2009; -ERROR: table "partitioning_test_2009" does not exist -DROP TABLE partitioning_test_2010; -ERROR: table "partitioning_test_2010" does not exist --- make sure we can drop partitioned table -DROP TABLE partitioning_test; -ERROR: table "partitioning_test" does not exist -DROP TABLE IF EXISTS partitioning_test_2013; --- test schema drop with partitioned tables -SET citus.replication_model TO 'streaming'; -SET citus.shard_replication_factor TO 1; -CREATE SCHEMA partition_test; -SET SEARCH_PATH TO partition_test; -CREATE TABLE partition_parent_table(a int, b int, c int) PARTITION BY RANGE (b); -ERROR: syntax error at or near "PARTITION" -LINE 1: ...TABLE partition_parent_table(a int, b int, c int) PARTITION ... - ^ -SELECT create_distributed_table('partition_parent_table', 'a'); -ERROR: relation "partition_parent_table" does not exist -LINE 1: SELECT create_distributed_table('partition_parent_table', 'a... - ^ -CREATE TABLE partition_0 PARTITION OF partition_parent_table FOR VALUES FROM (1) TO (10); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE partition_0 PARTITION OF partition_parent_table... - ^ -CREATE TABLE partition_1 PARTITION OF partition_parent_table FOR VALUES FROM (10) TO (20); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE partition_1 PARTITION OF partition_parent_table... - ^ -CREATE TABLE partition_2 PARTITION OF partition_parent_table FOR VALUES FROM (20) TO (30); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE partition_2 PARTITION OF partition_parent_table... - ^ -CREATE TABLE partition_3 PARTITION OF partition_parent_table FOR VALUES FROM (30) TO (40); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE TABLE partition_3 PARTITION OF partition_parent_table... - ^ -DROP SCHEMA partition_test CASCADE; -RESET SEARCH_PATH; diff --git a/src/test/regress/expected/multi_partitioning.out b/src/test/regress/expected/multi_partitioning.out index 6aee55f4a..bb5f6f192 100644 --- a/src/test/regress/expected/multi_partitioning.out +++ b/src/test/regress/expected/multi_partitioning.out @@ -4,13 +4,6 @@ SET citus.next_shard_id TO 1660000; SET citus.shard_count TO 4; SET citus.shard_replication_factor TO 1; -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS server_version_above_ten; - server_version_above_ten --------------------------- - t -(1 row) - -- -- Distributed Partitioned Table Creation Tests -- diff --git a/src/test/regress/expected/multi_partitioning_0.out b/src/test/regress/expected/multi_partitioning_0.out deleted file mode 100644 index 7ffd8edf0..000000000 --- a/src/test/regress/expected/multi_partitioning_0.out +++ /dev/null @@ -1,1869 +0,0 @@ --- --- Distributed Partitioned Table Tests --- -SET citus.next_shard_id TO 1660000; -SET citus.shard_count TO 4; -SET citus.shard_replication_factor TO 1; -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS server_version_above_ten; - server_version_above_ten --------------------------- - f -(1 row) - --- --- Distributed Partitioned Table Creation Tests --- --- 1-) Distributing partitioned table --- create partitioned table -CREATE TABLE partitioning_test(id int, time date) PARTITION BY RANGE (time); -CREATE TABLE partitioning_hash_test(id int, subid int) PARTITION BY HASH(subid); -ERROR: unrecognized partitioning strategy "hash" --- create its partitions -CREATE TABLE partitioning_test_2009 PARTITION OF partitioning_test FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -CREATE TABLE partitioning_test_2010 PARTITION OF partitioning_test FOR VALUES FROM ('2010-01-01') TO ('2011-01-01'); -CREATE TABLE partitioning_hash_test_0 PARTITION OF partitioning_hash_test FOR VALUES WITH (MODULUS 3, REMAINDER 0); -ERROR: syntax error at or near "WITH" -LINE 1: ..._0 PARTITION OF partitioning_hash_test FOR VALUES WITH (MODU... - ^ -CREATE TABLE partitioning_hash_test_1 PARTITION OF partitioning_hash_test FOR VALUES WITH (MODULUS 3, REMAINDER 1); -ERROR: syntax error at or near "WITH" -LINE 1: ..._1 PARTITION OF partitioning_hash_test FOR VALUES WITH (MODU... - ^ --- load some data and distribute tables -INSERT INTO partitioning_test VALUES (1, '2009-06-06'); -INSERT INTO partitioning_test VALUES (2, '2010-07-07'); -INSERT INTO partitioning_test_2009 VALUES (3, '2009-09-09'); -INSERT INTO partitioning_test_2010 VALUES (4, '2010-03-03'); -INSERT INTO partitioning_hash_test VALUES (1, 2); -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: INSERT INTO partitioning_hash_test VALUES (1, 2); - ^ -INSERT INTO partitioning_hash_test VALUES (2, 13); -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: INSERT INTO partitioning_hash_test VALUES (2, 13); - ^ -INSERT INTO partitioning_hash_test VALUES (3, 7); -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: INSERT INTO partitioning_hash_test VALUES (3, 7); - ^ -INSERT INTO partitioning_hash_test VALUES (4, 4); -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: INSERT INTO partitioning_hash_test VALUES (4, 4); - ^ --- distribute partitioned table -SELECT create_distributed_table('partitioning_test', 'id'); -NOTICE: Copying data from local table... -NOTICE: Copying data from local table... - create_distributed_table --------------------------- - -(1 row) - -SELECT create_distributed_table('partitioning_hash_test', 'id'); -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: SELECT create_distributed_table('partitioning_hash_test', 'i... - ^ --- see the data is loaded to shards -SELECT * FROM partitioning_test ORDER BY 1; - id | time -----+------------ - 1 | 06-06-2009 - 2 | 07-07-2010 - 3 | 09-09-2009 - 4 | 03-03-2010 -(4 rows) - -SELECT * FROM partitioning_hash_test ORDER BY 1; -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: SELECT * FROM partitioning_hash_test ORDER BY 1; - ^ --- see partitioned table and its partitions are distributed -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_test', 'partitioning_test_2009', 'partitioning_test_2010') -ORDER BY 1; - logicalrelid ------------------------- - partitioning_test - partitioning_test_2009 - partitioning_test_2010 -(3 rows) - -SELECT - logicalrelid, count(*) -FROM pg_dist_shard - WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2009', 'partitioning_test_2010') -GROUP BY - logicalrelid -ORDER BY - 1,2; - logicalrelid | count -------------------------+------- - partitioning_test | 4 - partitioning_test_2009 | 4 - partitioning_test_2010 | 4 -(3 rows) - -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_hash_test', 'partitioning_hash_test_0', 'partitioning_hash_test_1') -ORDER BY 1; -ERROR: relation "partitioning_hash_test" does not exist -LINE 6: logicalrelid IN ('partitioning_hash_test', 'partitioning_ha... - ^ -SELECT - logicalrelid, count(*) -FROM pg_dist_shard - WHERE logicalrelid IN ('partitioning_hash_test', 'partitioning_hash_test_0', 'partitioning_hash_test_1') -GROUP BY - logicalrelid -ORDER BY - 1,2; -ERROR: relation "partitioning_hash_test" does not exist -LINE 4: WHERE logicalrelid IN ('partitioning_hash_test', 'partition... - ^ --- 2-) Creating partition of a distributed table -CREATE TABLE partitioning_test_2011 PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); --- new partition is automatically distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_test', 'partitioning_test_2011') -ORDER BY 1; - logicalrelid ------------------------- - partitioning_test - partitioning_test_2011 -(2 rows) - -SELECT - logicalrelid, count(*) -FROM pg_dist_shard - WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2011') -GROUP BY - logicalrelid -ORDER BY - 1,2; - logicalrelid | count -------------------------+------- - partitioning_test | 4 - partitioning_test_2011 | 4 -(2 rows) - --- 3-) Attaching non distributed table to a distributed table -CREATE TABLE partitioning_test_2012(id int, time date); --- load some data -INSERT INTO partitioning_test_2012 VALUES (5, '2012-06-06'); -INSERT INTO partitioning_test_2012 VALUES (6, '2012-07-07'); -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2012 FOR VALUES FROM ('2012-01-01') TO ('2013-01-01'); -NOTICE: Copying data from local table... --- attached partition is distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_test', 'partitioning_test_2012') -ORDER BY 1; - logicalrelid ------------------------- - partitioning_test - partitioning_test_2012 -(2 rows) - -SELECT - logicalrelid, count(*) -FROM pg_dist_shard - WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2012') -GROUP BY - logicalrelid -ORDER BY - 1,2; - logicalrelid | count -------------------------+------- - partitioning_test | 4 - partitioning_test_2012 | 4 -(2 rows) - --- try to insert a new data to hash partitioned table --- no partition is defined for value 5 -INSERT INTO partitioning_hash_test VALUES (8, 5); -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: INSERT INTO partitioning_hash_test VALUES (8, 5); - ^ -INSERT INTO partitioning_hash_test VALUES (9, 12); -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: INSERT INTO partitioning_hash_test VALUES (9, 12); - ^ -CREATE TABLE partitioning_hash_test_2 (id int, subid int); -INSERT INTO partitioning_hash_test_2 VALUES (8, 5); -ALTER TABLE partitioning_hash_test ATTACH PARTITION partitioning_hash_test_2 FOR VALUES WITH (MODULUS 3, REMAINDER 2); -ERROR: syntax error at or near "WITH" -LINE 1: ...ACH PARTITION partitioning_hash_test_2 FOR VALUES WITH (MODU... - ^ -INSERT INTO partitioning_hash_test VALUES (9, 12); -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: INSERT INTO partitioning_hash_test VALUES (9, 12); - ^ --- see the data is loaded to shards -SELECT * FROM partitioning_test ORDER BY 1; - id | time -----+------------ - 1 | 06-06-2009 - 2 | 07-07-2010 - 3 | 09-09-2009 - 4 | 03-03-2010 - 5 | 06-06-2012 - 6 | 07-07-2012 -(6 rows) - -SELECT * FROM partitioning_hash_test ORDER BY 1; -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: SELECT * FROM partitioning_hash_test ORDER BY 1; - ^ --- 4-) Attaching distributed table to distributed table -CREATE TABLE partitioning_test_2013(id int, time date); -SELECT create_distributed_table('partitioning_test_2013', 'id'); - create_distributed_table --------------------------- - -(1 row) - --- load some data -INSERT INTO partitioning_test_2013 VALUES (7, '2013-06-06'); -INSERT INTO partitioning_test_2013 VALUES (8, '2013-07-07'); -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2013 FOR VALUES FROM ('2013-01-01') TO ('2014-01-01'); --- see the data is loaded to shards -SELECT * FROM partitioning_test ORDER BY 1; - id | time -----+------------ - 1 | 06-06-2009 - 2 | 07-07-2010 - 3 | 09-09-2009 - 4 | 03-03-2010 - 5 | 06-06-2012 - 6 | 07-07-2012 - 7 | 06-06-2013 - 8 | 07-07-2013 -(8 rows) - --- 5-) Failure cases while creating distributed partitioned tables --- cannot distribute a partition if its parent is not distributed -CREATE TABLE partitioning_test_failure(id int, time date) PARTITION BY RANGE (time); -CREATE TABLE partitioning_test_failure_2009 PARTITION OF partitioning_test_failure FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -SELECT create_distributed_table('partitioning_test_failure_2009', 'id'); -ERROR: cannot distribute relation "partitioning_test_failure_2009" which is partition of "partitioning_test_failure" -DETAIL: Citus does not support distributing partitions if their parent is not distributed table. -HINT: Distribute the partitioned table "partitioning_test_failure" instead. --- only hash distributed tables can have partitions -SELECT create_distributed_table('partitioning_test_failure', 'id', 'append'); -ERROR: distributing partitioned tables in only supported for hash-distributed tables -SELECT create_distributed_table('partitioning_test_failure', 'id', 'range'); -ERROR: distributing partitioned tables in only supported for hash-distributed tables -SELECT create_reference_table('partitioning_test_failure'); -ERROR: distributing partitioned tables in only supported for hash-distributed tables -SET citus.shard_replication_factor TO 1; --- non-distributed tables cannot have distributed partitions; -DROP TABLE partitioning_test_failure_2009; -CREATE TABLE partitioning_test_failure_2009(id int, time date); -SELECT create_distributed_table('partitioning_test_failure_2009', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE partitioning_test_failure ATTACH PARTITION partitioning_test_failure_2009 FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -ERROR: non-distributed tables cannot have distributed partitions -HINT: Distribute the partitioned table "partitioning_test_failure_2009" instead --- multi-level partitioning is not allowed -DROP TABLE partitioning_test_failure_2009; -CREATE TABLE partitioning_test_failure_2009 PARTITION OF partitioning_test_failure FOR VALUES FROM ('2009-01-01') TO ('2010-01-01') PARTITION BY RANGE (time); -SELECT create_distributed_table('partitioning_test_failure', 'id'); -ERROR: distributing multi-level partitioned tables is not supported -DETAIL: Relation "partitioning_test_failure_2009" is partitioned table itself and it is also partition of relation "partitioning_test_failure". --- multi-level partitioning is not allowed in different order -DROP TABLE partitioning_test_failure_2009; -SELECT create_distributed_table('partitioning_test_failure', 'id'); - create_distributed_table --------------------------- - -(1 row) - -CREATE TABLE partitioning_test_failure_2009 PARTITION OF partitioning_test_failure FOR VALUES FROM ('2009-01-01') TO ('2010-01-01') PARTITION BY RANGE (time); -ERROR: distributing multi-level partitioned tables is not supported -DETAIL: Relation "partitioning_test_failure_2009" is partitioned table itself and it is also partition of relation "partitioning_test_failure". --- --- DMLs in distributed partitioned tables --- --- test COPY --- COPY data to partitioned table -COPY partitioning_test FROM STDIN WITH CSV; --- COPY data to partition directly -COPY partitioning_test_2009 FROM STDIN WITH CSV; --- see the data is loaded to shards -SELECT * FROM partitioning_test WHERE id >= 9 ORDER BY 1; - id | time -----+------------ - 9 | 01-01-2009 - 10 | 01-01-2010 - 11 | 01-01-2011 - 12 | 01-01-2012 - 13 | 01-02-2009 - 14 | 01-03-2009 -(6 rows) - --- test INSERT --- INSERT INTO the partitioned table -INSERT INTO partitioning_test VALUES(15, '2009-02-01'); -INSERT INTO partitioning_test VALUES(16, '2010-02-01'); -INSERT INTO partitioning_test VALUES(17, '2011-02-01'); -INSERT INTO partitioning_test VALUES(18, '2012-02-01'); --- INSERT INTO the partitions directly table -INSERT INTO partitioning_test VALUES(19, '2009-02-02'); -INSERT INTO partitioning_test VALUES(20, '2010-02-02'); --- see the data is loaded to shards -SELECT * FROM partitioning_test WHERE id >= 15 ORDER BY 1; - id | time -----+------------ - 15 | 02-01-2009 - 16 | 02-01-2010 - 17 | 02-01-2011 - 18 | 02-01-2012 - 19 | 02-02-2009 - 20 | 02-02-2010 -(6 rows) - --- test INSERT/SELECT --- INSERT/SELECT from partition to partitioned table -INSERT INTO partitioning_test SELECT * FROM partitioning_test_2011; --- INSERT/SELECT from partitioned table to partition -INSERT INTO partitioning_test_2012 SELECT * FROM partitioning_test WHERE time >= '2012-01-01' AND time < '2013-01-01'; --- see the data is loaded to shards (rows in the given range should be duplicated) -SELECT * FROM partitioning_test WHERE time >= '2011-01-01' AND time < '2013-01-01' ORDER BY 1; - id | time -----+------------ - 5 | 06-06-2012 - 5 | 06-06-2012 - 6 | 07-07-2012 - 6 | 07-07-2012 - 11 | 01-01-2011 - 11 | 01-01-2011 - 12 | 01-01-2012 - 12 | 01-01-2012 - 17 | 02-01-2011 - 17 | 02-01-2011 - 18 | 02-01-2012 - 18 | 02-01-2012 -(12 rows) - --- test UPDATE --- UPDATE partitioned table -UPDATE partitioning_test SET time = '2013-07-07' WHERE id = 7; --- UPDATE partition directly -UPDATE partitioning_test_2013 SET time = '2013-08-08' WHERE id = 8; --- see the data is updated -SELECT * FROM partitioning_test WHERE id = 7 OR id = 8 ORDER BY 1; - id | time -----+------------ - 7 | 07-07-2013 - 8 | 08-08-2013 -(2 rows) - --- UPDATE that tries to move a row to a non-existing partition (this should fail) -UPDATE partitioning_test SET time = '2020-07-07' WHERE id = 7; -ERROR: new row for relation "partitioning_test_2013_1660021" violates partition constraint -DETAIL: Failing row contains (7, 2020-07-07). -CONTEXT: while executing command on localhost:57638 --- UPDATE with subqueries on partitioned table -UPDATE - partitioning_test -SET - time = time + INTERVAL '1 day' -WHERE - id IN (SELECT id FROM partitioning_test WHERE id = 1); --- UPDATE with subqueries on partition -UPDATE - partitioning_test_2009 -SET - time = time + INTERVAL '1 month' -WHERE - id IN (SELECT id FROM partitioning_test WHERE id = 2); --- see the data is updated -SELECT * FROM partitioning_test WHERE id = 1 OR id = 2 ORDER BY 1; - id | time -----+------------ - 1 | 06-07-2009 - 2 | 07-07-2010 -(2 rows) - --- test DELETE --- DELETE from partitioned table -DELETE FROM partitioning_test WHERE id = 9; --- DELETE from partition directly -DELETE FROM partitioning_test_2010 WHERE id = 10; --- see the data is deleted -SELECT * FROM partitioning_test WHERE id = 9 OR id = 10 ORDER BY 1; - id | time -----+------ -(0 rows) - --- create default partition -CREATE TABLE partitioning_test_default PARTITION OF partitioning_test DEFAULT; -ERROR: syntax error at or near "DEFAULT" -LINE 1: ...tioning_test_default PARTITION OF partitioning_test DEFAULT; - ^ -\d+ partitioning_test - Table "public.partitioning_test" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - id | integer | | | | plain | | - time | date | | | | plain | | -Partition key: RANGE ("time") -Partitions: partitioning_test_2009 FOR VALUES FROM ('01-01-2009') TO ('01-01-2010'), - partitioning_test_2010 FOR VALUES FROM ('01-01-2010') TO ('01-01-2011'), - partitioning_test_2011 FOR VALUES FROM ('01-01-2011') TO ('01-01-2012'), - partitioning_test_2012 FOR VALUES FROM ('01-01-2012') TO ('01-01-2013'), - partitioning_test_2013 FOR VALUES FROM ('01-01-2013') TO ('01-01-2014') - -INSERT INTO partitioning_test VALUES(21, '2014-02-02'); -ERROR: no partition of relation "partitioning_test_1660002" found for row -DETAIL: Partition key of the failing row contains ("time") = (2014-02-02). -CONTEXT: while executing command on localhost:57637 -INSERT INTO partitioning_test VALUES(22, '2015-04-02'); -ERROR: no partition of relation "partitioning_test_1660003" found for row -DETAIL: Partition key of the failing row contains ("time") = (2015-04-02). -CONTEXT: while executing command on localhost:57638 --- see they are inserted into default partition -SELECT * FROM partitioning_test WHERE id > 20 ORDER BY 1, 2; - id | time -----+------ -(0 rows) - -SELECT * FROM partitioning_test_default ORDER BY 1, 2; -ERROR: relation "partitioning_test_default" does not exist -LINE 1: SELECT * FROM partitioning_test_default ORDER BY 1, 2; - ^ --- create a new partition (will fail) -CREATE TABLE partitioning_test_2014 PARTITION OF partitioning_test FOR VALUES FROM ('2014-01-01') TO ('2015-01-01'); -BEGIN; -ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_default; -ERROR: relation "partitioning_test_default" does not exist -CREATE TABLE partitioning_test_2014 PARTITION OF partitioning_test FOR VALUES FROM ('2014-01-01') TO ('2015-01-01'); -ERROR: current transaction is aborted, commands ignored until end of transaction block -INSERT INTO partitioning_test SELECT * FROM partitioning_test_default WHERE time >= '2014-01-01' AND time < '2015-01-01'; -ERROR: current transaction is aborted, commands ignored until end of transaction block -DELETE FROM partitioning_test_default WHERE time >= '2014-01-01' AND time < '2015-01-01'; -ERROR: current transaction is aborted, commands ignored until end of transaction block -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_default DEFAULT; -ERROR: syntax error at or near "DEFAULT" -LINE 1: ...ing_test ATTACH PARTITION partitioning_test_default DEFAULT; - ^ -END; --- see data is in the table, but some moved out from default partition -SELECT * FROM partitioning_test WHERE id > 20 ORDER BY 1, 2; - id | time -----+------ -(0 rows) - -SELECT * FROM partitioning_test_default ORDER BY 1, 2; -ERROR: relation "partitioning_test_default" does not exist -LINE 1: SELECT * FROM partitioning_test_default ORDER BY 1, 2; - ^ --- multi-shard UPDATE on partitioned table -UPDATE partitioning_test SET time = time + INTERVAL '1 day'; --- see rows are UPDATED -SELECT * FROM partitioning_test ORDER BY 1; - id | time -----+------------ - 1 | 06-08-2009 - 2 | 07-08-2010 - 3 | 09-10-2009 - 4 | 03-04-2010 - 5 | 06-07-2012 - 5 | 06-07-2012 - 6 | 07-08-2012 - 6 | 07-08-2012 - 7 | 07-08-2013 - 8 | 08-09-2013 - 11 | 01-02-2011 - 11 | 01-02-2011 - 12 | 01-02-2012 - 12 | 01-02-2012 - 13 | 01-03-2009 - 14 | 01-04-2009 - 15 | 02-02-2009 - 16 | 02-02-2010 - 17 | 02-02-2011 - 17 | 02-02-2011 - 18 | 02-02-2012 - 18 | 02-02-2012 - 19 | 02-03-2009 - 20 | 02-03-2010 -(24 rows) - --- multi-shard UPDATE on partition directly -UPDATE partitioning_test_2009 SET time = time + INTERVAL '1 day'; --- see rows are UPDATED -SELECT * FROM partitioning_test_2009 ORDER BY 1; - id | time -----+------------ - 1 | 06-09-2009 - 3 | 09-11-2009 - 13 | 01-04-2009 - 14 | 01-05-2009 - 15 | 02-03-2009 - 19 | 02-04-2009 -(6 rows) - --- test multi-shard UPDATE which fails in workers (updated value is outside of partition bounds) -UPDATE partitioning_test_2009 SET time = time + INTERVAL '6 month'; -ERROR: new row for relation "partitioning_test_2009_1660005" violates partition constraint -DETAIL: Failing row contains (3, 2010-03-11). -CONTEXT: while executing command on localhost:57638 --- --- DDL in distributed partitioned tables --- --- test CREATE INDEX --- CREATE INDEX on partitioned table - this will error out --- on earlier versions of postgres earlier than 11. -CREATE INDEX partitioning_index ON partitioning_test(id); -ERROR: cannot create index on partitioned table "partitioning_test" --- CREATE INDEX on partition -CREATE INDEX partitioning_2009_index ON partitioning_test_2009(id); --- CREATE INDEX CONCURRENTLY on partition -CREATE INDEX CONCURRENTLY partitioned_2010_index ON partitioning_test_2010(id); --- see index is created -SELECT tablename, indexname FROM pg_indexes WHERE tablename LIKE 'partitioning_test_%' ORDER BY indexname; - tablename | indexname -------------------------+------------------------- - partitioning_test_2010 | partitioned_2010_index - partitioning_test_2009 | partitioning_2009_index -(2 rows) - --- test drop --- indexes created on parent table can only be dropped on parent table --- ie using the same index name --- following will fail -DROP INDEX partitioning_test_2009_id_idx; -ERROR: index "partitioning_test_2009_id_idx" does not exist --- but dropping index on parent table will succeed -DROP INDEX partitioning_index; -ERROR: index "partitioning_index" does not exist --- this index was already created on partition table -DROP INDEX partitioning_2009_index; --- test drop index on non-distributed, partitioned table -CREATE TABLE non_distributed_partitioned_table(a int, b int) PARTITION BY RANGE (a); -CREATE TABLE non_distributed_partitioned_table_1 PARTITION OF non_distributed_partitioned_table -FOR VALUES FROM (0) TO (10); -CREATE INDEX non_distributed_partitioned_table_index ON non_distributed_partitioned_table(a); -ERROR: cannot create index on partitioned table "non_distributed_partitioned_table" --- see index is created -SELECT tablename, indexname FROM pg_indexes WHERE tablename LIKE 'non_distributed_partitioned_table_%' ORDER BY indexname; - tablename | indexname ------------+----------- -(0 rows) - --- drop the index and see it is dropped -DROP INDEX non_distributed_partitioned_table_index; -ERROR: index "non_distributed_partitioned_table_index" does not exist -SELECT tablename, indexname FROM pg_indexes WHERE tablename LIKE 'non_distributed%' ORDER BY indexname; - tablename | indexname ------------+----------- -(0 rows) - --- test add COLUMN --- add COLUMN to partitioned table -ALTER TABLE partitioning_test ADD new_column int; --- add COLUMN to partition - this will error out -ALTER TABLE partitioning_test_2010 ADD new_column_2 int; -ERROR: cannot add column to a partition --- see additional column is created -SELECT name, type FROM table_attrs WHERE relid = 'partitioning_test'::regclass ORDER BY 1; - name | type -------------+--------- - id | integer - new_column | integer - time | date -(3 rows) - -SELECT name, type FROM table_attrs WHERE relid = 'partitioning_test_2010'::regclass ORDER BY 1; - name | type -------------+--------- - id | integer - new_column | integer - time | date -(3 rows) - --- test add PRIMARY KEY --- add PRIMARY KEY to partitioned table - this will error out -ALTER TABLE partitioning_test ADD CONSTRAINT partitioning_primary PRIMARY KEY (id); -ERROR: primary key constraints are not supported on partitioned tables -LINE 1: ALTER TABLE partitioning_test ADD CONSTRAINT partitioning_pr... - ^ --- ADD PRIMARY KEY to partition -ALTER TABLE partitioning_test_2009 ADD CONSTRAINT partitioning_2009_primary PRIMARY KEY (id); --- see PRIMARY KEY is created -SELECT - table_name, - constraint_name, - constraint_type -FROM - information_schema.table_constraints -WHERE - table_name = 'partitioning_test_2009' AND - constraint_name = 'partitioning_2009_primary'; - table_name | constraint_name | constraint_type -------------------------+---------------------------+----------------- - partitioning_test_2009 | partitioning_2009_primary | PRIMARY KEY -(1 row) - --- however, you can add primary key if it contains both distribution and partition key -ALTER TABLE partitioning_hash_test ADD CONSTRAINT partitioning_hash_primary PRIMARY KEY (id, subid); -ERROR: relation "partitioning_hash_test" does not exist --- see PRIMARY KEY is created -SELECT - table_name, - constraint_name, - constraint_type -FROM - information_schema.table_constraints -WHERE - table_name LIKE 'partitioning_hash_test%' AND - constraint_type = 'PRIMARY KEY' -ORDER BY 1; - table_name | constraint_name | constraint_type -------------+-----------------+----------------- -(0 rows) - --- test ADD FOREIGN CONSTRAINT --- add FOREIGN CONSTRAINT to partitioned table -- this will error out (it is a self reference) -ALTER TABLE partitioning_test ADD CONSTRAINT partitioning_foreign FOREIGN KEY (id) REFERENCES partitioning_test_2009 (id); -ERROR: foreign key constraints are not supported on partitioned tables -LINE 1: ALTER TABLE partitioning_test ADD CONSTRAINT partitioning_fo... - ^ --- add FOREIGN CONSTRAINT to partition -INSERT INTO partitioning_test_2009 VALUES (5, '2009-06-06'); -INSERT INTO partitioning_test_2009 VALUES (6, '2009-07-07'); -INSERT INTO partitioning_test_2009 VALUES(12, '2009-02-01'); -INSERT INTO partitioning_test_2009 VALUES(18, '2009-02-01'); -ALTER TABLE partitioning_test_2012 ADD CONSTRAINT partitioning_2012_foreign FOREIGN KEY (id) REFERENCES partitioning_test_2009 (id) ON DELETE CASCADE; --- see FOREIGN KEY is created -SELECT "Constraint" FROM table_fkeys WHERE relid = 'partitioning_test_2012'::regclass ORDER BY 1; - Constraint ---------------------------- - partitioning_2012_foreign -(1 row) - --- test ON DELETE CASCADE works -DELETE FROM partitioning_test_2009 WHERE id = 5; --- see that element is deleted from both partitions -SELECT * FROM partitioning_test_2009 WHERE id = 5 ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - -SELECT * FROM partitioning_test_2012 WHERE id = 5 ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - --- test DETACH partition -ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2009; --- see DETACHed partitions content is not accessible from partitioning_test; -SELECT * FROM partitioning_test WHERE time >= '2009-01-01' AND time < '2010-01-01' ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - --- delete from default partition -DELETE FROM partitioning_test WHERE time >= '2015-01-01'; -SELECT * FROM partitioning_test_default; -ERROR: relation "partitioning_test_default" does not exist -LINE 1: SELECT * FROM partitioning_test_default; - ^ --- create a reference table for foreign key test -CREATE TABLE partitioning_test_reference(id int PRIMARY KEY, subid int); -INSERT INTO partitioning_test_reference SELECT a, a FROM generate_series(1, 50) a; -SELECT create_reference_table('partitioning_test_reference'); -NOTICE: Copying data from local table... - create_reference_table ------------------------- - -(1 row) - -ALTER TABLE partitioning_test ADD CONSTRAINT partitioning_reference_fkey FOREIGN KEY (id) REFERENCES partitioning_test_reference(id) ON DELETE CASCADE; -ERROR: foreign key constraints are not supported on partitioned tables -LINE 1: ALTER TABLE partitioning_test ADD CONSTRAINT partitioning_re... - ^ -CREATE TABLE partitioning_test_foreign_key(id int PRIMARY KEY, value int); -SELECT create_distributed_table('partitioning_test_foreign_key', 'id'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO partitioning_test_foreign_key SELECT * FROM partitioning_test_reference; -ALTER TABLE partitioning_hash_test ADD CONSTRAINT partitioning_reference_fk_test FOREIGN KEY (id) REFERENCES partitioning_test_foreign_key(id) ON DELETE CASCADE; -ERROR: relation "partitioning_hash_test" does not exist --- check foreign keys on partitions -SELECT - table_name, constraint_name, constraint_type FROm information_schema.table_constraints -WHERE - table_name LIKE 'partitioning_hash_test%' AND - constraint_type = 'FOREIGN KEY' -ORDER BY - 1,2; - table_name | constraint_name | constraint_type -------------+-----------------+----------------- -(0 rows) - --- check foreign keys on partition shards --- there is some text ordering issue regarding table name --- forcing integer sort by extracting shardid -CREATE TYPE foreign_key_details AS (table_name text, constraint_name text, constraint_type text); -SELECT right(table_name, 7)::int as shardid, * FROM ( - SELECT (json_populate_record(NULL::foreign_key_details, - json_array_elements_text(result::json)::json )).* - FROM run_command_on_workers($$ - SELECT - COALESCE(json_agg(row_to_json(q)), '[]'::json) - FROM ( - SELECT - table_name, constraint_name, constraint_type - FROM information_schema.table_constraints - WHERE - table_name LIKE 'partitioning_hash_test%' AND - constraint_type = 'FOREIGN KEY' - ORDER BY 1, 2, 3 - ) q - $$) ) w -ORDER BY 1, 2, 3, 4; - shardid | table_name | constraint_name | constraint_type ----------+------------+-----------------+----------------- -(0 rows) - -DROP TYPE foreign_key_details; --- set replication factor back to 1 since it gots reset --- after connection re-establishment -SET citus.shard_replication_factor TO 1; -SELECT * FROM partitioning_test WHERE id = 11 or id = 12; - id | time | new_column -----+------------+------------ - 11 | 01-02-2011 | - 11 | 01-02-2011 | - 12 | 01-02-2012 | - 12 | 01-02-2012 | -(4 rows) - -DELETE FROM partitioning_test_reference WHERE id = 11 or id = 12; -SELECT * FROM partitioning_hash_test ORDER BY 1, 2; -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: SELECT * FROM partitioning_hash_test ORDER BY 1, 2; - ^ -DELETE FROM partitioning_test_foreign_key WHERE id = 2 OR id = 9; --- see data is deleted from referencing table -SELECT * FROM partitioning_test WHERE id = 11 or id = 12; - id | time | new_column -----+------------+------------ - 11 | 01-02-2011 | - 11 | 01-02-2011 | - 12 | 01-02-2012 | - 12 | 01-02-2012 | -(4 rows) - -SELECT * FROM partitioning_hash_test ORDER BY 1, 2; -ERROR: relation "partitioning_hash_test" does not exist -LINE 1: SELECT * FROM partitioning_hash_test ORDER BY 1, 2; - ^ --- --- Transaction tests --- --- DDL in transaction -BEGIN; -ALTER TABLE partitioning_test ADD newer_column int; --- see additional column is created -SELECT name, type FROM table_attrs WHERE relid = 'partitioning_test'::regclass ORDER BY 1; - name | type ---------------+--------- - id | integer - new_column | integer - newer_column | integer - time | date -(4 rows) - -ROLLBACK; --- see rollback is successful -SELECT name, type FROM table_attrs WHERE relid = 'partitioning_test'::regclass ORDER BY 1; - name | type -------------+--------- - id | integer - new_column | integer - time | date -(3 rows) - --- COPY in transaction -BEGIN; -COPY partitioning_test FROM STDIN WITH CSV; --- see the data is loaded to shards -SELECT * FROM partitioning_test WHERE id = 22 ORDER BY 1; - id | time | new_column -----+------------+------------ - 22 | 01-01-2010 | 22 -(1 row) - -SELECT * FROM partitioning_test WHERE id = 23 ORDER BY 1; - id | time | new_column -----+------------+------------ - 23 | 01-01-2011 | 23 -(1 row) - -SELECT * FROM partitioning_test WHERE id = 24 ORDER BY 1; - id | time | new_column -----+------------+------------ - 24 | 01-01-2013 | 24 -(1 row) - -ROLLBACK; --- see rollback is successful -SELECT * FROM partitioning_test WHERE id >= 22 ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - --- DML in transaction -BEGIN; --- INSERT in transaction -INSERT INTO partitioning_test VALUES(25, '2010-02-02'); --- see the data is loaded to shards -SELECT * FROM partitioning_test WHERE id = 25 ORDER BY 1; - id | time | new_column -----+------------+------------ - 25 | 02-02-2010 | -(1 row) - --- INSERT/SELECT in transaction -INSERT INTO partitioning_test SELECT * FROM partitioning_test WHERE id = 25; --- see the data is loaded to shards -SELECT * FROM partitioning_test WHERE id = 25 ORDER BY 1; - id | time | new_column -----+------------+------------ - 25 | 02-02-2010 | - 25 | 02-02-2010 | -(2 rows) - --- UPDATE in transaction -UPDATE partitioning_test SET time = '2010-10-10' WHERE id = 25; --- see the data is updated -SELECT * FROM partitioning_test WHERE id = 25 ORDER BY 1; - id | time | new_column -----+------------+------------ - 25 | 10-10-2010 | - 25 | 10-10-2010 | -(2 rows) - --- perform operations on partition and partioned tables together -INSERT INTO partitioning_test VALUES(26, '2010-02-02', 26); -INSERT INTO partitioning_test_2010 VALUES(26, '2010-02-02', 26); -COPY partitioning_test FROM STDIN WITH CSV; -COPY partitioning_test_2010 FROM STDIN WITH CSV; --- see the data is loaded to shards (we should see 4 rows with same content) -SELECT * FROM partitioning_test WHERE id = 26 ORDER BY 1; - id | time | new_column -----+------------+------------ - 26 | 02-02-2010 | 26 - 26 | 02-02-2010 | 26 - 26 | 02-02-2010 | 26 - 26 | 02-02-2010 | 26 -(4 rows) - -ROLLBACK; --- see rollback is successful -SELECT * FROM partitioning_test WHERE id = 26 ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - --- DETACH and DROP in a transaction -BEGIN; -ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2011; -DROP TABLE partitioning_test_2011; -COMMIT; --- see DROPed partitions content is not accessible -SELECT * FROM partitioning_test WHERE time >= '2011-01-01' AND time < '2012-01-01' ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - --- --- Misc tests --- --- test TRUNCATE --- test TRUNCATE partition -TRUNCATE partitioning_test_2012; --- see partition is TRUNCATEd -SELECT * FROM partitioning_test_2012 ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - --- test TRUNCATE partitioned table -TRUNCATE partitioning_test; --- see partitioned table is TRUNCATEd -SELECT * FROM partitioning_test ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - --- test DROP --- test DROP partition -INSERT INTO partitioning_test_2010 VALUES(27, '2010-02-01'); -DROP TABLE partitioning_test_2010; --- see DROPped partitions content is not accessible from partitioning_test; -SELECT * FROM partitioning_test WHERE time >= '2010-01-01' AND time < '2011-01-01' ORDER BY 1; - id | time | new_column -----+------+------------ -(0 rows) - --- test DROP partitioned table -DROP TABLE partitioning_test; -DROP TABLE partitioning_test_reference; --- dropping the parent should CASCADE to the children as well -SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'partitioning_test%' ORDER BY 1; - table_name -------------------------------- - partitioning_test_2009 - partitioning_test_failure - partitioning_test_foreign_key -(3 rows) - --- test distributing partitioned table colocated with non-partitioned table -CREATE TABLE partitioned_users_table (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint) PARTITION BY RANGE (time); -CREATE TABLE partitioned_events_table (user_id int, time timestamp, event_type int, value_2 int, value_3 float, value_4 bigint) PARTITION BY RANGE (time); -SELECT create_distributed_table('partitioned_users_table', 'user_id', colocate_with => 'users_table'); - create_distributed_table --------------------------- - -(1 row) - -SELECT create_distributed_table('partitioned_events_table', 'user_id', colocate_with => 'events_table'); - create_distributed_table --------------------------- - -(1 row) - --- INSERT/SELECT from regular table to partitioned table -CREATE TABLE partitioned_users_table_2009 PARTITION OF partitioned_users_table FOR VALUES FROM ('2017-01-01') TO ('2018-01-01'); -CREATE TABLE partitioned_events_table_2009 PARTITION OF partitioned_events_table FOR VALUES FROM ('2017-01-01') TO ('2018-01-01'); -INSERT INTO partitioned_events_table SELECT * FROM events_table; -INSERT INTO partitioned_users_table_2009 SELECT * FROM users_table; --- --- Complex JOINs, subqueries, UNIONs etc... --- --- subquery with UNIONs on partitioned table -SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType -FROM - (SELECT *, random() - FROM - (SELECT - "t"."user_id", "t"."time", unnest("t"."collected_events") AS "event_types" - FROM - (SELECT - "t1"."user_id", min("t1"."time") AS "time", array_agg(("t1"."event") ORDER BY TIME ASC, event DESC) AS collected_events - FROM( - (SELECT - "events"."user_id", "events"."time", 0 AS event - FROM - partitioned_events_table as "events" - WHERE - event_type IN (1, 2) ) - UNION - (SELECT - "events"."user_id", "events"."time", 1 AS event - FROM - partitioned_events_table as "events" - WHERE - event_type IN (3, 4) ) - UNION - (SELECT - "events"."user_id", "events"."time", 2 AS event - FROM - partitioned_events_table as "events" - WHERE - event_type IN (5, 6) ) - UNION - (SELECT - "events"."user_id", "events"."time", 3 AS event - FROM - partitioned_events_table as "events" - WHERE - event_type IN (1, 6))) t1 - GROUP BY "t1"."user_id") AS t) "q" -) AS final_query -GROUP BY types -ORDER BY types; - types | sumofeventtype --------+---------------- - 0 | 43 - 1 | 44 - 2 | 8 - 3 | 25 -(4 rows) - --- UNION and JOIN on both partitioned and regular tables -SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType -FROM - (SELECT - *, random() - FROM - (SELECT - "t"."user_id", "t"."time", unnest("t"."collected_events") AS "event_types" - FROM - (SELECT - "t1"."user_id", min("t1"."time") AS "time", array_agg(("t1"."event") ORDER BY TIME ASC, event DESC) AS collected_events - FROM ( - (SELECT - * - FROM - (SELECT - "events"."time", 0 AS event, "events"."user_id" - FROM - partitioned_events_table as "events" - WHERE - event_type IN (1, 2)) events_subquery_1) - UNION - (SELECT * - FROM - ( - SELECT * FROM - ( - SELECT - max("events"."time"), - 0 AS event, - "events"."user_id" - FROM - events_table as "events", users_table as "users" - WHERE - events.user_id = users.user_id AND - event_type IN (1, 2) - GROUP BY "events"."user_id" - ) as events_subquery_5 - ) events_subquery_2) - UNION - (SELECT * - FROM - (SELECT - "events"."time", 2 AS event, "events"."user_id" - FROM - partitioned_events_table as "events" - WHERE - event_type IN (3, 4)) events_subquery_3) - UNION - (SELECT * - FROM - (SELECT - "events"."time", 3 AS event, "events"."user_id" - FROM - events_table as "events" - WHERE - event_type IN (5, 6)) events_subquery_4) - ) t1 - GROUP BY "t1"."user_id") AS t) "q" -INNER JOIN - (SELECT - "users"."user_id" - FROM - partitioned_users_table as "users" - WHERE - value_1 > 2 and value_1 < 5) AS t - ON (t.user_id = q.user_id)) as final_query -GROUP BY - types -ORDER BY - types; - types | sumofeventtype --------+---------------- - 0 | 367 - 2 | 360 - 3 | 57 -(3 rows) - --- test LIST partitioning -CREATE TABLE list_partitioned_events_table (user_id int, time date, event_type int, value_2 int, value_3 float, value_4 bigint) PARTITION BY LIST (time); -CREATE TABLE list_partitioned_events_table_2014_01_01_05 PARTITION OF list_partitioned_events_table FOR VALUES IN ('2017-11-21', '2017-11-22', '2017-11-23', '2017-11-24', '2017-11-25'); -CREATE TABLE list_partitioned_events_table_2014_01_06_10 PARTITION OF list_partitioned_events_table FOR VALUES IN ('2017-11-26', '2017-11-27', '2017-11-28', '2017-11-29', '2017-11-30'); -CREATE TABLE list_partitioned_events_table_2014_01_11_15 PARTITION OF list_partitioned_events_table FOR VALUES IN ('2017-12-01', '2017-12-02', '2017-12-03', '2017-12-04', '2017-12-05'); --- test distributing partitioned table colocated with another partitioned table -SELECT create_distributed_table('list_partitioned_events_table', 'user_id', colocate_with => 'partitioned_events_table'); - create_distributed_table --------------------------- - -(1 row) - --- INSERT/SELECT from partitioned table to partitioned table -INSERT INTO - list_partitioned_events_table -SELECT - user_id, - date_trunc('day', time) as time, - event_type, - value_2, - value_3, - value_4 -FROM - events_table -WHERE - time >= '2017-11-21' AND - time <= '2017-12-01'; --- LEFT JOINs used with INNER JOINs on range partitioned table, list partitioned table and non-partitioned table -SELECT -count(*) AS cnt, "generated_group_field" - FROM - (SELECT - "eventQuery"."user_id", random(), generated_group_field - FROM - (SELECT - "multi_group_wrapper_1".*, generated_group_field, random() - FROM - (SELECT * - FROM - (SELECT - "list_partitioned_events_table"."time", "list_partitioned_events_table"."user_id" as event_user_id - FROM - list_partitioned_events_table as "list_partitioned_events_table" - WHERE - user_id > 2) "temp_data_queries" - INNER JOIN - (SELECT - "users"."user_id" - FROM - partitioned_users_table as "users" - WHERE - user_id > 2 and value_2 = 1) "user_filters_1" - ON ("temp_data_queries".event_user_id = "user_filters_1".user_id)) AS "multi_group_wrapper_1" - LEFT JOIN - (SELECT - "users"."user_id" AS "user_id", value_2 AS "generated_group_field" - FROM - partitioned_users_table as "users") "left_group_by_1" - ON ("left_group_by_1".user_id = "multi_group_wrapper_1".event_user_id)) "eventQuery") "pushedDownQuery" - GROUP BY - "generated_group_field" - ORDER BY - cnt DESC, generated_group_field ASC - LIMIT 10; - cnt | generated_group_field -------+----------------------- - 1851 | 1 - 1077 | 4 - 963 | 2 - 955 | 3 - 768 | 5 - 639 | 0 -(6 rows) - --- --- Additional partitioning features --- --- test multi column partitioning -CREATE TABLE multi_column_partitioning(c1 int, c2 int) PARTITION BY RANGE (c1, c2); -CREATE TABLE multi_column_partitioning_0_0_10_0 PARTITION OF multi_column_partitioning FOR VALUES FROM (0, 0) TO (10, 0); -SELECT create_distributed_table('multi_column_partitioning', 'c1'); - create_distributed_table --------------------------- - -(1 row) - --- test INSERT to multi-column partitioned table -INSERT INTO multi_column_partitioning VALUES(1, 1); -INSERT INTO multi_column_partitioning_0_0_10_0 VALUES(5, -5); --- test INSERT to multi-column partitioned table where no suitable partition exists -INSERT INTO multi_column_partitioning VALUES(10, 1); -ERROR: no partition of relation "multi_column_partitioning_1660077" found for row -DETAIL: Partition key of the failing row contains (c1, c2) = (10, 1). -CONTEXT: while executing command on localhost:57637 --- test with MINVALUE/MAXVALUE -CREATE TABLE multi_column_partitioning_10_max_20_min PARTITION OF multi_column_partitioning FOR VALUES FROM (10, MAXVALUE) TO (20, MINVALUE); --- test INSERT to partition with MINVALUE/MAXVALUE bounds -INSERT INTO multi_column_partitioning VALUES(11, -11); -INSERT INTO multi_column_partitioning_10_max_20_min VALUES(19, -19); --- test INSERT to multi-column partitioned table where no suitable partition exists -INSERT INTO multi_column_partitioning VALUES(20, -20); -ERROR: no partition of relation "multi_column_partitioning_1660077" found for row -DETAIL: Partition key of the failing row contains (c1, c2) = (20, -20). -CONTEXT: while executing command on localhost:57637 --- see data is loaded to multi-column partitioned table -SELECT * FROM multi_column_partitioning ORDER BY 1, 2; - c1 | c2 -----+----- - 1 | 1 - 5 | -5 - 11 | -11 - 19 | -19 -(4 rows) - --- --- Tests for locks on partitioned tables --- -CREATE TABLE partitioning_locks(id int, ref_id int, time date) PARTITION BY RANGE (time); --- create its partitions -CREATE TABLE partitioning_locks_2009 PARTITION OF partitioning_locks FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -CREATE TABLE partitioning_locks_2010 PARTITION OF partitioning_locks FOR VALUES FROM ('2010-01-01') TO ('2011-01-01'); --- distribute partitioned table -SELECT create_distributed_table('partitioning_locks', 'id'); - create_distributed_table --------------------------- - -(1 row) - --- test locks on router SELECT -BEGIN; -SELECT * FROM partitioning_locks WHERE id = 1 ORDER BY 1, 2; - id | ref_id | time -----+--------+------ -(0 rows) - -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+----------------- - partitioning_locks | relation | AccessShareLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2010 | relation | AccessShareLock -(3 rows) - -COMMIT; --- test locks on real-time SELECT -BEGIN; -SELECT * FROM partitioning_locks ORDER BY 1, 2; - id | ref_id | time -----+--------+------ -(0 rows) - -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+----------------- - partitioning_locks | relation | AccessShareLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2010 | relation | AccessShareLock -(3 rows) - -COMMIT; --- test locks on task-tracker SELECT -SET citus.task_executor_type TO 'task-tracker'; -BEGIN; -SELECT * FROM partitioning_locks AS pl1 JOIN partitioning_locks AS pl2 ON pl1.id = pl2.ref_id ORDER BY 1, 2; - id | ref_id | time | id | ref_id | time -----+--------+------+----+--------+------ -(0 rows) - -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+----------------- - partitioning_locks | relation | AccessShareLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2010 | relation | AccessShareLock -(3 rows) - -COMMIT; -RESET citus.task_executor_type; --- test locks on INSERT -BEGIN; -INSERT INTO partitioning_locks VALUES(1, 1, '2009-01-01'); -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+------------------ - partitioning_locks | relation | AccessShareLock - partitioning_locks | relation | RowExclusiveLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2009 | relation | RowExclusiveLock - partitioning_locks_2010 | relation | AccessShareLock - partitioning_locks_2010 | relation | RowExclusiveLock -(6 rows) - -COMMIT; --- test locks on UPDATE -BEGIN; -UPDATE partitioning_locks SET time = '2009-02-01' WHERE id = 1; -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+------------------ - partitioning_locks | relation | AccessShareLock - partitioning_locks | relation | RowExclusiveLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2009 | relation | RowExclusiveLock - partitioning_locks_2010 | relation | AccessShareLock - partitioning_locks_2010 | relation | RowExclusiveLock -(6 rows) - -COMMIT; --- test locks on DELETE -BEGIN; -DELETE FROM partitioning_locks WHERE id = 1; -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+------------------ - partitioning_locks | relation | AccessShareLock - partitioning_locks | relation | RowExclusiveLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2009 | relation | RowExclusiveLock - partitioning_locks_2010 | relation | AccessShareLock - partitioning_locks_2010 | relation | RowExclusiveLock -(6 rows) - -COMMIT; --- test locks on INSERT/SELECT -CREATE TABLE partitioning_locks_for_select(id int, ref_id int, time date); -SELECT create_distributed_table('partitioning_locks_for_select', 'id'); - create_distributed_table --------------------------- - -(1 row) - -BEGIN; -INSERT INTO partitioning_locks SELECT * FROM partitioning_locks_for_select; -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------------+----------+------------------ - partitioning_locks | relation | AccessShareLock - partitioning_locks | relation | RowExclusiveLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2009 | relation | RowExclusiveLock - partitioning_locks_2010 | relation | AccessShareLock - partitioning_locks_2010 | relation | RowExclusiveLock - partitioning_locks_for_select | relation | AccessShareLock -(7 rows) - -COMMIT; --- test locks on coordinator INSERT/SELECT -BEGIN; -INSERT INTO partitioning_locks SELECT * FROM partitioning_locks_for_select LIMIT 5; -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------------+----------+------------------ - partitioning_locks | relation | AccessShareLock - partitioning_locks | relation | RowExclusiveLock - partitioning_locks_2009 | relation | RowExclusiveLock - partitioning_locks_2010 | relation | RowExclusiveLock - partitioning_locks_for_select | relation | AccessShareLock -(5 rows) - -COMMIT; --- test locks on multi-shard UPDATE -BEGIN; -UPDATE partitioning_locks SET time = '2009-03-01'; -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+------------------ - partitioning_locks | relation | AccessShareLock - partitioning_locks | relation | RowExclusiveLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2009 | relation | RowExclusiveLock - partitioning_locks_2010 | relation | AccessShareLock - partitioning_locks_2010 | relation | RowExclusiveLock -(6 rows) - -COMMIT; --- test locks on DDL -BEGIN; -ALTER TABLE partitioning_locks ADD COLUMN new_column int; -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+--------------------- - partitioning_locks | relation | AccessExclusiveLock - partitioning_locks | relation | AccessShareLock - partitioning_locks_2009 | relation | AccessExclusiveLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2010 | relation | AccessExclusiveLock - partitioning_locks_2010 | relation | AccessShareLock -(6 rows) - -COMMIT; --- test locks on TRUNCATE -BEGIN; -TRUNCATE partitioning_locks; -SELECT relation::regclass, locktype, mode FROM pg_locks WHERE relation::regclass::text LIKE 'partitioning_locks%' AND pid = pg_backend_pid() ORDER BY 1, 2, 3; - relation | locktype | mode --------------------------+----------+--------------------- - partitioning_locks | relation | AccessExclusiveLock - partitioning_locks | relation | AccessShareLock - partitioning_locks_2009 | relation | AccessExclusiveLock - partitioning_locks_2009 | relation | AccessShareLock - partitioning_locks_2009 | relation | ShareLock - partitioning_locks_2010 | relation | AccessExclusiveLock - partitioning_locks_2010 | relation | AccessShareLock - partitioning_locks_2010 | relation | ShareLock -(8 rows) - -COMMIT; --- test shard resource locks with multi-shard UPDATE -BEGIN; -UPDATE partitioning_locks_2009 SET time = '2009-03-01'; --- see the locks on parent table -SELECT - logicalrelid, - locktype, - mode -FROM - pg_locks AS l JOIN pg_dist_shard AS s -ON - l.objid = s.shardid -WHERE - logicalrelid IN ('partitioning_locks', 'partitioning_locks_2009', 'partitioning_locks_2010') AND - pid = pg_backend_pid() -ORDER BY - 1, 2, 3; - logicalrelid | locktype | mode --------------------------+----------+-------------------------- - partitioning_locks | advisory | ShareUpdateExclusiveLock - partitioning_locks | advisory | ShareUpdateExclusiveLock - partitioning_locks | advisory | ShareUpdateExclusiveLock - partitioning_locks | advisory | ShareUpdateExclusiveLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareUpdateExclusiveLock - partitioning_locks_2009 | advisory | ShareUpdateExclusiveLock - partitioning_locks_2009 | advisory | ShareUpdateExclusiveLock - partitioning_locks_2009 | advisory | ShareUpdateExclusiveLock -(12 rows) - -COMMIT; --- test shard resource locks with TRUNCATE -BEGIN; -TRUNCATE partitioning_locks_2009; --- see the locks on parent table -SELECT - logicalrelid, - locktype, - mode -FROM - pg_locks AS l JOIN pg_dist_shard AS s -ON - l.objid = s.shardid -WHERE - logicalrelid IN ('partitioning_locks', 'partitioning_locks_2009', 'partitioning_locks_2010') AND - pid = pg_backend_pid() -ORDER BY - 1, 2, 3; - logicalrelid | locktype | mode --------------------------+----------+----------- - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock -(4 rows) - -COMMIT; --- test shard resource locks with INSERT/SELECT -BEGIN; -INSERT INTO partitioning_locks_2009 SELECT * FROM partitioning_locks WHERE time >= '2009-01-01' AND time < '2010-01-01'; --- see the locks on parent table -SELECT - logicalrelid, - locktype, - mode -FROM - pg_locks AS l JOIN pg_dist_shard AS s -ON - l.objid = s.shardid -WHERE - logicalrelid IN ('partitioning_locks', 'partitioning_locks_2009', 'partitioning_locks_2010') AND - pid = pg_backend_pid() -ORDER BY - 1, 2, 3; - logicalrelid | locktype | mode --------------------------+----------+-------------------------- - partitioning_locks | advisory | ShareUpdateExclusiveLock - partitioning_locks | advisory | ShareUpdateExclusiveLock - partitioning_locks | advisory | ShareUpdateExclusiveLock - partitioning_locks | advisory | ShareUpdateExclusiveLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareLock - partitioning_locks_2009 | advisory | ShareUpdateExclusiveLock - partitioning_locks_2009 | advisory | ShareUpdateExclusiveLock - partitioning_locks_2009 | advisory | ShareUpdateExclusiveLock - partitioning_locks_2009 | advisory | ShareUpdateExclusiveLock -(12 rows) - -COMMIT; --- test partition-wise join -CREATE TABLE partitioning_hash_join_test(id int, subid int) PARTITION BY HASH(subid); -ERROR: unrecognized partitioning strategy "hash" -CREATE TABLE partitioning_hash_join_test_0 PARTITION OF partitioning_hash_join_test FOR VALUES WITH (MODULUS 3, REMAINDER 0); -ERROR: syntax error at or near "WITH" -LINE 1: ...RTITION OF partitioning_hash_join_test FOR VALUES WITH (MODU... - ^ -CREATE TABLE partitioning_hash_join_test_1 PARTITION OF partitioning_hash_join_test FOR VALUES WITH (MODULUS 3, REMAINDER 1); -ERROR: syntax error at or near "WITH" -LINE 1: ...RTITION OF partitioning_hash_join_test FOR VALUES WITH (MODU... - ^ -CREATE TABLE partitioning_hash_join_test_2 PARTITION OF partitioning_hash_join_test FOR VALUES WITH (MODULUS 3, REMAINDER 2); -ERROR: syntax error at or near "WITH" -LINE 1: ...RTITION OF partitioning_hash_join_test FOR VALUES WITH (MODU... - ^ -SELECT create_distributed_table('partitioning_hash_join_test', 'id'); -ERROR: relation "partitioning_hash_join_test" does not exist -LINE 1: SELECT create_distributed_table('partitioning_hash_join_test... - ^ -SELECT success FROM run_command_on_workers('alter system set enable_mergejoin to off'); - success ---------- - t - t -(2 rows) - -SELECT success FROM run_command_on_workers('alter system set enable_nestloop to off'); - success ---------- - t - t -(2 rows) - -SELECT success FROM run_command_on_workers('alter system set enable_indexscan to off'); - success ---------- - t - t -(2 rows) - -SELECT success FROM run_command_on_workers('alter system set enable_indexonlyscan to off'); - success ---------- - t - t -(2 rows) - -SELECT success FROM run_command_on_workers('alter system set enable_partitionwise_join to off'); - success ---------- - f - f -(2 rows) - -SELECT success FROM run_command_on_workers('select pg_reload_conf()'); - success ---------- - t - t -(2 rows) - -EXPLAIN (COSTS OFF) -SELECT * FROM partitioning_hash_test JOIN partitioning_hash_join_test USING (id, subid); -ERROR: relation "partitioning_hash_test" does not exist -LINE 2: SELECT * FROM partitioning_hash_test JOIN partitioning_hash_... - ^ --- set partition-wise join on -SELECT success FROM run_command_on_workers('alter system set enable_partitionwise_join to on'); - success ---------- - f - f -(2 rows) - -SELECT success FROM run_command_on_workers('select pg_reload_conf()'); - success ---------- - t - t -(2 rows) - -SET enable_partitionwise_join TO on; -ERROR: unrecognized configuration parameter "enable_partitionwise_join" -EXPLAIN (COSTS OFF) -SELECT * FROM partitioning_hash_test JOIN partitioning_hash_join_test USING (id, subid); -ERROR: relation "partitioning_hash_test" does not exist -LINE 2: SELECT * FROM partitioning_hash_test JOIN partitioning_hash_... - ^ --- note that partition-wise joins only work when partition key is in the join --- following join does not have that, therefore join will not be pushed down to --- partitions -EXPLAIN (COSTS OFF) -SELECT * FROM partitioning_hash_test JOIN partitioning_hash_join_test USING (id); -ERROR: relation "partitioning_hash_test" does not exist -LINE 2: SELECT * FROM partitioning_hash_test JOIN partitioning_hash_... - ^ --- reset partition-wise join -SELECT success FROM run_command_on_workers('alter system reset enable_partitionwise_join'); - success ---------- - f - f -(2 rows) - -SELECT success FROM run_command_on_workers('alter system reset enable_mergejoin'); - success ---------- - t - t -(2 rows) - -SELECT success FROM run_command_on_workers('alter system reset enable_nestloop'); - success ---------- - t - t -(2 rows) - -SELECT success FROM run_command_on_workers('alter system reset enable_indexscan'); - success ---------- - t - t -(2 rows) - -SELECT success FROM run_command_on_workers('alter system reset enable_indexonlyscan'); - success ---------- - t - t -(2 rows) - -SELECT success FROM run_command_on_workers('select pg_reload_conf()'); - success ---------- - t - t -(2 rows) - -RESET enable_partitionwise_join; -ERROR: unrecognized configuration parameter "enable_partitionwise_join" -DROP TABLE -IF EXISTS - partitioning_test_2009, - partitioned_events_table, - partitioned_users_table, - list_partitioned_events_table, - multi_column_partitioning, - partitioning_locks, - partitioning_locks_for_select; --- make sure we can create a partitioned table with streaming replication -SET citus.replication_model TO 'streaming'; -CREATE TABLE partitioning_test(id int, time date) PARTITION BY RANGE (time); -CREATE TABLE partitioning_test_2009 PARTITION OF partitioning_test FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -SELECT create_distributed_table('partitioning_test', 'id'); - create_distributed_table --------------------------- - -(1 row) - -DROP TABLE partitioning_test; --- make sure we can attach partitions to a distributed table in a schema -CREATE SCHEMA partitioning_schema; -CREATE TABLE partitioning_schema."schema-test"(id int, time date) PARTITION BY RANGE (time); -SELECT create_distributed_table('partitioning_schema."schema-test"', 'id'); - create_distributed_table --------------------------- - -(1 row) - -CREATE TABLE partitioning_schema."schema-test_2009"(id int, time date); -ALTER TABLE partitioning_schema."schema-test" ATTACH PARTITION partitioning_schema."schema-test_2009" FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); --- attached partition is distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) -ORDER BY 1; - logicalrelid ----------------------------------------- - partitioning_schema."schema-test" - partitioning_schema."schema-test_2009" -(2 rows) - -SELECT - logicalrelid, count(*) -FROM - pg_dist_shard -WHERE - logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) -GROUP BY - logicalrelid -ORDER BY - 1,2; - logicalrelid | count -----------------------------------------+------- - partitioning_schema."schema-test" | 4 - partitioning_schema."schema-test_2009" | 4 -(2 rows) - -DROP TABLE partitioning_schema."schema-test"; --- make sure we can create partition of a distributed table in a schema -CREATE TABLE partitioning_schema."schema-test"(id int, time date) PARTITION BY RANGE (time); -SELECT create_distributed_table('partitioning_schema."schema-test"', 'id'); - create_distributed_table --------------------------- - -(1 row) - -CREATE TABLE partitioning_schema."schema-test_2009" PARTITION OF partitioning_schema."schema-test" FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); --- newly created partition is distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) -ORDER BY 1; - logicalrelid ----------------------------------------- - partitioning_schema."schema-test" - partitioning_schema."schema-test_2009" -(2 rows) - -SELECT - logicalrelid, count(*) -FROM - pg_dist_shard -WHERE - logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) -GROUP BY - logicalrelid -ORDER BY - 1,2; - logicalrelid | count -----------------------------------------+------- - partitioning_schema."schema-test" | 4 - partitioning_schema."schema-test_2009" | 4 -(2 rows) - -DROP TABLE partitioning_schema."schema-test"; --- make sure creating partitioned tables works while search_path is set -CREATE TABLE partitioning_schema."schema-test"(id int, time date) PARTITION BY RANGE (time); -SET search_path = partitioning_schema; -SELECT create_distributed_table('"schema-test"', 'id'); - create_distributed_table --------------------------- - -(1 row) - -CREATE TABLE partitioning_schema."schema-test_2009" PARTITION OF "schema-test" FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); --- newly created partition is distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE - logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) -ORDER BY 1; - logicalrelid --------------------- - "schema-test" - "schema-test_2009" -(2 rows) - -SELECT - logicalrelid, count(*) -FROM - pg_dist_shard -WHERE - logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) -GROUP BY - logicalrelid -ORDER BY - 1,2; - logicalrelid | count ---------------------+------- - "schema-test" | 4 - "schema-test_2009" | 4 -(2 rows) - --- test we don't deadlock when attaching and detaching partitions from partitioned --- tables with foreign keys -CREATE TABLE reference_table(id int PRIMARY KEY); -SELECT create_reference_table('reference_table'); - create_reference_table ------------------------- - -(1 row) - -CREATE TABLE reference_table_2(id int PRIMARY KEY); -SELECT create_reference_table('reference_table_2'); - create_reference_table ------------------------- - -(1 row) - -CREATE TABLE partitioning_test(id int, time date) PARTITION BY RANGE (time); -CREATE TABLE partitioning_test_2008 PARTITION OF partitioning_test FOR VALUES FROM ('2008-01-01') TO ('2009-01-01'); -CREATE TABLE partitioning_test_2009 (LIKE partitioning_test); -CREATE TABLE partitioning_test_2010 (LIKE partitioning_test); -CREATE TABLE partitioning_test_2011 (LIKE partitioning_test); --- distributing partitioning_test will also distribute partitioning_test_2008 -SELECT create_distributed_table('partitioning_test', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT create_distributed_table('partitioning_test_2009', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT create_distributed_table('partitioning_test_2010', 'id'); - create_distributed_table --------------------------- - -(1 row) - -SELECT create_distributed_table('partitioning_test_2011', 'id'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE partitioning_test ADD CONSTRAINT partitioning_reference_fkey - FOREIGN KEY (id) REFERENCES reference_table(id) ON DELETE CASCADE; -ERROR: foreign key constraints are not supported on partitioned tables -LINE 1: ALTER TABLE partitioning_test ADD CONSTRAINT partitioning_re... - ^ -ALTER TABLE partitioning_test_2009 ADD CONSTRAINT partitioning_reference_fkey_2009 - FOREIGN KEY (id) REFERENCES reference_table(id) ON DELETE CASCADE; -INSERT INTO partitioning_test_2010 VALUES (1, '2010-02-01'); --- This should fail because of foreign key constraint violation -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2010 - FOR VALUES FROM ('2010-01-01') TO ('2011-01-01'); --- Truncate, so attaching again won't fail -TRUNCATE partitioning_test_2010; --- Attach a table which already has the same constraint -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2009 - FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); --- Attach a table which doesn't have the constraint -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2010 - FOR VALUES FROM ('2010-01-01') TO ('2011-01-01'); -ERROR: "partitioning_test_2010" is already a partition --- Attach a table which has a different constraint -ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2011 - FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); -ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2008; -ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2009; -ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2010; -ALTER TABLE partitioning_test DETACH PARTITION partitioning_test_2011; -DROP TABLE partitioning_test, partitioning_test_2008, partitioning_test_2009, - partitioning_test_2010, partitioning_test_2011, - reference_table, reference_table_2; -DROP SCHEMA partitioning_schema CASCADE; -NOTICE: drop cascades to table "schema-test" -RESET SEARCH_PATH; -DROP TABLE IF EXISTS - partitioning_hash_test, - partitioning_hash_join_test, - partitioning_test_failure, - non_distributed_partitioned_table, - partitioning_test_foreign_key; -NOTICE: table "partitioning_hash_test" does not exist, skipping -NOTICE: table "partitioning_hash_join_test" does not exist, skipping diff --git a/src/test/regress/expected/multi_partitioning_1.out b/src/test/regress/expected/multi_partitioning_1.out index a2a423606..8f5c63d46 100644 --- a/src/test/regress/expected/multi_partitioning_1.out +++ b/src/test/regress/expected/multi_partitioning_1.out @@ -4,13 +4,6 @@ SET citus.next_shard_id TO 1660000; SET citus.shard_count TO 4; SET citus.shard_replication_factor TO 1; -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS server_version_above_ten; - server_version_above_ten --------------------------- - t -(1 row) - -- -- Distributed Partitioned Table Creation Tests -- diff --git a/src/test/regress/expected/multi_partitioning_utils.out b/src/test/regress/expected/multi_partitioning_utils.out index 7f7901ce5..9ef8f9016 100644 --- a/src/test/regress/expected/multi_partitioning_utils.out +++ b/src/test/regress/expected/multi_partitioning_utils.out @@ -1,11 +1,3 @@ --- This test has different output per major version -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - -- =================================================================== -- create test functions -- =================================================================== diff --git a/src/test/regress/expected/multi_partitioning_utils_0.out b/src/test/regress/expected/multi_partitioning_utils_0.out deleted file mode 100644 index 81e36696b..000000000 --- a/src/test/regress/expected/multi_partitioning_utils_0.out +++ /dev/null @@ -1,431 +0,0 @@ --- This test has different output per major version -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - f -(1 row) - --- =================================================================== --- create test functions --- =================================================================== -CREATE FUNCTION generate_alter_table_detach_partition_command(regclass) - RETURNS text - AS 'citus' - LANGUAGE C STRICT; -CREATE FUNCTION generate_alter_table_attach_partition_command(regclass) - RETURNS text - AS 'citus' - LANGUAGE C STRICT; -CREATE FUNCTION generate_partition_information(regclass) - RETURNS text - AS 'citus' - LANGUAGE C STRICT; -CREATE FUNCTION print_partitions(regclass) - RETURNS text - AS 'citus' - LANGUAGE C STRICT; -CREATE FUNCTION table_inherits(regclass) - RETURNS bool - AS 'citus' - LANGUAGE C STRICT; -CREATE FUNCTION table_inherited(regclass) - RETURNS bool - AS 'citus' - LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION detach_and_attach_partition(partition_name regclass, parent_table_name regclass) -RETURNS void LANGUAGE plpgsql VOLATILE -AS $function$ -DECLARE - detach_partition_command text := ''; - attach_partition_command text := ''; - command_result text := ''; - -BEGIN - -- first generate the command - SELECT public.generate_alter_table_attach_partition_command(partition_name) INTO attach_partition_command; - - -- now genereate the detach command - SELECT public.generate_alter_table_detach_partition_command(partition_name) INTO detach_partition_command; - - -- later detach the same partition - EXECUTE detach_partition_command; - - -- not attach it again - EXECUTE attach_partition_command; -END; -$function$; -CREATE OR REPLACE FUNCTION drop_and_recreate_partitioned_table(parent_table_name regclass) -RETURNS void LANGUAGE plpgsql VOLATILE -AS $function$ -DECLARE - command text := ''; - -BEGIN - -- first generate the command - CREATE TABLE partitioned_table_create_commands AS SELECT master_get_table_ddl_events(parent_table_name::text); - - -- later detach the same partition - EXECUTE 'DROP TABLE ' || parent_table_name::text || ';'; - - FOR command IN SELECT * FROM partitioned_table_create_commands - LOOP - -- can do some processing here - EXECUTE command; - END LOOP; - - DROP TABLE partitioned_table_create_commands; - -END; -$function$; --- create a partitioned table -CREATE TABLE date_partitioned_table(id int, time date) PARTITION BY RANGE (time); --- we should be able to get the partitioning information even if there are no partitions -SELECT generate_partition_information('date_partitioned_table'); - generate_partition_information --------------------------------- - RANGE ("time") -(1 row) - --- we should be able to drop and re-create the partitioned table using the command that Citus generate -SELECT drop_and_recreate_partitioned_table('date_partitioned_table'); - drop_and_recreate_partitioned_table -------------------------------------- - -(1 row) - --- we should also be able to see the PARTITION BY ... for the parent table -SELECT master_get_table_ddl_events('date_partitioned_table'); - master_get_table_ddl_events ---------------------------------------------------------------------------------------------------- - CREATE TABLE public.date_partitioned_table (id integer, "time" date) PARTITION BY RANGE ("time") - ALTER TABLE public.date_partitioned_table OWNER TO postgres -(2 rows) - --- now create the partitions -CREATE TABLE date_partition_2006 PARTITION OF date_partitioned_table FOR VALUES FROM ('2006-01-01') TO ('2007-01-01'); -CREATE TABLE date_partition_2007 PARTITION OF date_partitioned_table FOR VALUES FROM ('2007-01-01') TO ('2008-01-01'); --- we should be able to get the partitioning information after the partitions are created -SELECT generate_partition_information('date_partitioned_table'); - generate_partition_information --------------------------------- - RANGE ("time") -(1 row) - --- lets get the attach partition commands -SELECT generate_alter_table_attach_partition_command('date_partition_2006'); - generate_alter_table_attach_partition_command ------------------------------------------------------------------------------------------------------------------------------------------ - ALTER TABLE public.date_partitioned_table ATTACH PARTITION public.date_partition_2006 FOR VALUES FROM ('01-01-2006') TO ('01-01-2007'); -(1 row) - -SELECT generate_alter_table_attach_partition_command('date_partition_2007'); - generate_alter_table_attach_partition_command ------------------------------------------------------------------------------------------------------------------------------------------ - ALTER TABLE public.date_partitioned_table ATTACH PARTITION public.date_partition_2007 FOR VALUES FROM ('01-01-2007') TO ('01-01-2008'); -(1 row) - --- detach and attach the partition by the command generated by us -\d+ date_partitioned_table - Table "public.date_partitioned_table" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - id | integer | | | | plain | | - time | date | | | | plain | | -Partition key: RANGE ("time") -Partitions: date_partition_2006 FOR VALUES FROM ('01-01-2006') TO ('01-01-2007'), - date_partition_2007 FOR VALUES FROM ('01-01-2007') TO ('01-01-2008') - -SELECT detach_and_attach_partition('date_partition_2007', 'date_partitioned_table'); - detach_and_attach_partition ------------------------------ - -(1 row) - --- check that both partitions are visiable -\d+ date_partitioned_table - Table "public.date_partitioned_table" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - id | integer | | | | plain | | - time | date | | | | plain | | -Partition key: RANGE ("time") -Partitions: date_partition_2006 FOR VALUES FROM ('01-01-2006') TO ('01-01-2007'), - date_partition_2007 FOR VALUES FROM ('01-01-2007') TO ('01-01-2008') - --- make sure that inter shard commands work as expected --- assume that the shardId is 100 -CREATE TABLE date_partitioned_table_100 (id int, time date) PARTITION BY RANGE (time); -CREATE TABLE date_partition_2007_100 (id int, time date ); --- now create the partitioning hierarcy -SELECT worker_apply_inter_shard_ddl_command(referencing_shard:=100, referencing_schema_name:='public', - referenced_shard:=100, referenced_schema_name:='public', - command:='ALTER TABLE date_partitioned_table ATTACH PARTITION date_partition_2007 FOR VALUES FROM (''2007-01-01'') TO (''2008-01-02'')' ); - worker_apply_inter_shard_ddl_command --------------------------------------- - -(1 row) - --- the hierarcy is successfully created -\d+ date_partitioned_table_100 - Table "public.date_partitioned_table_100" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - id | integer | | | | plain | | - time | date | | | | plain | | -Partition key: RANGE ("time") -Partitions: date_partition_2007_100 FOR VALUES FROM ('01-01-2007') TO ('01-02-2008') - --- Citus can also get the DDL events for the partitions as regular tables -SELECT master_get_table_ddl_events('date_partition_2007_100'); - master_get_table_ddl_events ------------------------------------------------------------------------ - CREATE TABLE public.date_partition_2007_100 (id integer, "time" date) - ALTER TABLE public.date_partition_2007_100 OWNER TO postgres -(2 rows) - --- now break the partitioning hierarcy -SELECT worker_apply_inter_shard_ddl_command(referencing_shard:=100, referencing_schema_name:='public', - referenced_shard:=100, referenced_schema_name:='public', - command:='ALTER TABLE date_partitioned_table DETACH PARTITION date_partition_2007' ); - worker_apply_inter_shard_ddl_command --------------------------------------- - -(1 row) - --- the hierarcy is successfully broken -\d+ date_partitioned_table_100 - Table "public.date_partitioned_table_100" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - id | integer | | | | plain | | - time | date | | | | plain | | -Partition key: RANGE ("time") - --- now lets have some more complex partitioning hierarcies with --- tables on different schemas and constraints on the tables -CREATE SCHEMA partition_parent_schema; -CREATE TABLE partition_parent_schema.parent_table (id int NOT NULL, time date DEFAULT now()) PARTITION BY RANGE (time); -CREATE SCHEMA partition_child_1_schema; -CREATE TABLE partition_child_1_schema.child_1 (id int NOT NULL, time date ); -CREATE SCHEMA partition_child_2_schema; -CREATE TABLE partition_child_2_schema.child_2 (id int NOT NULL, time date ); --- we should be able to get the partitioning information even if there are no partitions -SELECT generate_partition_information('partition_parent_schema.parent_table'); - generate_partition_information --------------------------------- - RANGE ("time") -(1 row) - --- we should be able to drop and re-create the partitioned table using the command that Citus generate -SELECT drop_and_recreate_partitioned_table('partition_parent_schema.parent_table'); - drop_and_recreate_partitioned_table -------------------------------------- - -(1 row) - -ALTER TABLE partition_parent_schema.parent_table ATTACH PARTITION partition_child_1_schema.child_1 FOR VALUES FROM ('2009-01-01') TO ('2010-01-02'); -SET search_path = 'partition_parent_schema'; -ALTER TABLE parent_table ATTACH PARTITION partition_child_2_schema.child_2 FOR VALUES FROM ('2006-01-01') TO ('2007-01-01'); -SELECT public.generate_partition_information('parent_table'); - generate_partition_information --------------------------------- - RANGE ("time") -(1 row) - --- lets get the attach partition commands -SELECT public.generate_alter_table_attach_partition_command('partition_child_1_schema.child_1'); - generate_alter_table_attach_partition_command ------------------------------------------------------------------------------------------------------------------------------------------------------- - ALTER TABLE partition_parent_schema.parent_table ATTACH PARTITION partition_child_1_schema.child_1 FOR VALUES FROM ('01-01-2009') TO ('01-02-2010'); -(1 row) - -SET search_path = 'partition_child_2_schema'; -SELECT public.generate_alter_table_attach_partition_command('child_2'); - generate_alter_table_attach_partition_command ------------------------------------------------------------------------------------------------------------------------------------------------------- - ALTER TABLE partition_parent_schema.parent_table ATTACH PARTITION partition_child_2_schema.child_2 FOR VALUES FROM ('01-01-2006') TO ('01-01-2007'); -(1 row) - -SET search_path = 'partition_parent_schema'; --- detach and attach the partition by the command generated by us -\d+ parent_table - Table "partition_parent_schema.parent_table" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - id | integer | | not null | | plain | | - time | date | | | now() | plain | | -Partition key: RANGE ("time") -Partitions: partition_child_1_schema.child_1 FOR VALUES FROM ('01-01-2009') TO ('01-02-2010'), - partition_child_2_schema.child_2 FOR VALUES FROM ('01-01-2006') TO ('01-01-2007') - -SELECT public.detach_and_attach_partition('partition_child_1_schema.child_1', 'parent_table'); - detach_and_attach_partition ------------------------------ - -(1 row) - --- check that both partitions are visiable -\d+ parent_table - Table "partition_parent_schema.parent_table" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------+---------+--------------+------------- - id | integer | | not null | | plain | | - time | date | | | now() | plain | | -Partition key: RANGE ("time") -Partitions: partition_child_1_schema.child_1 FOR VALUES FROM ('01-01-2009') TO ('01-02-2010'), - partition_child_2_schema.child_2 FOR VALUES FROM ('01-01-2006') TO ('01-01-2007') - --- some very simple checks that should error out -SELECT public.generate_alter_table_attach_partition_command('parent_table'); -ERROR: "parent_table" is not a partition -SELECT public.generate_partition_information('partition_child_1_schema.child_1'); -ERROR: "child_1" is not a parent table -SELECT public.print_partitions('partition_child_1_schema.child_1'); -ERROR: "child_1" is not a parent table --- now pring the partitions -SELECT public.print_partitions('parent_table'); - print_partitions ------------------- - child_1,child_2 -(1 row) - -SET search_path = 'public'; --- test multi column / expression partitioning with UNBOUNDED ranges -CREATE OR REPLACE FUNCTION some_function(input_val text) -RETURNS text LANGUAGE plpgsql IMMUTABLE -AS $function$ -BEGIN - return reverse(input_val); -END; -$function$; -CREATE TABLE multi_column_partitioned ( - a int, - b int, - c text - ) PARTITION BY RANGE (a, (a+b+1), some_function(upper(c))); -CREATE TABLE multi_column_partition_1( - a int, - b int, - c text -); -CREATE TABLE multi_column_partition_2( - a int, - b int, - c text -); --- partitioning information -SELECT generate_partition_information('multi_column_partitioned'); - generate_partition_information ------------------------------------------------------ - RANGE (a, (((a + b) + 1)), some_function(upper(c))) -(1 row) - -SELECT master_get_table_ddl_events('multi_column_partitioned'); - master_get_table_ddl_events ------------------------------------------------------------------------------------------------------------------------------------------------------- - CREATE TABLE public.multi_column_partitioned (a integer, b integer, c text) PARTITION BY RANGE (a, (((a + b) + 1)), public.some_function(upper(c))) - ALTER TABLE public.multi_column_partitioned OWNER TO postgres -(2 rows) - -SELECT drop_and_recreate_partitioned_table('multi_column_partitioned'); - drop_and_recreate_partitioned_table -------------------------------------- - -(1 row) - --- partitions and their ranges -ALTER TABLE multi_column_partitioned ATTACH PARTITION multi_column_partition_1 FOR VALUES FROM (1, 10, '250') TO (1, 20, '250'); -SELECT generate_alter_table_attach_partition_command('multi_column_partition_1'); - generate_alter_table_attach_partition_command ------------------------------------------------------------------------------------------------------------------------------------------------- - ALTER TABLE public.multi_column_partitioned ATTACH PARTITION public.multi_column_partition_1 FOR VALUES FROM (1, 10, '250') TO (1, 20, '250'); -(1 row) - -ALTER TABLE multi_column_partitioned ATTACH PARTITION multi_column_partition_2 FOR VALUES FROM (10, 1000, '2500') TO (MAXVALUE, MAXVALUE, MAXVALUE); -SELECT generate_alter_table_attach_partition_command('multi_column_partition_2'); - generate_alter_table_attach_partition_command --------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ALTER TABLE public.multi_column_partitioned ATTACH PARTITION public.multi_column_partition_2 FOR VALUES FROM (10, 1000, '2500') TO (MAXVALUE, MAXVALUE, MAXVALUE); -(1 row) - -SELECT generate_alter_table_detach_partition_command('multi_column_partition_2'); - generate_alter_table_detach_partition_command ---------------------------------------------------------------------------------------------------------- - ALTER TABLE IF EXISTS public.multi_column_partitioned DETACH PARTITION public.multi_column_partition_2; -(1 row) - --- finally a test with LIST partitioning -CREATE TABLE list_partitioned (col1 NUMERIC, col2 NUMERIC, col3 VARCHAR(10)) PARTITION BY LIST (col1) ; -SELECT generate_partition_information('list_partitioned'); - generate_partition_information --------------------------------- - LIST (col1) -(1 row) - -SELECT master_get_table_ddl_events('list_partitioned'); - master_get_table_ddl_events -------------------------------------------------------------------------------------------------------------------------- - CREATE TABLE public.list_partitioned (col1 numeric, col2 numeric, col3 character varying(10)) PARTITION BY LIST (col1) - ALTER TABLE public.list_partitioned OWNER TO postgres -(2 rows) - -SELECT drop_and_recreate_partitioned_table('list_partitioned'); - drop_and_recreate_partitioned_table -------------------------------------- - -(1 row) - -CREATE TABLE list_partitioned_1 PARTITION OF list_partitioned FOR VALUES IN (100, 101, 102, 103, 104); -SELECT generate_alter_table_attach_partition_command('list_partitioned_1'); - generate_alter_table_attach_partition_command ------------------------------------------------------------------------------------------------------------------------------------ - ALTER TABLE public.list_partitioned ATTACH PARTITION public.list_partitioned_1 FOR VALUES IN ('100', '101', '102', '103', '104'); -(1 row) - --- also differentiate partitions and inhereted tables -CREATE TABLE cities ( - name text, - population float, - altitude int -- in feet -); -CREATE TABLE capitals ( - state char(2) -) INHERITS (cities); --- returns true since capitals inherits from cities -SELECT table_inherits('capitals'); - table_inherits ----------------- - t -(1 row) - --- although date_partition_2006 inherits from its parent --- returns false since the hierarcy is formed via partitioning -SELECT table_inherits('date_partition_2006'); - table_inherits ----------------- - f -(1 row) - --- returns true since cities inherited by capitals -SELECT table_inherited('cities'); - table_inherited ------------------ - t -(1 row) - --- although date_partitioned_table inherited by its partitions --- returns false since the hierarcy is formed via partitioning -SELECT table_inherited('date_partitioned_table'); - table_inherited ------------------ - f -(1 row) - --- also these are not supported -SELECT master_get_table_ddl_events('capitals'); -ERROR: capitals is not a regular, foreign or partitioned table -SELECT master_get_table_ddl_events('cities'); -ERROR: cities is not a regular, foreign or partitioned table --- dropping parents frop the partitions -DROP TABLE date_partitioned_table, multi_column_partitioned, list_partitioned, partition_parent_schema.parent_table, cities, capitals; diff --git a/src/test/regress/expected/multi_repartition_join_planning.out b/src/test/regress/expected/multi_repartition_join_planning.out index 05ae53d2a..3b34f42f0 100644 --- a/src/test/regress/expected/multi_repartition_join_planning.out +++ b/src/test/regress/expected/multi_repartition_join_planning.out @@ -7,14 +7,6 @@ -- executor here, as we cannot run repartition jobs with real time executor. SET citus.next_shard_id TO 690000; SET citus.enable_unique_job_ids TO off; --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - version_above_nine --------------------- - t -(1 row) - BEGIN; SET client_min_messages TO DEBUG4; SET citus.task_executor_type TO 'task-tracker'; diff --git a/src/test/regress/expected/multi_repartition_join_task_assignment.out b/src/test/regress/expected/multi_repartition_join_task_assignment.out index de4d85fa2..7553c9ab0 100644 --- a/src/test/regress/expected/multi_repartition_join_task_assignment.out +++ b/src/test/regress/expected/multi_repartition_join_task_assignment.out @@ -6,14 +6,6 @@ -- from a sql task to its depended tasks. Note that we set the executor type to task -- tracker executor here, as we cannot run repartition jobs with real time executor. SET citus.next_shard_id TO 710000; --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - version_above_nine --------------------- - t -(1 row) - BEGIN; SET client_min_messages TO DEBUG3; SET citus.task_executor_type TO 'task-tracker'; diff --git a/src/test/regress/expected/multi_shard_update_delete_0.out b/src/test/regress/expected/multi_shard_update_delete_0.out deleted file mode 100644 index 45fae836f..000000000 --- a/src/test/regress/expected/multi_shard_update_delete_0.out +++ /dev/null @@ -1,922 +0,0 @@ --- --- multi shard update delete --- this file is intended to test multi shard update/delete queries --- -SET citus.next_shard_id TO 1440000; -SET citus.shard_replication_factor to 1; -SET citus.multi_shard_modify_mode to 'parallel'; -CREATE TABLE users_test_table(user_id int, value_1 int, value_2 int, value_3 int); -SELECT create_distributed_table('users_test_table', 'user_id'); - create_distributed_table --------------------------- - -(1 row) - -\COPY users_test_table FROM STDIN DELIMITER AS ','; -CREATE TABLE events_test_table (user_id int, value_1 int, value_2 int, value_3 int); -SELECT create_distributed_table('events_test_table', 'user_id'); - create_distributed_table --------------------------- - -(1 row) - -\COPY events_test_table FROM STDIN DELIMITER AS ','; -CREATE TABLE events_reference_copy_table (like events_test_table); -SELECT create_reference_table('events_reference_copy_table'); - create_reference_table ------------------------- - -(1 row) - -INSERT INTO events_reference_copy_table SELECT * FROM events_test_table; -CREATE TABLE users_reference_copy_table (like users_test_table); -SELECT create_reference_table('users_reference_copy_table'); - create_reference_table ------------------------- - -(1 row) - -INSERT INTO users_reference_copy_table SELECT * FROM users_test_table; --- Run multi shard updates and deletes without transaction on hash distributed tables -UPDATE users_test_table SET value_1 = 1; -SELECT COUNT(*), SUM(value_1) FROM users_test_table; - count | sum --------+----- - 15 | 15 -(1 row) - -SELECT COUNT(*), SUM(value_2) FROM users_test_table WHERE user_id = 1 or user_id = 3; - count | sum --------+----- - 4 | 52 -(1 row) - -UPDATE users_test_table SET value_2 = value_2 + 1 WHERE user_id = 1 or user_id = 3; -SELECT COUNT(*), SUM(value_2) FROM users_test_table WHERE user_id = 1 or user_id = 3; - count | sum --------+----- - 4 | 56 -(1 row) - -UPDATE users_test_table SET value_3 = 0 WHERE user_id <> 5; -SELECT SUM(value_3) FROM users_test_table WHERE user_id <> 5; - sum ------ - 0 -(1 row) - -SELECT COUNT(*) FROM users_test_table WHERE user_id = 3 or user_id = 5; - count -------- - 4 -(1 row) - -DELETE FROM users_test_table WHERE user_id = 3 or user_id = 5; -SELECT COUNT(*) FROM users_test_table WHERE user_id = 3 or user_id = 5; - count -------- - 0 -(1 row) - --- Run multi shard update delete queries within transactions -BEGIN; -UPDATE users_test_table SET value_3 = 0; -END; -SELECT SUM(value_3) FROM users_test_table; - sum ------ - 0 -(1 row) - --- Update can also be rollbacked -BEGIN; -UPDATE users_test_table SET value_3 = 1; -ROLLBACK; -SELECT SUM(value_3) FROM users_test_table; - sum ------ - 0 -(1 row) - --- Run with inserts (we need to set citus.multi_shard_modify_mode to sequential) -BEGIN; -INSERT INTO users_test_table (user_id, value_3) VALUES(20, 15); -INSERT INTO users_test_table (user_id, value_3) VALUES(16,1), (20,16), (7,1), (20,17); -SET citus.multi_shard_modify_mode to sequential; -UPDATE users_test_table SET value_3 = 1; -END; -SELECT COUNT()SUM(value_3) FROM users_test_table; -ERROR: syntax error at or near "(" -LINE 1: SELECT COUNT()SUM(value_3) FROM users_test_table; - ^ -SET citus.multi_shard_modify_mode to 'sequential'; --- Run multiple multi shard updates (with sequential executor) -BEGIN; -UPDATE users_test_table SET value_3 = 5; -UPDATE users_test_table SET value_3 = 0; -END; -SELECT SUM(value_3) FROM users_copy_table; -ERROR: relation "users_copy_table" does not exist -LINE 1: SELECT SUM(value_3) FROM users_copy_table; - ^ --- Run multiple multi shard updates (with parallel executor) -SET citus.multi_shard_modify_mode to 'parallel'; -UPDATE users_test_table SET value_3 = 5; -BEGIN; -UPDATE users_test_table SET value_3 = 2; -UPDATE users_test_table SET value_3 = 0; -END; -SELECT SUM(value_3) FROM users_test_table; - sum ------ - 0 -(1 row) - --- Check with kind of constraints -UPDATE users_test_table SET value_3 = 1 WHERE user_id = 3 or true; -SELECT COUNT(*), SUM(value_3) FROM users_test_table; - count | sum --------+----- - 16 | 16 -(1 row) - -UPDATE users_test_table SET value_3 = 0 WHERE user_id = 20 and false; -SELECT COUNT(*), SUM(value_3) FROM users_test_table; - count | sum --------+----- - 16 | 16 -(1 row) - --- Run multi shard updates with prepared statements -PREPARE foo_plan(int,int) AS UPDATE users_test_table SET value_1 = $1, value_3 = $2; -EXECUTE foo_plan(1,5); -EXECUTE foo_plan(3,15); -EXECUTE foo_plan(5,25); -EXECUTE foo_plan(7,35); -EXECUTE foo_plan(9,45); -EXECUTE foo_plan(0,0); -SELECT SUM(value_1), SUM(value_3) FROM users_test_table; - sum | sum ------+----- - 0 | 0 -(1 row) - --- Test on append table (set executor mode to sequential, since with the append --- distributed tables parallel executor may create tons of connections) -SET citus.multi_shard_modify_mode to sequential; -CREATE TABLE append_stage_table(id int, col_2 int); -INSERT INTO append_stage_table VALUES(1,3); -INSERT INTO append_stage_table VALUES(3,2); -INSERT INTO append_stage_table VALUES(5,4); -CREATE TABLE append_stage_table_2(id int, col_2 int); -INSERT INTO append_stage_table_2 VALUES(8,3); -INSERT INTO append_stage_table_2 VALUES(9,2); -INSERT INTO append_stage_table_2 VALUES(10,4); -CREATE TABLE test_append_table(id int, col_2 int); -SELECT create_distributed_table('test_append_table','id','append'); - create_distributed_table --------------------------- - -(1 row) - -SELECT master_create_empty_shard('test_append_table'); - master_create_empty_shard ---------------------------- - 1440010 -(1 row) - -SELECT * FROM master_append_table_to_shard(1440010, 'append_stage_table', 'localhost', :master_port); - master_append_table_to_shard ------------------------------- - 0.00533333 -(1 row) - -SELECT master_create_empty_shard('test_append_table') AS new_shard_id; - new_shard_id --------------- - 1440011 -(1 row) - -SELECT * FROM master_append_table_to_shard(1440011, 'append_stage_table_2', 'localhost', :master_port); - master_append_table_to_shard ------------------------------- - 0.00533333 -(1 row) - -UPDATE test_append_table SET col_2 = 5; -SELECT * FROM test_append_table; - id | col_2 -----+------- - 8 | 5 - 9 | 5 - 10 | 5 - 1 | 5 - 3 | 5 - 5 | 5 -(6 rows) - -DROP TABLE append_stage_table; -DROP TABLE append_stage_table_2; -DROP TABLE test_append_table; --- Update multi shard of partitioned distributed table -SET citus.multi_shard_modify_mode to 'parallel'; -SET citus.shard_replication_factor to 1; -CREATE TABLE tt1(id int, col_2 int) partition by range (col_2); -ERROR: syntax error at or near "partition" -LINE 1: CREATE TABLE tt1(id int, col_2 int) partition by range (col_... - ^ -CREATE TABLE tt1_510 partition of tt1 for VALUES FROM (5) to (10); -ERROR: syntax error at or near "partition" -LINE 1: CREATE TABLE tt1_510 partition of tt1 for VALUES FROM (5) to... - ^ -CREATE TABLE tt1_1120 partition of tt1 for VALUES FROM (11) to (20); -ERROR: syntax error at or near "partition" -LINE 1: CREATE TABLE tt1_1120 partition of tt1 for VALUES FROM (11) ... - ^ -INSERT INTO tt1 VALUES (1,11), (3,15), (5,17), (6,19), (8,17), (2,12); -ERROR: relation "tt1" does not exist -LINE 1: INSERT INTO tt1 VALUES (1,11), (3,15), (5,17), (6,19), (8,17... - ^ -SELECT create_distributed_table('tt1','id'); -ERROR: relation "tt1" does not exist -LINE 1: SELECT create_distributed_table('tt1','id'); - ^ -UPDATE tt1 SET col_2 = 13; -ERROR: relation "tt1" does not exist -LINE 1: UPDATE tt1 SET col_2 = 13; - ^ -DELETE FROM tt1 WHERE id = 1 or id = 3 or id = 5; -ERROR: relation "tt1" does not exist -LINE 1: DELETE FROM tt1 WHERE id = 1 or id = 3 or id = 5; - ^ -SELECT * FROM tt1; -ERROR: relation "tt1" does not exist -LINE 1: SELECT * FROM tt1; - ^ --- Partitioned distributed table within transaction -INSERT INTO tt1 VALUES(4,6); -ERROR: relation "tt1" does not exist -LINE 1: INSERT INTO tt1 VALUES(4,6); - ^ -INSERT INTO tt1 VALUES(7,7); -ERROR: relation "tt1" does not exist -LINE 1: INSERT INTO tt1 VALUES(7,7); - ^ -INSERT INTO tt1 VALUES(9,8); -ERROR: relation "tt1" does not exist -LINE 1: INSERT INTO tt1 VALUES(9,8); - ^ -BEGIN; --- Update rows from partititon tt1_1120 -UPDATE tt1 SET col_2 = 12 WHERE col_2 > 10 and col_2 < 20; -ERROR: relation "tt1" does not exist -LINE 1: UPDATE tt1 SET col_2 = 12 WHERE col_2 > 10 and col_2 < 20; - ^ --- Update rows from partititon tt1_510 -UPDATE tt1 SET col_2 = 7 WHERE col_2 < 10 and col_2 > 5; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; -SELECT * FROM tt1 ORDER BY id; -ERROR: relation "tt1" does not exist -LINE 1: SELECT * FROM tt1 ORDER BY id; - ^ --- Modify main table and partition table within same transaction -BEGIN; -UPDATE tt1 SET col_2 = 12 WHERE col_2 > 10 and col_2 < 20; -ERROR: relation "tt1" does not exist -LINE 1: UPDATE tt1 SET col_2 = 12 WHERE col_2 > 10 and col_2 < 20; - ^ -UPDATE tt1 SET col_2 = 7 WHERE col_2 < 10 and col_2 > 5; -ERROR: current transaction is aborted, commands ignored until end of transaction block -DELETE FROM tt1_510; -ERROR: current transaction is aborted, commands ignored until end of transaction block -DELETE FROM tt1_1120; -ERROR: current transaction is aborted, commands ignored until end of transaction block -COMMIT; -SELECT * FROM tt1 ORDER BY id; -ERROR: relation "tt1" does not exist -LINE 1: SELECT * FROM tt1 ORDER BY id; - ^ -DROP TABLE tt1; -ERROR: table "tt1" does not exist --- Update and copy in the same transaction -CREATE TABLE tt2(id int, col_2 int); -SELECT create_distributed_table('tt2','id'); - create_distributed_table --------------------------- - -(1 row) - -BEGIN; -\COPY tt2 FROM STDIN DELIMITER AS ','; -UPDATE tt2 SET col_2 = 1; -COMMIT; -SELECT * FROM tt2 ORDER BY id; - id | col_2 -----+------- - 1 | 1 - 2 | 1 - 3 | 1 - 7 | 1 - 9 | 1 -(5 rows) - --- Test returning with both type of executors -UPDATE tt2 SET col_2 = 5 RETURNING id, col_2; - id | col_2 -----+------- - 1 | 5 - 3 | 5 - 7 | 5 - 9 | 5 - 2 | 5 -(5 rows) - -SET citus.multi_shard_modify_mode to sequential; -UPDATE tt2 SET col_2 = 3 RETURNING id, col_2; - id | col_2 -----+------- - 1 | 3 - 3 | 3 - 7 | 3 - 9 | 3 - 2 | 3 -(5 rows) - -DROP TABLE tt2; --- Multiple RTEs are only supported if subquery is pushdownable -SET citus.multi_shard_modify_mode to DEFAULT; --- To test colocation between tables in modify query -SET citus.shard_count to 6; -CREATE TABLE events_test_table_2 (user_id int, value_1 int, value_2 int, value_3 int); -SELECT create_distributed_table('events_test_table_2', 'user_id'); - create_distributed_table --------------------------- - -(1 row) - -\COPY events_test_table_2 FROM STDIN DELIMITER AS ','; -CREATE TABLE events_test_table_local (user_id int, value_1 int, value_2 int, value_3 int); -\COPY events_test_table_local FROM STDIN DELIMITER AS ','; -CREATE TABLE test_table_1(id int, date_col timestamptz, col_3 int); -INSERT INTO test_table_1 VALUES(1, '2014-04-05 08:32:12', 5); -INSERT INTO test_table_1 VALUES(2, '2015-02-01 08:31:16', 7); -INSERT INTO test_table_1 VALUES(3, '2111-01-12 08:35:19', 9); -SELECT create_distributed_table('test_table_1', 'id'); -NOTICE: Copying data from local table... - create_distributed_table --------------------------- - -(1 row) - --- We can pushdown query if there is partition key equality -UPDATE users_test_table -SET value_2 = 5 -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id; -DELETE FROM users_test_table -USING events_test_table -WHERE users_test_table.user_id = events_test_table.user_id; -UPDATE users_test_table -SET value_1 = 3 -WHERE user_id IN (SELECT user_id - FROM events_test_table); -DELETE FROM users_test_table -WHERE user_id IN (SELECT user_id - FROM events_test_table); -DELETE FROM events_test_table_2 -WHERE now() > (SELECT max(date_col) - FROM test_table_1 - WHERE test_table_1.id = events_test_table_2.user_id - GROUP BY id) -RETURNING *; - user_id | value_1 | value_2 | value_3 ----------+---------+---------+--------- - 1 | 5 | 7 | 7 - 1 | 20 | 12 | 25 - 1 | 60 | 17 | 17 -(3 rows) - -UPDATE users_test_table -SET value_1 = 5 -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id - AND events_test_table.user_id > 5; -UPDATE users_test_table -SET value_1 = 4 -WHERE user_id IN (SELECT user_id - FROM users_test_table - UNION - SELECT user_id - FROM events_test_table); -UPDATE users_test_table -SET value_1 = 4 -WHERE user_id IN (SELECT user_id - FROM users_test_table - UNION - SELECT user_id - FROM events_test_table) returning value_3; - value_3 ---------- - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -(11 rows) - -UPDATE users_test_table -SET value_1 = 4 -WHERE user_id IN (SELECT user_id - FROM users_test_table - UNION ALL - SELECT user_id - FROM events_test_table) returning value_3; - value_3 ---------- - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -(11 rows) - -UPDATE users_test_table -SET value_1 = 5 -WHERE - value_2 > - (SELECT - max(value_2) - FROM - events_test_table - WHERE - users_test_table.user_id = events_test_table.user_id - GROUP BY - user_id - ); -UPDATE users_test_table -SET value_3 = 1 -WHERE - value_2 > - (SELECT - max(value_2) - FROM - events_test_table - WHERE - users_test_table.user_id = events_test_table.user_id AND - users_test_table.value_2 > events_test_table.value_2 - GROUP BY - user_id - ); -UPDATE users_test_table -SET value_2 = 4 -WHERE - value_1 > 1 AND value_1 < 3 - AND value_2 >= 1 - AND user_id IN - ( - SELECT - e1.user_id - FROM ( - SELECT - user_id, - 1 AS view_homepage - FROM events_test_table - WHERE - value_1 IN (0, 1) - ) e1 LEFT JOIN LATERAL ( - SELECT - user_id, - 1 AS use_demo - FROM events_test_table - WHERE - user_id = e1.user_id - ) e2 ON true -); -UPDATE users_test_table -SET value_3 = 5 -WHERE value_2 IN (SELECT AVG(value_1) OVER (PARTITION BY user_id) FROM events_test_table WHERE events_test_table.user_id = users_test_table.user_id); --- Test it within transaction -BEGIN; -INSERT INTO users_test_table -SELECT * FROM events_test_table -WHERE events_test_table.user_id = 1 OR events_test_table.user_id = 5; -SELECT SUM(value_2) FROM users_test_table; - sum ------ - 169 -(1 row) - -UPDATE users_test_table -SET value_2 = 1 -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id; -SELECT SUM(value_2) FROM users_test_table; - sum ------ - 97 -(1 row) - -COMMIT; --- Test with schema -CREATE SCHEMA sec_schema; -CREATE TABLE sec_schema.tt1(id int, value_1 int); -SELECT create_distributed_table('sec_schema.tt1','id'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO sec_schema.tt1 values(1,1),(2,2),(7,7),(9,9); -UPDATE sec_schema.tt1 -SET value_1 = 11 -WHERE id < (SELECT max(value_2) FROM events_test_table_2 - WHERE sec_schema.tt1.id = events_test_table_2.user_id - GROUP BY user_id) -RETURNING *; - id | value_1 -----+--------- - 7 | 11 - 9 | 11 -(2 rows) - -DROP SCHEMA sec_schema CASCADE; -NOTICE: drop cascades to table sec_schema.tt1 --- We don't need partition key equality with reference tables -UPDATE events_test_table -SET value_2 = 5 -FROM users_reference_copy_table -WHERE users_reference_copy_table.user_id = events_test_table.value_1; --- Both reference tables and hash distributed tables can be used in subquery -UPDATE events_test_table as ett -SET value_2 = 6 -WHERE ett.value_3 IN (SELECT utt.value_3 - FROM users_test_table as utt, users_reference_copy_table as uct - WHERE utt.user_id = uct.user_id AND utt.user_id = ett.user_id); --- We don't need equality check with constant values in sub-select -UPDATE users_reference_copy_table -SET value_2 = 6 -WHERE user_id IN (SELECT 2); -UPDATE users_reference_copy_table -SET value_2 = 6 -WHERE value_1 IN (SELECT 2); -UPDATE users_test_table -SET value_2 = 6 -WHERE user_id IN (SELECT 2); -UPDATE users_test_table -SET value_2 = 6 -WHERE value_1 IN (SELECT 2); --- Function calls in subqueries will be recursively planned -UPDATE test_table_1 -SET col_3 = 6 -WHERE date_col IN (SELECT now()); --- Test with prepared statements -SELECT COUNT(*) FROM users_test_table WHERE value_1 = 0; - count -------- - 0 -(1 row) - -PREPARE foo_plan_2(int,int) AS UPDATE users_test_table - SET value_1 = $1, value_3 = $2 - FROM events_test_table - WHERE users_test_table.user_id = events_test_table.user_id; -EXECUTE foo_plan_2(1,5); -EXECUTE foo_plan_2(3,15); -EXECUTE foo_plan_2(5,25); -EXECUTE foo_plan_2(7,35); -EXECUTE foo_plan_2(9,45); -EXECUTE foo_plan_2(0,0); -SELECT COUNT(*) FROM users_test_table WHERE value_1 = 0; - count -------- - 6 -(1 row) - --- Test with varying WHERE expressions -UPDATE users_test_table -SET value_1 = 7 -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id OR FALSE; -UPDATE users_test_table -SET value_1 = 7 -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id AND TRUE; --- Test with inactive shard-placement --- manually set shardstate of one placement of users_test_table as inactive -UPDATE pg_dist_shard_placement SET shardstate = 3 WHERE shardid = 1440000; -UPDATE users_test_table -SET value_2 = 5 -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id; -ERROR: cannot find a worker that has active placements for all shards in the query --- manually set shardstate of one placement of events_test_table as inactive -UPDATE pg_dist_shard_placement SET shardstate = 3 WHERE shardid = 1440004; -UPDATE users_test_table -SET value_2 = 5 -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id; -ERROR: cannot find a worker that has active placements for all shards in the query -UPDATE pg_dist_shard_placement SET shardstate = 1 WHERE shardid = 1440000; -UPDATE pg_dist_shard_placement SET shardstate = 1 WHERE shardid = 1440004; --- Subquery must return single value to use it with comparison operators -UPDATE users_test_table as utt -SET value_1 = 3 -WHERE value_2 > (SELECT value_3 FROM events_test_table as ett WHERE utt.user_id = ett.user_id); -ERROR: more than one row returned by a subquery used as an expression -CONTEXT: while executing command on localhost:57637 --- We can not pushdown a query if the target relation is reference table -UPDATE users_reference_copy_table -SET value_2 = 5 -FROM events_test_table -WHERE users_reference_copy_table.user_id = events_test_table.user_id; -ERROR: only reference tables may be queried when targeting a reference table with multi shard UPDATE/DELETE queries with multiple tables --- We cannot push down it if the query has outer join and using -UPDATE events_test_table -SET value_2 = users_test_table.user_id -FROM users_test_table -FULL OUTER JOIN events_test_table e2 USING (user_id) -WHERE e2.user_id = events_test_table.user_id RETURNING events_test_table.value_2; -ERROR: a join with USING causes an internal naming conflict, use ON instead --- Non-pushdownable subqueries, but will be handled through recursive planning -UPDATE users_test_table -SET value_1 = 1 -WHERE user_id IN (SELECT Count(value_1) - FROM events_test_table - GROUP BY user_id); -UPDATE users_test_table -SET value_1 = (SELECT Count(*) - FROM events_test_table); -UPDATE users_test_table -SET value_1 = 4 -WHERE user_id IN (SELECT user_id - FROM users_test_table - UNION - SELECT value_1 - FROM events_test_table); -UPDATE users_test_table -SET value_1 = 4 -WHERE user_id IN (SELECT user_id - FROM users_test_table - INTERSECT - SELECT Sum(value_1) - FROM events_test_table - GROUP BY user_id); -UPDATE users_test_table -SET value_2 = (SELECT value_3 - FROM users_test_table); -ERROR: complex joins are only supported when all distributed tables are joined on their distribution columns with equal operator -UPDATE users_test_table -SET value_2 = 2 -WHERE - value_2 > - (SELECT - max(value_2) - FROM - events_test_table - WHERE - users_test_table.user_id > events_test_table.user_id AND - users_test_table.value_1 = events_test_table.value_1 - GROUP BY - user_id - ); -ERROR: complex joins are only supported when all distributed tables are joined on their distribution columns with equal operator -UPDATE users_test_table -SET (value_1, value_2) = (2,1) -WHERE user_id IN - (SELECT user_id - FROM users_test_table - INTERSECT - SELECT user_id - FROM events_test_table); --- Reference tables can not locate on the outer part of the outer join -UPDATE users_test_table -SET value_1 = 4 -WHERE user_id IN - (SELECT DISTINCT e2.user_id - FROM users_reference_copy_table - LEFT JOIN users_test_table e2 ON (e2.user_id = users_reference_copy_table.value_1)) RETURNING *; -ERROR: cannot pushdown the subquery -DETAIL: There exist a reference table in the outer part of the outer join --- Volatile functions are also not supported -UPDATE users_test_table -SET value_2 = 5 -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id * random(); -ERROR: functions used in the WHERE clause of modification queries on distributed tables must not be VOLATILE -UPDATE users_test_table -SET value_2 = 5 * random() -FROM events_test_table -WHERE users_test_table.user_id = events_test_table.user_id; -ERROR: functions used in UPDATE queries on distributed tables must not be VOLATILE --- Recursive modify planner does not take care of following test because the query --- is fully pushdownable, yet not allowed because it would lead to inconsistent replicas. -UPDATE users_test_table -SET value_2 = subquery.random FROM (SELECT user_id, random() - FROM events_test_table) subquery -WHERE users_test_table.user_id = subquery.user_id; -ERROR: functions used in UPDATE queries on distributed tables must not be VOLATILE --- Volatile functions in a subquery are recursively planned -UPDATE users_test_table -SET value_2 = 5 -WHERE users_test_table.user_id IN (SELECT user_id * random() FROM events_test_table); -UPDATE users_test_table -SET value_2 = subquery.random FROM (SELECT user_id, random() - FROM events_test_table) subquery; -UPDATE users_test_table -SET value_2 = subquery.random FROM (SELECT user_id, random() - FROM events_test_table OFFSET 0) subquery -WHERE users_test_table.user_id = subquery.user_id; --- Make following tests consistent -UPDATE users_test_table SET value_2 = 0; --- Local tables are not supported -UPDATE users_test_table -SET value_2 = 5 -FROM events_test_table_local -WHERE users_test_table.user_id = events_test_table_local.user_id; -ERROR: relation events_test_table_local is not distributed -UPDATE events_test_table_local -SET value_2 = 5 -FROM users_test_table -WHERE events_test_table_local.user_id = users_test_table.user_id; -ERROR: relation events_test_table_local is not distributed --- Local tables in a subquery are supported through recursive planning -UPDATE users_test_table -SET value_2 = 5 -WHERE users_test_table.user_id IN(SELECT user_id FROM events_test_table_local); --- Shard counts of tables must be equal to pushdown the query -UPDATE users_test_table -SET value_2 = 5 -FROM events_test_table_2 -WHERE users_test_table.user_id = events_test_table_2.user_id; -ERROR: cannot push down this subquery -DETAIL: Shards of relations in subquery need to have 1-to-1 shard partitioning --- Should error out due to multiple row return from subquery, but we can not get this information within --- subquery pushdown planner. This query will be sent to worker with recursive planner. -DELETE FROM users_test_table -WHERE users_test_table.user_id = (SELECT user_id - FROM events_test_table); -ERROR: more than one row returned by a subquery used as an expression -CONTEXT: while executing command on localhost:57637 --- Cursors are not supported -BEGIN; -DECLARE test_cursor CURSOR FOR SELECT * FROM users_test_table ORDER BY user_id; -FETCH test_cursor; - user_id | value_1 | value_2 | value_3 ----------+---------+---------+--------- - 1 | 2 | 5 | 0 -(1 row) - -UPDATE users_test_table SET value_2 = 5 WHERE CURRENT OF test_cursor; -ERROR: cannot run DML queries with cursors -ROLLBACK; --- Stable functions are supported -SELECT * FROM test_table_1; - id | date_col | col_3 -----+------------------------------+------- - 1 | Sat Apr 05 08:32:12 2014 PDT | 5 - 3 | Mon Jan 12 08:35:19 2111 PST | 9 - 2 | Sun Feb 01 08:31:16 2015 PST | 7 -(3 rows) - -UPDATE test_table_1 SET col_3 = 3 WHERE date_col < now(); -SELECT * FROM test_table_1; - id | date_col | col_3 -----+------------------------------+------- - 1 | Sat Apr 05 08:32:12 2014 PDT | 3 - 3 | Mon Jan 12 08:35:19 2111 PST | 9 - 2 | Sun Feb 01 08:31:16 2015 PST | 3 -(3 rows) - -DELETE FROM test_table_1 WHERE date_col < current_timestamp; -SELECT * FROM test_table_1; - id | date_col | col_3 -----+------------------------------+------- - 3 | Mon Jan 12 08:35:19 2111 PST | 9 -(1 row) - -DROP TABLE test_table_1; --- Volatile functions are not supported -CREATE TABLE test_table_2(id int, double_col double precision); -INSERT INTO test_table_2 VALUES(1, random()); -INSERT INTO test_table_2 VALUES(2, random()); -INSERT INTO test_table_2 VALUES(3, random()); -SELECT create_distributed_table('test_table_2', 'id'); -NOTICE: Copying data from local table... - create_distributed_table --------------------------- - -(1 row) - -UPDATE test_table_2 SET double_col = random(); -ERROR: functions used in UPDATE queries on distributed tables must not be VOLATILE -DROP TABLE test_table_2; --- Run multi shard updates and deletes without transaction on reference tables -SELECT COUNT(*) FROM users_reference_copy_table; - count -------- - 15 -(1 row) - -UPDATE users_reference_copy_table SET value_1 = 1; -SELECT SUM(value_1) FROM users_reference_copy_table; - sum ------ - 15 -(1 row) - -SELECT COUNT(*), SUM(value_2) FROM users_reference_copy_table WHERE user_id = 3 or user_id = 5; - count | sum --------+----- - 4 | 52 -(1 row) - -UPDATE users_reference_copy_table SET value_2 = value_2 + 1 WHERE user_id = 3 or user_id = 5; -SELECT COUNT(*), SUM(value_2) FROM users_reference_copy_table WHERE user_id = 3 or user_id = 5; - count | sum --------+----- - 4 | 56 -(1 row) - -UPDATE users_reference_copy_table SET value_3 = 0 WHERE user_id <> 3; -SELECT SUM(value_3) FROM users_reference_copy_table WHERE user_id <> 3; - sum ------ - 0 -(1 row) - -DELETE FROM users_reference_copy_table WHERE user_id = 3 or user_id = 5; -SELECT COUNT(*) FROM users_reference_copy_table WHERE user_id = 3 or user_id = 5; - count -------- - 0 -(1 row) - --- Do some tests by changing shard replication factor -DROP TABLE users_test_table; -SET citus.shard_replication_factor to 2; -CREATE TABLE users_test_table(user_id int, value_1 int, value_2 int, value_3 int); -SELECT create_distributed_table('users_test_table', 'user_id'); - create_distributed_table --------------------------- - -(1 row) - -\COPY users_test_table FROM STDIN DELIMITER AS ','; --- Run multi shard updates and deletes without transaction on hash distributed tables -UPDATE users_test_table SET value_1 = 1; -SELECT COUNT(*), SUM(value_1) FROM users_test_table; - count | sum --------+----- - 15 | 15 -(1 row) - -SELECT COUNT(*), SUM(value_2) FROM users_test_table WHERE user_id = 1 or user_id = 3; - count | sum --------+----- - 4 | 52 -(1 row) - -UPDATE users_test_table SET value_2 = value_2 + 1 WHERE user_id = 1 or user_id = 3; -SELECT COUNT(*), SUM(value_2) FROM users_test_table WHERE user_id = 1 or user_id = 3; - count | sum --------+----- - 4 | 56 -(1 row) - -UPDATE users_test_table SET value_3 = 0 WHERE user_id <> 5; -SELECT SUM(value_3) FROM users_test_table WHERE user_id <> 5; - sum ------ - 0 -(1 row) - -SELECT COUNT(*) FROM users_test_table WHERE user_id = 3 or user_id = 5; - count -------- - 4 -(1 row) - -DELETE FROM users_test_table WHERE user_id = 3 or user_id = 5; -SELECT COUNT(*) FROM users_test_table WHERE user_id = 3 or user_id = 5; - count -------- - 0 -(1 row) - -DROP TABLE users_test_table; -DROP TABLE events_test_table; -DROP TABLE events_reference_copy_table; -DROP TABLE users_reference_copy_table; diff --git a/src/test/regress/expected/multi_task_assignment_policy.out b/src/test/regress/expected/multi_task_assignment_policy.out index 4478ad479..470860e0b 100644 --- a/src/test/regress/expected/multi_task_assignment_policy.out +++ b/src/test/regress/expected/multi_task_assignment_policy.out @@ -2,17 +2,9 @@ -- MULTI_TASK_ASSIGNMENT -- SET citus.next_shard_id TO 880000; --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - version_above_nine --------------------- - t -(1 row) - -- the function simply parses the results and returns 'shardId@worker' -- for all the explain task outputs -CREATE OR REPLACE FUNCTION parse_explain_output(in qry text, in table_name text, out r text) +CREATE OR REPLACE FUNCTION parse_explain_output(in qry text, in table_name text, out r text) RETURNS SETOF TEXT AS $$ DECLARE portOfTheTask text; @@ -78,25 +70,25 @@ SET client_min_messages TO DEBUG3; SET citus.task_assignment_policy TO 'greedy'; EXPLAIN SELECT count(*) FROM task_assignment_test_table; DEBUG: Router planner does not support append-partitioned tables. -DEBUG: assigned task 3 to node localhost:57637 -DEBUG: assigned task 1 to node localhost:57638 +DEBUG: assigned task 1 to node localhost:57637 +DEBUG: assigned task 3 to node localhost:57638 DEBUG: assigned task 2 to node localhost:57637 - QUERY PLAN ------------------------------------------------------------------------ + QUERY PLAN +---------------------------------------------------------------------- Aggregate (cost=0.00..0.00 rows=0 width=0) - -> Custom Scan (Citus Real-Time) (cost=0.00..0.00 rows=0 width=0) + -> Custom Scan (Citus Adaptive) (cost=0.00..0.00 rows=0 width=0) explain statements for distributed queries are not enabled (3 rows) EXPLAIN SELECT count(*) FROM task_assignment_test_table; DEBUG: Router planner does not support append-partitioned tables. -DEBUG: assigned task 3 to node localhost:57637 -DEBUG: assigned task 1 to node localhost:57638 +DEBUG: assigned task 1 to node localhost:57637 +DEBUG: assigned task 3 to node localhost:57638 DEBUG: assigned task 2 to node localhost:57637 - QUERY PLAN ------------------------------------------------------------------------ + QUERY PLAN +---------------------------------------------------------------------- Aggregate (cost=0.00..0.00 rows=0 width=0) - -> Custom Scan (Citus Real-Time) (cost=0.00..0.00 rows=0 width=0) + -> Custom Scan (Citus Adaptive) (cost=0.00..0.00 rows=0 width=0) explain statements for distributed queries are not enabled (3 rows) @@ -104,25 +96,25 @@ DEBUG: assigned task 2 to node localhost:57637 SET citus.task_assignment_policy TO 'first-replica'; EXPLAIN SELECT count(*) FROM task_assignment_test_table; DEBUG: Router planner does not support append-partitioned tables. -DEBUG: assigned task 3 to node localhost:57637 +DEBUG: assigned task 1 to node localhost:57637 DEBUG: assigned task 2 to node localhost:57637 -DEBUG: assigned task 1 to node localhost:57638 - QUERY PLAN ------------------------------------------------------------------------ +DEBUG: assigned task 3 to node localhost:57638 + QUERY PLAN +---------------------------------------------------------------------- Aggregate (cost=0.00..0.00 rows=0 width=0) - -> Custom Scan (Citus Real-Time) (cost=0.00..0.00 rows=0 width=0) + -> Custom Scan (Citus Adaptive) (cost=0.00..0.00 rows=0 width=0) explain statements for distributed queries are not enabled (3 rows) EXPLAIN SELECT count(*) FROM task_assignment_test_table; DEBUG: Router planner does not support append-partitioned tables. -DEBUG: assigned task 3 to node localhost:57637 +DEBUG: assigned task 1 to node localhost:57637 DEBUG: assigned task 2 to node localhost:57637 -DEBUG: assigned task 1 to node localhost:57638 - QUERY PLAN ------------------------------------------------------------------------ +DEBUG: assigned task 3 to node localhost:57638 + QUERY PLAN +---------------------------------------------------------------------- Aggregate (cost=0.00..0.00 rows=0 width=0) - -> Custom Scan (Citus Real-Time) (cost=0.00..0.00 rows=0 width=0) + -> Custom Scan (Citus Adaptive) (cost=0.00..0.00 rows=0 width=0) explain statements for distributed queries are not enabled (3 rows) @@ -145,7 +137,7 @@ DEBUG: Creating router plan DEBUG: Plan is router executable QUERY PLAN -------------------------------------------------------------- - Custom Scan (Citus Router) + Custom Scan (Citus Adaptive) explain statements for distributed queries are not enabled (2 rows) @@ -155,7 +147,7 @@ DEBUG: Creating router plan DEBUG: Plan is router executable QUERY PLAN -------------------------------------------------------------- - Custom Scan (Citus Router) + Custom Scan (Citus Adaptive) explain statements for distributed queries are not enabled (2 rows) @@ -166,7 +158,7 @@ DEBUG: Creating router plan DEBUG: Plan is router executable QUERY PLAN -------------------------------------------------------------- - Custom Scan (Citus Router) + Custom Scan (Citus Adaptive) explain statements for distributed queries are not enabled (2 rows) @@ -176,26 +168,26 @@ DEBUG: Creating router plan DEBUG: Plan is router executable QUERY PLAN -------------------------------------------------------------- - Custom Scan (Citus Router) + Custom Scan (Citus Adaptive) explain statements for distributed queries are not enabled (2 rows) ROLLBACK; RESET client_min_messages; -- Now, lets test round-robin policy --- round-robin policy relies on PostgreSQL's local transactionId, +-- round-robin policy relies on PostgreSQL's local transactionId, -- which might change and we don't have any control over it. --- the important thing that we look for is that round-robin policy --- should give the same output for executions in the same transaction +-- the important thing that we look for is that round-robin policy +-- should give the same output for executions in the same transaction -- and different output for executions that are not inside the -- same transaction. To ensure that, we define a helper function BEGIN; SET LOCAL citus.explain_distributed_queries TO on; CREATE TEMPORARY TABLE explain_outputs (value text); SET LOCAL citus.task_assignment_policy TO 'round-robin'; -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_reference_table;', 'task_assignment_reference_table'); -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_reference_table;', 'task_assignment_reference_table'); -- given that we're in the same transaction, the count should be 1 SELECT count(DISTINCT value) FROM explain_outputs; @@ -211,9 +203,9 @@ COMMIT; -- change on every execution SET citus.task_assignment_policy TO 'round-robin'; SET citus.explain_distributed_queries TO ON; -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_reference_table;', 'task_assignment_reference_table'); -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_reference_table;', 'task_assignment_reference_table'); -- given that we're in the same transaction, the count should be 2 -- since there are two different worker nodes @@ -226,7 +218,7 @@ SELECT count(DISTINCT value) FROM explain_outputs; TRUNCATE explain_outputs; -- same test with a distributed table -- we keep this test because as of this commit, the code --- paths for reference tables and distributed tables are +-- paths for reference tables and distributed tables are -- not the same SET citus.shard_replication_factor TO 2; CREATE TABLE task_assignment_replicated_hash (test_id integer); @@ -239,9 +231,9 @@ SELECT create_distributed_table('task_assignment_replicated_hash', 'test_id'); BEGIN; SET LOCAL citus.explain_distributed_queries TO on; SET LOCAL citus.task_assignment_policy TO 'round-robin'; -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_replicated_hash;', 'task_assignment_replicated_hash'); -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_replicated_hash;', 'task_assignment_replicated_hash'); -- given that we're in the same transaction, the count should be 1 SELECT count(DISTINCT value) FROM explain_outputs; @@ -257,9 +249,9 @@ COMMIT; -- change on every execution SET citus.task_assignment_policy TO 'round-robin'; SET citus.explain_distributed_queries TO ON; -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_replicated_hash;', 'task_assignment_replicated_hash'); -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_replicated_hash;', 'task_assignment_replicated_hash'); -- given that we're in the same transaction, the count should be 2 -- since there are two different worker nodes diff --git a/src/test/regress/expected/multi_utilities.out b/src/test/regress/expected/multi_utilities.out index 79cdbdfed..ee2c00168 100644 --- a/src/test/regress/expected/multi_utilities.out +++ b/src/test/regress/expected/multi_utilities.out @@ -1,12 +1,4 @@ SET citus.next_shard_id TO 990000; --- print server version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 as version_above_ten; - version_above_ten -------------------- - t -(1 row) - -- =================================================================== -- test utility statement functionality -- =================================================================== diff --git a/src/test/regress/expected/multi_utilities_0.out b/src/test/regress/expected/multi_utilities_0.out deleted file mode 100644 index e576eb8db..000000000 --- a/src/test/regress/expected/multi_utilities_0.out +++ /dev/null @@ -1,466 +0,0 @@ -SET citus.next_shard_id TO 990000; --- print server version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 as version_above_ten; - version_above_ten -------------------- - f -(1 row) - --- =================================================================== --- test utility statement functionality --- =================================================================== -SET citus.shard_count TO 2; -SET citus.shard_replication_factor TO 1; -CREATE TABLE sharded_table ( name text, id bigint ); -SELECT create_distributed_table('sharded_table', 'id', 'hash'); - create_distributed_table --------------------------- - -(1 row) - --- COPY out is supported with distributed tables -COPY sharded_table TO STDOUT; -COPY (SELECT COUNT(*) FROM sharded_table) TO STDOUT; -0 -BEGIN; -SET TRANSACTION READ ONLY; -COPY sharded_table TO STDOUT; -COPY (SELECT COUNT(*) FROM sharded_table) TO STDOUT; -0 -COMMIT; --- ANALYZE is supported in a transaction block -BEGIN; -ANALYZE sharded_table; -ANALYZE sharded_table; -END; --- cursors may not involve distributed tables -DECLARE all_sharded_rows CURSOR FOR SELECT * FROM sharded_table; -ERROR: DECLARE CURSOR can only be used in transaction blocks --- verify PREPARE functionality -PREPARE sharded_insert AS INSERT INTO sharded_table VALUES ('adam', 1); -PREPARE sharded_update AS UPDATE sharded_table SET name = 'bob' WHERE id = 1; -PREPARE sharded_delete AS DELETE FROM sharded_table WHERE id = 1; -PREPARE sharded_query AS SELECT name FROM sharded_table WHERE id = 1; -EXECUTE sharded_query; - name ------- -(0 rows) - -EXECUTE sharded_insert; -EXECUTE sharded_query; - name ------- - adam -(1 row) - -EXECUTE sharded_update; -EXECUTE sharded_query; - name ------- - bob -(1 row) - -EXECUTE sharded_delete; -EXECUTE sharded_query; - name ------- -(0 rows) - --- try to drop shards with where clause -SELECT master_apply_delete_command('DELETE FROM sharded_table WHERE id > 0'); -ERROR: cannot delete from hash distributed table with this command -DETAIL: Delete statements on hash-partitioned tables are not supported with master_apply_delete_command. -HINT: Use the DELETE command instead. --- drop all shards -SELECT master_apply_delete_command('DELETE FROM sharded_table'); -ERROR: cannot delete from hash distributed table with this command -DETAIL: Delete statements on hash-partitioned tables are not supported with master_apply_delete_command. -HINT: Use the DELETE command instead. --- lock shard metadata: take some share locks and exclusive locks -BEGIN; -SELECT lock_shard_metadata(5, ARRAY[999001, 999002, 999002]); - lock_shard_metadata ---------------------- - -(1 row) - -SELECT lock_shard_metadata(7, ARRAY[999001, 999003, 999004]); - lock_shard_metadata ---------------------- - -(1 row) - -SELECT locktype, objid, mode, granted -FROM pg_locks -WHERE objid IN (999001, 999002, 999003, 999004) -ORDER BY objid, mode; - locktype | objid | mode | granted -----------+--------+---------------+--------- - advisory | 999001 | ExclusiveLock | t - advisory | 999001 | ShareLock | t - advisory | 999002 | ShareLock | t - advisory | 999003 | ExclusiveLock | t - advisory | 999004 | ExclusiveLock | t -(5 rows) - -END; --- lock shard metadata: unsupported lock type -SELECT lock_shard_metadata(0, ARRAY[990001, 999002]); -ERROR: unsupported lockmode 0 --- lock shard metadata: invalid shard ID -SELECT lock_shard_metadata(5, ARRAY[0]); - lock_shard_metadata ---------------------- - -(1 row) - --- lock shard metadata: lock nothing -SELECT lock_shard_metadata(5, ARRAY[]::bigint[]); -ERROR: no locks specified --- lock shard resources: take some share locks and exclusive locks -BEGIN; -SELECT lock_shard_resources(5, ARRAY[999001, 999002, 999002]); - lock_shard_resources ----------------------- - -(1 row) - -SELECT lock_shard_resources(7, ARRAY[999001, 999003, 999004]); - lock_shard_resources ----------------------- - -(1 row) - -SELECT locktype, objid, mode, granted -FROM pg_locks -WHERE objid IN (999001, 999002, 999003, 999004) -ORDER BY objid, mode; - locktype | objid | mode | granted -----------+--------+---------------+--------- - advisory | 999001 | ExclusiveLock | t - advisory | 999001 | ShareLock | t - advisory | 999002 | ShareLock | t - advisory | 999003 | ExclusiveLock | t - advisory | 999004 | ExclusiveLock | t -(5 rows) - -END; --- lock shard metadata: unsupported lock type -SELECT lock_shard_resources(0, ARRAY[990001, 999002]); -ERROR: unsupported lockmode 0 --- lock shard metadata: invalid shard ID -SELECT lock_shard_resources(5, ARRAY[-1]); - lock_shard_resources ----------------------- - -(1 row) - --- lock shard metadata: lock nothing -SELECT lock_shard_resources(5, ARRAY[]::bigint[]); -ERROR: no locks specified --- drop table -DROP TABLE sharded_table; --- VACUUM tests --- create a table with a single shard (for convenience) -SET citus.shard_count TO 1; -SET citus.shard_replication_factor TO 2; -CREATE TABLE dustbunnies (id integer, name text, age integer); -SELECT create_distributed_table('dustbunnies', 'id', 'hash'); - create_distributed_table --------------------------- - -(1 row) - --- add some data to the distributed table -\copy dustbunnies (id, name) from stdin with csv -CREATE TABLE second_dustbunnies(id integer, name text, age integer); -SELECT master_create_distributed_table('second_dustbunnies', 'id', 'hash'); - master_create_distributed_table ---------------------------------- - -(1 row) - -SELECT master_create_worker_shards('second_dustbunnies', 1, 2); - master_create_worker_shards ------------------------------ - -(1 row) - --- following approach adapted from PostgreSQL's stats.sql file --- save relevant stat counter values in refreshable view -\c - - - :worker_1_port -CREATE MATERIALIZED VIEW prevcounts AS -SELECT analyze_count, vacuum_count FROM pg_stat_user_tables -WHERE relname='dustbunnies_990002'; --- create function that sleeps until those counters increment -create function wait_for_stats() returns void as $$ -declare - start_time timestamptz := clock_timestamp(); - analyze_updated bool; - vacuum_updated bool; -begin - -- we don't want to wait forever; loop will exit after 10 seconds - for i in 1 .. 100 loop - - -- check to see if analyze has been updated - SELECT (st.analyze_count >= pc.analyze_count + 1) INTO analyze_updated - FROM pg_stat_user_tables AS st, pg_class AS cl, prevcounts AS pc - WHERE st.relname='dustbunnies_990002' AND cl.relname='dustbunnies_990002'; - - -- check to see if vacuum has been updated - SELECT (st.vacuum_count >= pc.vacuum_count + 1) INTO vacuum_updated - FROM pg_stat_user_tables AS st, pg_class AS cl, prevcounts AS pc - WHERE st.relname='dustbunnies_990002' AND cl.relname='dustbunnies_990002'; - - exit when analyze_updated or vacuum_updated; - - -- wait a little - perform pg_sleep(0.1); - - -- reset stats snapshot so we can test again - perform pg_stat_clear_snapshot(); - - end loop; - - -- report time waited in postmaster log (where it won't change test output) - raise log 'wait_for_stats delayed % seconds', - extract(epoch from clock_timestamp() - start_time); -end -$$ language plpgsql; -\c - - - :worker_2_port -CREATE MATERIALIZED VIEW prevcounts AS -SELECT analyze_count, vacuum_count FROM pg_stat_user_tables -WHERE relname='dustbunnies_990001'; --- create function that sleeps until those counters increment -create function wait_for_stats() returns void as $$ -declare - start_time timestamptz := clock_timestamp(); - analyze_updated bool; - vacuum_updated bool; -begin - -- we don't want to wait forever; loop will exit after 10 seconds - for i in 1 .. 100 loop - - -- check to see if analyze has been updated - SELECT (st.analyze_count >= pc.analyze_count + 1) INTO analyze_updated - FROM pg_stat_user_tables AS st, pg_class AS cl, prevcounts AS pc - WHERE st.relname='dustbunnies_990001' AND cl.relname='dustbunnies_990001'; - - -- check to see if vacuum has been updated - SELECT (st.vacuum_count >= pc.vacuum_count + 1) INTO vacuum_updated - FROM pg_stat_user_tables AS st, pg_class AS cl, prevcounts AS pc - WHERE st.relname='dustbunnies_990001' AND cl.relname='dustbunnies_990001'; - - exit when analyze_updated or vacuum_updated; - - -- wait a little - perform pg_sleep(0.1); - - -- reset stats snapshot so we can test again - perform pg_stat_clear_snapshot(); - - end loop; - - -- report time waited in postmaster log (where it won't change test output) - raise log 'wait_for_stats delayed % seconds', - extract(epoch from clock_timestamp() - start_time); -end -$$ language plpgsql; --- run VACUUM and ANALYZE against the table on the master -\c - - - :master_port -VACUUM dustbunnies; -ANALYZE dustbunnies; --- verify that the VACUUM and ANALYZE ran -\c - - - :worker_1_port -SELECT wait_for_stats(); - wait_for_stats ----------------- - -(1 row) - -REFRESH MATERIALIZED VIEW prevcounts; -SELECT pg_stat_get_vacuum_count('dustbunnies_990002'::regclass); - pg_stat_get_vacuum_count --------------------------- - 1 -(1 row) - -SELECT pg_stat_get_analyze_count('dustbunnies_990002'::regclass); - pg_stat_get_analyze_count ---------------------------- - 1 -(1 row) - --- get file node to verify VACUUM FULL -SELECT relfilenode AS oldnode FROM pg_class WHERE oid='dustbunnies_990002'::regclass -\gset --- send a VACUUM FULL and a VACUUM ANALYZE -\c - - - :master_port -VACUUM (FULL) dustbunnies; -VACUUM ANALYZE dustbunnies; --- verify that relfilenode changed -\c - - - :worker_1_port -SELECT relfilenode != :oldnode AS table_rewritten FROM pg_class -WHERE oid='dustbunnies_990002'::regclass; - table_rewritten ------------------ - t -(1 row) - --- verify the VACUUM ANALYZE incremented both vacuum and analyze counts -SELECT wait_for_stats(); - wait_for_stats ----------------- - -(1 row) - -SELECT pg_stat_get_vacuum_count('dustbunnies_990002'::regclass); - pg_stat_get_vacuum_count --------------------------- - 2 -(1 row) - -SELECT pg_stat_get_analyze_count('dustbunnies_990002'::regclass); - pg_stat_get_analyze_count ---------------------------- - 2 -(1 row) - --- disable auto-VACUUM for next test -ALTER TABLE dustbunnies_990002 SET (autovacuum_enabled = false); -SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid='dustbunnies_990002'::regclass -\gset --- send a VACUUM FREEZE after adding a new row -\c - - - :master_port -INSERT INTO dustbunnies VALUES (5, 'peter'); -VACUUM (FREEZE) dustbunnies; --- verify that relfrozenxid increased -\c - - - :worker_1_port -SELECT relfrozenxid::text::integer > :frozenxid AS frozen_performed FROM pg_class -WHERE oid='dustbunnies_990002'::regclass; - frozen_performed ------------------- - t -(1 row) - --- check there are no nulls in either column -SELECT attname, null_frac FROM pg_stats -WHERE tablename = 'dustbunnies_990002' ORDER BY attname; - attname | null_frac ----------+----------- - age | 1 - id | 0 - name | 0 -(3 rows) - --- add NULL values, then perform column-specific ANALYZE -\c - - - :master_port -INSERT INTO dustbunnies VALUES (6, NULL, NULL); -ANALYZE dustbunnies (name); --- verify that name's NULL ratio is updated but age's is not -\c - - - :worker_1_port -SELECT attname, null_frac FROM pg_stats -WHERE tablename = 'dustbunnies_990002' ORDER BY attname; - attname | null_frac ----------+----------- - age | 1 - id | 0 - name | 0.166667 -(3 rows) - -\c - - - :master_port --- verify warning for unqualified VACUUM -VACUUM; -WARNING: not propagating VACUUM command to worker nodes -HINT: Provide a specific table in order to VACUUM distributed tables. --- check for multiple table vacuum -VACUUM dustbunnies, second_dustbunnies; -ERROR: syntax error at or near "," -LINE 1: VACUUM dustbunnies, second_dustbunnies; - ^ --- check the current number of vacuum and analyze run on dustbunnies -SELECT run_command_on_workers($$SELECT wait_for_stats()$$); - run_command_on_workers ------------------------- - (localhost,57637,t,"") - (localhost,57638,t,"") -(2 rows) - -SELECT run_command_on_workers($$SELECT pg_stat_get_vacuum_count(tablename::regclass) from pg_tables where tablename LIKE 'dustbunnies_%' limit 1$$); - run_command_on_workers ------------------------- - (localhost,57637,t,3) - (localhost,57638,t,3) -(2 rows) - -SELECT run_command_on_workers($$SELECT pg_stat_get_analyze_count(tablename::regclass) from pg_tables where tablename LIKE 'dustbunnies_%' limit 1$$); - run_command_on_workers ------------------------- - (localhost,57637,t,3) - (localhost,57638,t,3) -(2 rows) - --- and warning when using targeted VACUUM without DDL propagation -SET citus.enable_ddl_propagation to false; -VACUUM dustbunnies; -WARNING: not propagating VACUUM command to worker nodes -HINT: Set citus.enable_ddl_propagation to true in order to send targeted VACUUM commands to worker nodes. -ANALYZE dustbunnies; -WARNING: not propagating ANALYZE command to worker nodes -HINT: Set citus.enable_ddl_propagation to true in order to send targeted ANALYZE commands to worker nodes. -SET citus.enable_ddl_propagation to DEFAULT; --- should not propagate the vacuum and analyze -SELECT run_command_on_workers($$SELECT wait_for_stats()$$); - run_command_on_workers ------------------------- - (localhost,57637,t,"") - (localhost,57638,t,"") -(2 rows) - -SELECT run_command_on_workers($$SELECT pg_stat_get_vacuum_count(tablename::regclass) from pg_tables where tablename LIKE 'dustbunnies_%' limit 1$$); - run_command_on_workers ------------------------- - (localhost,57637,t,3) - (localhost,57638,t,3) -(2 rows) - -SELECT run_command_on_workers($$SELECT pg_stat_get_analyze_count(tablename::regclass) from pg_tables where tablename LIKE 'dustbunnies_%' limit 1$$); - run_command_on_workers ------------------------- - (localhost,57637,t,3) - (localhost,57638,t,3) -(2 rows) - --- test worker_hash -SELECT worker_hash(123); - worker_hash -------------- - -205084363 -(1 row) - -SELECT worker_hash('1997-08-08'::date); - worker_hash -------------- - -499701663 -(1 row) - --- test a custom type (this test should run after multi_data_types) -SELECT worker_hash('(1, 2)'); -ERROR: cannot find a hash function for the input type -HINT: Cast input to a data type with a hash function. -SELECT worker_hash('(1, 2)'::test_composite_type); - worker_hash -------------- - -1895345704 -(1 row) - -SELECT citus_truncate_trigger(); -ERROR: must be called as trigger --- confirm that citus_create_restore_point works -SELECT 1 FROM citus_create_restore_point('regression-test'); - ?column? ----------- - 1 -(1 row) - diff --git a/src/test/regress/expected/multi_view.out b/src/test/regress/expected/multi_view.out index 7a3f93002..f17e580b3 100644 --- a/src/test/regress/expected/multi_view.out +++ b/src/test/regress/expected/multi_view.out @@ -5,14 +5,6 @@ -- Citus features: simple selects, aggregates, joins, outer joins -- router queries, single row inserts, multi row inserts via insert -- into select, multi row insert via copy commands. --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - SELECT count(*) FROM lineitem_hash_part; count ------- @@ -323,10 +315,10 @@ SELECT * FROM lineitems_by_shipping_method ORDER BY 1,2 LIMIT 5; -- create a view with group by on partition column CREATE VIEW lineitems_by_orderkey AS - SELECT - l_orderkey, count(*) - FROM - lineitem_hash_part + SELECT + l_orderkey, count(*) + FROM + lineitem_hash_part GROUP BY 1; -- this should work since we're able to push down this query SELECT * FROM lineitems_by_orderkey ORDER BY 2 DESC, 1 ASC LIMIT 10; @@ -363,7 +355,7 @@ DROP VIEW priority_orders; CREATE VIEW recent_users AS SELECT user_id, max(time) as lastseen FROM users_table GROUP BY user_id - HAVING max(time) > '2017-11-23 16:20:33.264457'::timestamp order by 2 DESC; + HAVING max(time) > '2017-11-23 16:20:33.264457'::timestamp order by 2 DESC; SELECT * FROM recent_users ORDER BY 2 DESC, 1 DESC; user_id | lastseen ---------+--------------------------------- @@ -390,8 +382,8 @@ SELECT count(*) FROM recent_users ru JOIN events_table et ON (ru.user_id = et.us (1 row) -- count number of events of per recent users order by count -SELECT ru.user_id, count(*) - FROM recent_users ru +SELECT ru.user_id, count(*) + FROM recent_users ru JOIN events_table et ON (ru.user_id = et.user_id) GROUP BY ru.user_id @@ -404,8 +396,8 @@ SELECT ru.user_id, count(*) (3 rows) -- the same query with a left join however, it would still generate the same result -SELECT ru.user_id, count(*) - FROM recent_users ru +SELECT ru.user_id, count(*) + FROM recent_users ru LEFT JOIN events_table et ON (ru.user_id = et.user_id) GROUP BY ru.user_id @@ -419,8 +411,8 @@ SELECT ru.user_id, count(*) -- query wrapped inside a subquery, it needs another top level order by SELECT * FROM - (SELECT ru.user_id, count(*) - FROM recent_users ru + (SELECT ru.user_id, count(*) + FROM recent_users ru JOIN events_table et ON (ru.user_id = et.user_id) GROUP BY ru.user_id @@ -436,8 +428,8 @@ ORDER BY 2 DESC, 1; -- non-partition key joins are supported inside subquery -- via pull-push execution SELECT * FROM - (SELECT ru.user_id, count(*) - FROM recent_users ru + (SELECT ru.user_id, count(*) + FROM recent_users ru JOIN events_table et ON (ru.user_id = et.event_type) GROUP BY ru.user_id @@ -463,7 +455,7 @@ SELECT ru.user_id FROM recent_users ru JOIN recent_events re USING(user_id) GROU -- recent_events who are not done by recent users SELECT count(*) FROM ( SELECT re.*, ru.user_id AS recent_user - FROM recent_events re LEFT JOIN recent_users ru USING(user_id)) reu + FROM recent_events re LEFT JOIN recent_users ru USING(user_id)) reu WHERE recent_user IS NULL; count ------- @@ -576,7 +568,7 @@ SELECT count(*) FROM events_table et WHERE et.user_id IN (SELECT user_id FROM re (1 row) -- union between views is supported through recursive planning -(SELECT user_id FROM recent_users) +(SELECT user_id FROM recent_users) UNION (SELECT user_id FROM selected_users) ORDER BY 1; @@ -593,7 +585,7 @@ ORDER BY 1; -- wrapping it inside a SELECT * works SELECT * FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION (SELECT user_id FROM selected_users) ) u WHERE user_id < 2 AND user_id > 0 @@ -606,7 +598,7 @@ SELECT * -- union all also works for views SELECT * FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION ALL (SELECT user_id FROM selected_users) ) u WHERE user_id < 2 AND user_id > 0 @@ -619,7 +611,7 @@ SELECT * SELECT count(*) FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION (SELECT user_id FROM selected_users) ) u WHERE user_id < 2 AND user_id > 0; @@ -631,7 +623,7 @@ SELECT count(*) -- UNION ALL between views is supported through recursive planning SELECT count(*) FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION ALL (SELECT user_id FROM selected_users) ) u WHERE user_id < 2 AND user_id > 0; @@ -646,7 +638,7 @@ SELECT count(*) (SELECT user_id FROM (SELECT user_id, max(time) as lastseen FROM users_table GROUP BY user_id HAVING max(time) > '2017-11-22 05:45:49.978738'::timestamp order by 2 DESC) aa - ) + ) UNION (SELECT user_id FROM (SELECT * FROM users_table WHERE value_1 >= 1 and value_1 < 3) bb) ) u WHERE user_id < 2 AND user_id > 0; @@ -660,7 +652,7 @@ SELECT count(*) (SELECT user_id FROM (SELECT user_id, max(time) as lastseen FROM users_table GROUP BY user_id HAVING max(time) > '2017-11-22 05:45:49.978738'::timestamp order by 2 DESC) aa - ) + ) UNION ALL (SELECT user_id FROM (SELECT * FROM users_table WHERE value_1 >= 1 and value_1 < 3) bb) ) u WHERE user_id < 2 AND user_id > 0; @@ -815,7 +807,7 @@ EXPLAIN (COSTS FALSE) SELECT user_id FROM recent_selected_users GROUP BY 1 ORDER Sort Key: remote_scan.user_id -> HashAggregate Group Key: remote_scan.user_id - -> Custom Scan (Citus Real-Time) + -> Custom Scan (Citus Adaptive) Task Count: 4 Tasks Shown: One of 4 -> Task @@ -836,7 +828,7 @@ EXPLAIN (COSTS FALSE) SELECT user_id FROM recent_selected_users GROUP BY 1 ORDER EXPLAIN (COSTS FALSE) SELECT * FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION (SELECT user_id FROM selected_users) ) u WHERE user_id < 4 AND user_id > 1 @@ -845,7 +837,7 @@ EXPLAIN (COSTS FALSE) SELECT * ------------------------------------------------------------------------------------------------------------------------------------------------- Sort Sort Key: remote_scan.user_id - -> Custom Scan (Citus Real-Time) + -> Custom Scan (Citus Adaptive) Task Count: 4 Tasks Shown: One of 4 -> Task @@ -874,14 +866,14 @@ EXPLAIN (COSTS FALSE) SELECT et.* FROM recent_10_users JOIN events_table et USIN Limit -> Sort Sort Key: remote_scan."time" DESC - -> Custom Scan (Citus Real-Time) + -> Custom Scan (Citus Adaptive) -> Distributed Subplan 98_1 -> Limit -> Sort Sort Key: max((max(remote_scan.lastseen))) DESC -> HashAggregate Group Key: remote_scan.user_id - -> Custom Scan (Citus Real-Time) + -> Custom Scan (Citus Adaptive) Task Count: 4 Tasks Shown: One of 4 -> Task @@ -913,7 +905,7 @@ EXPLAIN (COSTS FALSE) SELECT et.* FROM recent_10_users JOIN events_table et USIN Limit -> Sort Sort Key: remote_scan."time" DESC - -> Custom Scan (Citus Real-Time) + -> Custom Scan (Citus Adaptive) Task Count: 4 Tasks Shown: One of 4 -> Task diff --git a/src/test/regress/expected/multi_view_0.out b/src/test/regress/expected/multi_view_0.out deleted file mode 100644 index 28e38c7fc..000000000 --- a/src/test/regress/expected/multi_view_0.out +++ /dev/null @@ -1,949 +0,0 @@ --- --- MULTI_VIEW --- --- This file contains test cases for view support. It verifies various --- Citus features: simple selects, aggregates, joins, outer joins --- router queries, single row inserts, multi row inserts via insert --- into select, multi row insert via copy commands. --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - f -(1 row) - -SELECT count(*) FROM lineitem_hash_part; - count -------- - 12000 -(1 row) - -SELECT count(*) FROM orders_hash_part; - count -------- - 2985 -(1 row) - --- create a view for priority orders -CREATE VIEW priority_orders AS SELECT * FROM orders_hash_part WHERE o_orderpriority < '3-MEDIUM'; --- aggregate pushdown -SELECT o_orderpriority, count(*) FROM priority_orders GROUP BY 1 ORDER BY 2, 1; - o_orderpriority | count ------------------+------- - 2-HIGH | 593 - 1-URGENT | 604 -(2 rows) - -SELECT o_orderpriority, count(*) FROM orders_hash_part WHERE o_orderpriority < '3-MEDIUM' GROUP BY 1 ORDER BY 2,1; - o_orderpriority | count ------------------+------- - 2-HIGH | 593 - 1-URGENT | 604 -(2 rows) - --- filters -SELECT o_orderpriority, count(*) as all, count(*) FILTER (WHERE o_orderstatus ='F') as fullfilled FROM priority_orders GROUP BY 1 ORDER BY 2, 1; - o_orderpriority | all | fullfilled ------------------+-----+------------ - 2-HIGH | 593 | 271 - 1-URGENT | 604 | 280 -(2 rows) - --- having -SELECT o_orderdate, count(*) from priority_orders group by 1 having (count(*) > 3) order by 2 desc, 1 desc; - o_orderdate | count --------------+------- - 08-20-1996 | 5 - 10-10-1994 | 4 - 05-05-1994 | 4 - 04-07-1994 | 4 - 03-17-1993 | 4 -(5 rows) - --- having with filters -SELECT o_orderdate, count(*) as all, count(*) FILTER(WHERE o_orderstatus = 'F') from priority_orders group by 1 having (count(*) > 3) order by 2 desc, 1 desc; - o_orderdate | all | count --------------+-----+------- - 08-20-1996 | 5 | 0 - 10-10-1994 | 4 | 4 - 05-05-1994 | 4 | 4 - 04-07-1994 | 4 | 4 - 03-17-1993 | 4 | 4 -(5 rows) - --- limit -SELECT o_orderkey, o_totalprice from orders_hash_part order by 2 desc, 1 asc limit 5 ; - o_orderkey | o_totalprice -------------+-------------- - 4421 | 401055.62 - 10209 | 400191.77 - 11142 | 395039.05 - 14179 | 384265.43 - 11296 | 378166.33 -(5 rows) - -SELECT o_orderkey, o_totalprice from priority_orders order by 2 desc, 1 asc limit 1 ; - o_orderkey | o_totalprice -------------+-------------- - 14179 | 384265.43 -(1 row) - -CREATE VIEW priority_lineitem AS SELECT li.* FROM lineitem_hash_part li JOIN priority_orders ON (l_orderkey = o_orderkey); -SELECT l_orderkey, count(*) FROM priority_lineitem GROUP BY 1 ORDER BY 2 DESC, 1 LIMIT 5; - l_orderkey | count -------------+------- - 7 | 7 - 225 | 7 - 226 | 7 - 322 | 7 - 326 | 7 -(5 rows) - -CREATE VIEW air_shipped_lineitems AS SELECT * FROM lineitem_hash_part WHERE l_shipmode = 'AIR'; --- join between view and table -SELECT count(*) FROM orders_hash_part join air_shipped_lineitems ON (o_orderkey = l_orderkey); - count -------- - 1706 -(1 row) - --- join between views -SELECT count(*) FROM priority_orders join air_shipped_lineitems ON (o_orderkey = l_orderkey); - count -------- - 700 -(1 row) - --- count distinct on partition column is supported -SELECT count(distinct o_orderkey) FROM priority_orders join air_shipped_lineitems ON (o_orderkey = l_orderkey); - count -------- - 551 -(1 row) - --- count distinct on non-partition column is supported -SELECT count(distinct o_orderpriority) FROM priority_orders join air_shipped_lineitems ON (o_orderkey = l_orderkey); - count -------- - 2 -(1 row) - --- count distinct on partition column is supported on router queries -SELECT count(distinct o_orderkey) FROM priority_orders join air_shipped_lineitems - ON (o_orderkey = l_orderkey) - WHERE (o_orderkey = 231); - count -------- - 1 -(1 row) - --- select distinct on router joins of views also works -SELECT distinct(o_orderkey) FROM priority_orders join air_shipped_lineitems - ON (o_orderkey = l_orderkey) - WHERE (o_orderkey = 231); - o_orderkey ------------- - 231 -(1 row) - --- left join support depends on flattening of the query -SELECT o_orderkey, l_orderkey FROM priority_orders left join air_shipped_lineitems ON (o_orderkey = l_orderkey) ORDER BY o_orderkey LIMIT 1; - o_orderkey | l_orderkey -------------+------------ - 2 | -(1 row) - --- however, this works -SELECT count(*) FROM priority_orders left join lineitem_hash_part ON (o_orderkey = l_orderkey) WHERE l_shipmode ='AIR'; - count -------- - 700 -(1 row) - --- view on the inner side is supported -SELECT count(*) FROM priority_orders right join lineitem_hash_part ON (o_orderkey = l_orderkey) WHERE l_shipmode ='AIR'; - count -------- - 1706 -(1 row) - --- view on the outer side is supported -SELECT count(*) FROM lineitem_hash_part right join priority_orders ON (o_orderkey = l_orderkey) WHERE l_shipmode ='AIR'; - count -------- - 700 -(1 row) - --- left join on router query is supported -SELECT o_orderkey, l_linenumber FROM priority_orders left join air_shipped_lineitems ON (o_orderkey = l_orderkey) - WHERE o_orderkey = 2; - o_orderkey | l_linenumber -------------+-------------- - 2 | -(1 row) - --- repartition query on view join --- it passes planning, fails at execution stage -SET client_min_messages TO DEBUG1; -SELECT * FROM priority_orders JOIN air_shipped_lineitems ON (o_custkey = l_suppkey) ORDER BY o_orderkey DESC, o_custkey DESC, o_orderpriority DESC LIMIT 5; -DEBUG: generating subplan 22_1 for subquery SELECT lineitem_hash_part.l_orderkey, lineitem_hash_part.l_partkey, lineitem_hash_part.l_suppkey, lineitem_hash_part.l_linenumber, lineitem_hash_part.l_quantity, lineitem_hash_part.l_extendedprice, lineitem_hash_part.l_discount, lineitem_hash_part.l_tax, lineitem_hash_part.l_returnflag, lineitem_hash_part.l_linestatus, lineitem_hash_part.l_shipdate, lineitem_hash_part.l_commitdate, lineitem_hash_part.l_receiptdate, lineitem_hash_part.l_shipinstruct, lineitem_hash_part.l_shipmode, lineitem_hash_part.l_comment FROM public.lineitem_hash_part WHERE (lineitem_hash_part.l_shipmode OPERATOR(pg_catalog.=) 'AIR'::bpchar) -DEBUG: Plan 22 query after replacing subqueries and CTEs: SELECT priority_orders.o_orderkey, priority_orders.o_custkey, priority_orders.o_orderstatus, priority_orders.o_totalprice, priority_orders.o_orderdate, priority_orders.o_orderpriority, priority_orders.o_clerk, priority_orders.o_shippriority, priority_orders.o_comment, air_shipped_lineitems.l_orderkey, air_shipped_lineitems.l_partkey, air_shipped_lineitems.l_suppkey, air_shipped_lineitems.l_linenumber, air_shipped_lineitems.l_quantity, air_shipped_lineitems.l_extendedprice, air_shipped_lineitems.l_discount, air_shipped_lineitems.l_tax, air_shipped_lineitems.l_returnflag, air_shipped_lineitems.l_linestatus, air_shipped_lineitems.l_shipdate, air_shipped_lineitems.l_commitdate, air_shipped_lineitems.l_receiptdate, air_shipped_lineitems.l_shipinstruct, air_shipped_lineitems.l_shipmode, air_shipped_lineitems.l_comment FROM ((SELECT orders_hash_part.o_orderkey, orders_hash_part.o_custkey, orders_hash_part.o_orderstatus, orders_hash_part.o_totalprice, orders_hash_part.o_orderdate, orders_hash_part.o_orderpriority, orders_hash_part.o_clerk, orders_hash_part.o_shippriority, orders_hash_part.o_comment FROM public.orders_hash_part WHERE (orders_hash_part.o_orderpriority OPERATOR(pg_catalog.<) '3-MEDIUM'::bpchar)) priority_orders JOIN (SELECT intermediate_result.l_orderkey, intermediate_result.l_partkey, intermediate_result.l_suppkey, intermediate_result.l_linenumber, intermediate_result.l_quantity, intermediate_result.l_extendedprice, intermediate_result.l_discount, intermediate_result.l_tax, intermediate_result.l_returnflag, intermediate_result.l_linestatus, intermediate_result.l_shipdate, intermediate_result.l_commitdate, intermediate_result.l_receiptdate, intermediate_result.l_shipinstruct, intermediate_result.l_shipmode, intermediate_result.l_comment FROM read_intermediate_result('22_1'::text, 'binary'::citus_copy_format) intermediate_result(l_orderkey bigint, l_partkey integer, l_suppkey integer, l_linenumber integer, l_quantity numeric(15,2), l_extendedprice numeric(15,2), l_discount numeric(15,2), l_tax numeric(15,2), l_returnflag character(1), l_linestatus character(1), l_shipdate date, l_commitdate date, l_receiptdate date, l_shipinstruct character(25), l_shipmode character(10), l_comment character varying(44))) air_shipped_lineitems ON ((priority_orders.o_custkey OPERATOR(pg_catalog.=) air_shipped_lineitems.l_suppkey))) ORDER BY priority_orders.o_orderkey DESC, priority_orders.o_custkey DESC, priority_orders.o_orderpriority DESC LIMIT 5 -DEBUG: push down of limit count: 5 - o_orderkey | o_custkey | o_orderstatus | o_totalprice | o_orderdate | o_orderpriority | o_clerk | o_shippriority | o_comment | l_orderkey | l_partkey | l_suppkey | l_linenumber | l_quantity | l_extendedprice | l_discount | l_tax | l_returnflag | l_linestatus | l_shipdate | l_commitdate | l_receiptdate | l_shipinstruct | l_shipmode | l_comment -------------+-----------+---------------+--------------+-------------+-----------------+-----------------+----------------+-------------------------------------------------------+------------+-----------+-----------+--------------+------------+-----------------+------------+-------+--------------+--------------+------------+--------------+---------------+---------------------------+------------+------------------------------------------- - 14821 | 1435 | O | 322002.95 | 06-12-1998 | 2-HIGH | Clerk#000000630 | 0 | n packages are furiously ironic ideas. d | 1607 | 118923 | 1435 | 2 | 37.00 | 71851.04 | 0.05 | 0.02 | N | O | 02-27-1996 | 02-18-1996 | 03-16-1996 | NONE | AIR | alongside - 14790 | 613 | O | 270163.54 | 08-21-1996 | 2-HIGH | Clerk#000000347 | 0 | p. regular deposits wake. final n | 2629 | 123076 | 613 | 2 | 31.00 | 34071.17 | 0.08 | 0.03 | N | O | 05-24-1998 | 05-26-1998 | 06-10-1998 | COLLECT COD | AIR | ate blithely bold, regular deposits. bold - 14758 | 1225 | F | 37812.49 | 10-27-1993 | 2-HIGH | Clerk#000000687 | 0 | ages nag about the furio | 9156 | 176190 | 1225 | 2 | 22.00 | 27856.18 | 0.03 | 0.00 | R | F | 02-08-1994 | 04-01-1994 | 02-24-1994 | DELIVER IN PERSON | AIR | equests dete - 14725 | 569 | O | 261801.45 | 06-17-1995 | 2-HIGH | Clerk#000000177 | 0 | ng asymptotes. final, ironic accounts cajole after | 14688 | 173017 | 569 | 3 | 10.00 | 10900.10 | 0.02 | 0.08 | N | O | 03-14-1997 | 04-22-1997 | 04-05-1997 | COLLECT COD | AIR | riously even packages sleep a - 14657 | 370 | F | 116160.53 | 02-28-1994 | 1-URGENT | Clerk#000000756 | 0 | ly across the ironic, ironic instructions. bold ideas | 5153 | 67863 | 370 | 3 | 30.00 | 54925.80 | 0.09 | 0.01 | N | O | 11-10-1995 | 11-14-1995 | 11-16-1995 | DELIVER IN PERSON | AIR | beans sleep bl -(5 rows) - -RESET client_min_messages; -SELECT count(*) FROM priority_orders JOIN air_shipped_lineitems ON (o_custkey = l_suppkey); - count -------- - 192 -(1 row) - --- materialized views work --- insert into... select works with views -CREATE TABLE temp_lineitem(LIKE lineitem_hash_part); -SELECT create_distributed_table('temp_lineitem', 'l_orderkey', 'hash', 'lineitem_hash_part'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO temp_lineitem SELECT * FROM air_shipped_lineitems; -SELECT count(*) FROM temp_lineitem; - count -------- - 1706 -(1 row) - --- following is a where false query, should not be inserting anything -INSERT INTO temp_lineitem SELECT * FROM air_shipped_lineitems WHERE l_shipmode = 'MAIL'; -SELECT count(*) FROM temp_lineitem; - count -------- - 1706 -(1 row) - --- can create and query materialized views -CREATE MATERIALIZED VIEW mode_counts -AS SELECT l_shipmode, count(*) FROM temp_lineitem GROUP BY l_shipmode; -SELECT * FROM mode_counts WHERE l_shipmode = 'AIR' ORDER BY 2 DESC, 1 LIMIT 10; - l_shipmode | count -------------+------- - AIR | 1706 -(1 row) - --- materialized views are local, cannot join with distributed tables -SELECT count(*) FROM mode_counts JOIN temp_lineitem USING (l_shipmode); -ERROR: relation mode_counts is not distributed --- new data is not immediately reflected in the view -INSERT INTO temp_lineitem SELECT * FROM air_shipped_lineitems; -SELECT * FROM mode_counts WHERE l_shipmode = 'AIR' ORDER BY 2 DESC, 1 LIMIT 10; - l_shipmode | count -------------+------- - AIR | 1706 -(1 row) - --- refresh updates the materialised view with new data -REFRESH MATERIALIZED VIEW mode_counts; -SELECT * FROM mode_counts WHERE l_shipmode = 'AIR' ORDER BY 2 DESC, 1 LIMIT 10; - l_shipmode | count -------------+------- - AIR | 3412 -(1 row) - -DROP MATERIALIZED VIEW mode_counts; -SET citus.task_executor_type to "task-tracker"; --- single view repartition subqueries are not supported -SELECT l_suppkey, count(*) FROM - (SELECT l_suppkey, l_shipdate, count(*) - FROM air_shipped_lineitems GROUP BY l_suppkey, l_shipdate) supps - GROUP BY l_suppkey ORDER BY 2 DESC, 1 LIMIT 5; -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries without group by clause are not supported yet --- logically same query without a view works fine -SELECT l_suppkey, count(*) FROM - (SELECT l_suppkey, l_shipdate, count(*) - FROM lineitem_hash_part WHERE l_shipmode = 'AIR' GROUP BY l_suppkey, l_shipdate) supps - GROUP BY l_suppkey ORDER BY 2 DESC, 1 LIMIT 5; - l_suppkey | count ------------+------- - 7680 | 4 - 160 | 3 - 1042 | 3 - 1318 | 3 - 5873 | 3 -(5 rows) - --- when a view is replaced by actual query it still fails -SELECT l_suppkey, count(*) FROM - (SELECT l_suppkey, l_shipdate, count(*) - FROM (SELECT * FROM lineitem_hash_part WHERE l_shipmode = 'AIR') asi - GROUP BY l_suppkey, l_shipdate) supps - GROUP BY l_suppkey ORDER BY 2 DESC, 1 LIMIT 5; -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries without group by clause are not supported yet --- repartition query on view with single table subquery -CREATE VIEW supp_count_view AS SELECT * FROM (SELECT l_suppkey, count(*) FROM lineitem_hash_part GROUP BY 1) s1; -SELECT * FROM supp_count_view ORDER BY 2 DESC, 1 LIMIT 10; - l_suppkey | count ------------+------- - 6104 | 8 - 1868 | 6 - 5532 | 6 - 5849 | 6 - 6169 | 6 - 6669 | 6 - 6692 | 6 - 7703 | 6 - 7869 | 6 - 8426 | 6 -(10 rows) - -SET citus.task_executor_type to DEFAULT; --- create a view with aggregate -CREATE VIEW lineitems_by_shipping_method AS - SELECT l_shipmode, count(*) as cnt FROM lineitem_hash_part GROUP BY 1; --- following will be supported via recursive planning -SELECT * FROM lineitems_by_shipping_method ORDER BY 1,2 LIMIT 5; - l_shipmode | cnt -------------+------ - AIR | 1706 - FOB | 1709 - MAIL | 1739 - RAIL | 1706 - REG AIR | 1679 -(5 rows) - --- create a view with group by on partition column -CREATE VIEW lineitems_by_orderkey AS - SELECT - l_orderkey, count(*) - FROM - lineitem_hash_part - GROUP BY 1; --- this should work since we're able to push down this query -SELECT * FROM lineitems_by_orderkey ORDER BY 2 DESC, 1 ASC LIMIT 10; - l_orderkey | count -------------+------- - 7 | 7 - 68 | 7 - 129 | 7 - 164 | 7 - 194 | 7 - 225 | 7 - 226 | 7 - 322 | 7 - 326 | 7 - 354 | 7 -(10 rows) - --- it would also work since it is made router plannable -SELECT * FROM lineitems_by_orderkey WHERE l_orderkey = 100; - l_orderkey | count -------------+------- - 100 | 5 -(1 row) - -DROP TABLE temp_lineitem CASCADE; -DROP VIEW supp_count_view; -DROP VIEW lineitems_by_orderkey; -DROP VIEW lineitems_by_shipping_method; -DROP VIEW air_shipped_lineitems; -DROP VIEW priority_lineitem; -DROP VIEW priority_orders; --- new tests for real time use case including views and subqueries --- create view to display recent user who has an activity after a timestamp -CREATE VIEW recent_users AS - SELECT user_id, max(time) as lastseen FROM users_table - GROUP BY user_id - HAVING max(time) > '2017-11-23 16:20:33.264457'::timestamp order by 2 DESC; -SELECT * FROM recent_users ORDER BY 2 DESC, 1 DESC; - user_id | lastseen ----------+--------------------------------- - 1 | Thu Nov 23 17:30:34.635085 2017 - 3 | Thu Nov 23 17:18:51.048758 2017 - 5 | Thu Nov 23 16:48:32.08896 2017 -(3 rows) - --- create a view for recent_events -CREATE VIEW recent_events AS - SELECT user_id, time FROM events_table - WHERE time > '2017-11-23 16:20:33.264457'::timestamp; -SELECT count(*) FROM recent_events; - count -------- - 6 -(1 row) - --- count number of events of recent_users -SELECT count(*) FROM recent_users ru JOIN events_table et ON (ru.user_id = et.user_id); - count -------- - 50 -(1 row) - --- count number of events of per recent users order by count -SELECT ru.user_id, count(*) - FROM recent_users ru - JOIN events_table et - ON (ru.user_id = et.user_id) - GROUP BY ru.user_id - ORDER BY 2 DESC, 1; - user_id | count ----------+------- - 3 | 21 - 1 | 15 - 5 | 14 -(3 rows) - --- the same query with a left join however, it would still generate the same result -SELECT ru.user_id, count(*) - FROM recent_users ru - LEFT JOIN events_table et - ON (ru.user_id = et.user_id) - GROUP BY ru.user_id - ORDER BY 2 DESC, 1; - user_id | count ----------+------- - 3 | 21 - 1 | 15 - 5 | 14 -(3 rows) - --- query wrapped inside a subquery, it needs another top level order by -SELECT * FROM - (SELECT ru.user_id, count(*) - FROM recent_users ru - JOIN events_table et - ON (ru.user_id = et.user_id) - GROUP BY ru.user_id - ORDER BY 2 DESC, 1) s1 -ORDER BY 2 DESC, 1; - user_id | count ----------+------- - 3 | 21 - 1 | 15 - 5 | 14 -(3 rows) - --- non-partition key joins are supported inside subquery --- via pull-push execution -SELECT * FROM - (SELECT ru.user_id, count(*) - FROM recent_users ru - JOIN events_table et - ON (ru.user_id = et.event_type) - GROUP BY ru.user_id - ORDER BY 2 DESC, 1) s1 -ORDER BY 2 DESC, 1; - user_id | count ----------+------- - 1 | 24 - 3 | 23 - 5 | 7 -(3 rows) - --- join between views --- recent users who has an event in recent events -SELECT ru.user_id FROM recent_users ru JOIN recent_events re USING(user_id) GROUP BY ru.user_id ORDER BY ru.user_id; - user_id ---------- - 1 - 3 -(2 rows) - --- outer join inside a subquery --- recent_events who are not done by recent users -SELECT count(*) FROM ( - SELECT re.*, ru.user_id AS recent_user - FROM recent_events re LEFT JOIN recent_users ru USING(user_id)) reu - WHERE recent_user IS NULL; - count -------- - 2 -(1 row) - --- same query with anti-join -SELECT count(*) - FROM recent_events re LEFT JOIN recent_users ru ON(ru.user_id = re.user_id) - WHERE ru.user_id IS NULL; - count -------- - 2 -(1 row) - --- join between view and table --- users who has recent activity and they have an entry with value_1 is less than 3 -SELECT ut.* FROM recent_users ru JOIN users_table ut USING (user_id) WHERE ut.value_1 < 3 ORDER BY 1,2; - user_id | time | value_1 | value_2 | value_3 | value_4 ----------+---------------------------------+---------+---------+---------+--------- - 1 | Thu Nov 23 09:26:42.145043 2017 | 1 | 3 | 3 | - 3 | Wed Nov 22 18:43:51.450263 2017 | 1 | 1 | 4 | - 3 | Wed Nov 22 20:43:31.008625 2017 | 1 | 3 | 2 | - 3 | Thu Nov 23 00:15:45.610845 2017 | 1 | 1 | 4 | - 3 | Thu Nov 23 03:23:24.702501 2017 | 1 | 2 | 5 | - 3 | Thu Nov 23 06:20:05.854857 2017 | 1 | 4 | 2 | - 3 | Thu Nov 23 09:57:41.540228 2017 | 2 | 2 | 3 | - 3 | Thu Nov 23 11:18:53.114408 2017 | 2 | 2 | 0 | - 3 | Thu Nov 23 12:56:49.29191 2017 | 0 | 5 | 1 | - 3 | Thu Nov 23 17:18:51.048758 2017 | 1 | 5 | 5 | - 5 | Wed Nov 22 20:43:18.667473 2017 | 0 | 3 | 2 | - 5 | Wed Nov 22 21:02:07.575129 2017 | 2 | 0 | 2 | - 5 | Wed Nov 22 22:10:24.315371 2017 | 1 | 2 | 1 | - 5 | Thu Nov 23 00:54:44.192608 2017 | 1 | 3 | 2 | - 5 | Thu Nov 23 07:47:09.542999 2017 | 1 | 4 | 3 | - 5 | Thu Nov 23 09:05:08.53142 2017 | 2 | 2 | 2 | - 5 | Thu Nov 23 09:17:47.706703 2017 | 2 | 5 | 3 | - 5 | Thu Nov 23 10:15:31.764558 2017 | 2 | 2 | 2 | - 5 | Thu Nov 23 14:29:02.557934 2017 | 2 | 1 | 2 | - 5 | Thu Nov 23 15:55:08.493462 2017 | 0 | 3 | 3 | - 5 | Thu Nov 23 16:28:38.455322 2017 | 2 | 5 | 4 | -(21 rows) - --- determine if a recent user has done a given event type or not -SELECT ru.user_id, CASE WHEN et.user_id IS NULL THEN 'NO' ELSE 'YES' END as done_event - FROM recent_users ru - LEFT JOIN events_table et - ON(ru.user_id = et.user_id AND et.event_type = 6) - ORDER BY 2 DESC, 1; - user_id | done_event ----------+------------ - 1 | YES - 3 | NO - 5 | NO -(3 rows) - --- view vs table join wrapped inside a subquery -SELECT * FROM - (SELECT ru.user_id, CASE WHEN et.user_id IS NULL THEN 'NO' ELSE 'YES' END as done_event - FROM recent_users ru - LEFT JOIN events_table et - ON(ru.user_id = et.user_id AND et.event_type = 6) - ) s1 -ORDER BY 2 DESC, 1; - user_id | done_event ----------+------------ - 1 | YES - 3 | NO - 5 | NO -(3 rows) - --- event vs table non-partition-key join is not supported --- given that we cannot recursively plan tables yet -SELECT * FROM - (SELECT ru.user_id, CASE WHEN et.user_id IS NULL THEN 'NO' ELSE 'YES' END as done_event - FROM recent_users ru - LEFT JOIN events_table et - ON(ru.user_id = et.event_type) - ) s1 -ORDER BY 2 DESC, 1; -ERROR: cannot pushdown the subquery -DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join --- create a select only view -CREATE VIEW selected_users AS SELECT * FROM users_table WHERE value_1 >= 1 and value_1 <3; -CREATE VIEW recent_selected_users AS SELECT su.* FROM selected_users su JOIN recent_users ru USING(user_id); -SELECT user_id FROM recent_selected_users GROUP BY 1 ORDER BY 1; - user_id ---------- - 1 - 3 - 5 -(3 rows) - --- this would be supported when we implement where partition_key in (subquery) support -SELECT et.user_id, et.time FROM events_table et WHERE et.user_id IN (SELECT user_id FROM recent_selected_users) GROUP BY 1,2 ORDER BY 1 DESC,2 DESC LIMIT 5; - user_id | time ----------+--------------------------------- - 5 | Thu Nov 23 16:11:02.929469 2017 - 5 | Thu Nov 23 14:40:40.467511 2017 - 5 | Thu Nov 23 14:28:51.833214 2017 - 5 | Thu Nov 23 14:23:09.889786 2017 - 5 | Thu Nov 23 13:26:45.571108 2017 -(5 rows) - --- it is supported when it is a router query -SELECT count(*) FROM events_table et WHERE et.user_id IN (SELECT user_id FROM recent_selected_users WHERE user_id = 1); - count -------- - 15 -(1 row) - --- union between views is supported through recursive planning -(SELECT user_id FROM recent_users) -UNION -(SELECT user_id FROM selected_users) -ORDER BY 1; - user_id ---------- - 1 - 2 - 3 - 4 - 5 - 6 -(6 rows) - --- wrapping it inside a SELECT * works -SELECT * - FROM ( - (SELECT user_id FROM recent_users) - UNION - (SELECT user_id FROM selected_users) ) u - WHERE user_id < 2 AND user_id > 0 - ORDER BY user_id; - user_id ---------- - 1 -(1 row) - --- union all also works for views -SELECT * - FROM ( - (SELECT user_id FROM recent_users) - UNION ALL - (SELECT user_id FROM selected_users) ) u - WHERE user_id < 2 AND user_id > 0 - ORDER BY user_id; - user_id ---------- - 1 - 1 -(2 rows) - -SELECT count(*) - FROM ( - (SELECT user_id FROM recent_users) - UNION - (SELECT user_id FROM selected_users) ) u - WHERE user_id < 2 AND user_id > 0; - count -------- - 1 -(1 row) - --- UNION ALL between views is supported through recursive planning -SELECT count(*) - FROM ( - (SELECT user_id FROM recent_users) - UNION ALL - (SELECT user_id FROM selected_users) ) u - WHERE user_id < 2 AND user_id > 0; - count -------- - 2 -(1 row) - --- expand view definitions and re-run last 2 queries -SELECT count(*) - FROM ( - (SELECT user_id FROM (SELECT user_id, max(time) as lastseen FROM users_table - GROUP BY user_id - HAVING max(time) > '2017-11-22 05:45:49.978738'::timestamp order by 2 DESC) aa - ) - UNION - (SELECT user_id FROM (SELECT * FROM users_table WHERE value_1 >= 1 and value_1 < 3) bb) ) u - WHERE user_id < 2 AND user_id > 0; - count -------- - 1 -(1 row) - -SELECT count(*) - FROM ( - (SELECT user_id FROM (SELECT user_id, max(time) as lastseen FROM users_table - GROUP BY user_id - HAVING max(time) > '2017-11-22 05:45:49.978738'::timestamp order by 2 DESC) aa - ) - UNION ALL - (SELECT user_id FROM (SELECT * FROM users_table WHERE value_1 >= 1 and value_1 < 3) bb) ) u - WHERE user_id < 2 AND user_id > 0; - count -------- - 2 -(1 row) - --- test distinct --- distinct is supported if it is on a partition key -CREATE VIEW distinct_user_with_value_1_3 AS SELECT DISTINCT user_id FROM users_table WHERE value_1 = 3; -SELECT * FROM distinct_user_with_value_1_3 ORDER BY user_id; - user_id ---------- - 1 - 2 - 3 - 4 - 5 - 6 -(6 rows) - --- distinct is not supported if it is on a non-partition key --- but will be supported via recursive planning -CREATE VIEW distinct_value_1 AS SELECT DISTINCT value_1 FROM users_table WHERE value_2 = 3; -SELECT * FROM distinct_value_1 ORDER BY 1 DESC LIMIT 5; - value_1 ---------- - 5 - 4 - 3 - 2 - 1 -(5 rows) - --- CTEs are supported even if they are on views -CREATE VIEW cte_view_1 AS -WITH c1 AS (SELECT * FROM users_table WHERE value_1 = 3) SELECT * FROM c1 WHERE value_2 < 4 AND EXISTS (SELECT * FROM c1); -SELECT * FROM cte_view_1 ORDER BY 1,2,3,4,5 LIMIT 5; - user_id | time | value_1 | value_2 | value_3 | value_4 ----------+---------------------------------+---------+---------+---------+--------- - 1 | Thu Nov 23 03:32:50.803031 2017 | 3 | 2 | 1 | - 2 | Thu Nov 23 13:52:54.83829 2017 | 3 | 1 | 4 | - 3 | Wed Nov 22 23:24:32.080584 2017 | 3 | 2 | 5 | - 4 | Wed Nov 22 23:59:46.493416 2017 | 3 | 1 | 3 | - 4 | Thu Nov 23 01:55:21.824618 2017 | 3 | 1 | 4 | -(5 rows) - --- this is single shard query and still not supported since it has view + cte --- router planner can't detect it -SELECT * FROM cte_view_1 WHERE user_id = 2 ORDER BY 1,2,3,4,5; - user_id | time | value_1 | value_2 | value_3 | value_4 ----------+--------------------------------+---------+---------+---------+--------- - 2 | Thu Nov 23 13:52:54.83829 2017 | 3 | 1 | 4 | -(1 row) - --- if CTE itself prunes down to a single shard than the view is supported (router plannable) -CREATE VIEW cte_view_2 AS -WITH c1 AS (SELECT * FROM users_table WHERE user_id = 2) SELECT * FROM c1 WHERE value_1 = 3; -SELECT * FROM cte_view_2; - user_id | time | value_1 | value_2 | value_3 | value_4 ----------+---------------------------------+---------+---------+---------+--------- - 2 | Thu Nov 23 00:19:14.138058 2017 | 3 | 4 | 0 | - 2 | Thu Nov 23 13:52:54.83829 2017 | 3 | 1 | 4 | - 2 | Wed Nov 22 18:19:49.944985 2017 | 3 | 5 | 1 | - 2 | Thu Nov 23 11:41:04.042936 2017 | 3 | 4 | 1 | -(4 rows) - -CREATE VIEW router_view AS SELECT * FROM users_table WHERE user_id = 2; --- router plannable -SELECT user_id FROM router_view GROUP BY 1; - user_id ---------- - 2 -(1 row) - --- join a router view - SELECT * FROM (SELECT user_id FROM router_view GROUP BY 1) rv JOIN recent_events USING (user_id) ORDER BY 2 LIMIT 3; - user_id | time ----------+--------------------------------- - 2 | Thu Nov 23 17:26:14.563216 2017 -(1 row) - - SELECT * FROM (SELECT user_id FROM router_view GROUP BY 1) rv JOIN (SELECT * FROM recent_events) re USING (user_id) ORDER BY 2 LIMIT 3; - user_id | time ----------+--------------------------------- - 2 | Thu Nov 23 17:26:14.563216 2017 -(1 row) - --- views with limits -CREATE VIEW recent_10_users AS - SELECT user_id, max(time) as lastseen FROM users_table - GROUP BY user_id - ORDER BY lastseen DESC - LIMIT 10; --- this is not supported since it has limit in it and subquery_pushdown is not set -SELECT * FROM recent_10_users; - user_id | lastseen ----------+--------------------------------- - 1 | Thu Nov 23 17:30:34.635085 2017 - 3 | Thu Nov 23 17:18:51.048758 2017 - 5 | Thu Nov 23 16:48:32.08896 2017 - 4 | Thu Nov 23 15:32:02.360969 2017 - 6 | Thu Nov 23 14:43:18.024104 2017 - 2 | Thu Nov 23 13:52:54.83829 2017 -(6 rows) - -SET citus.subquery_pushdown to ON; --- still not supported since outer query does not have limit --- it shows a different (subquery with single relation) error message -SELECT * FROM recent_10_users; -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries with limit are not supported yet --- now it displays more correct error message -SELECT et.* FROM recent_10_users JOIN events_table et USING(user_id); -ERROR: cannot push down this subquery -DETAIL: Limit in subquery without limit in the outermost query is unsupported --- now both are supported when there is a limit on the outer most query -SELECT * FROM recent_10_users ORDER BY lastseen DESC LIMIT 10; - user_id | lastseen ----------+--------------------------------- - 1 | Thu Nov 23 17:30:34.635085 2017 - 3 | Thu Nov 23 17:18:51.048758 2017 - 5 | Thu Nov 23 16:48:32.08896 2017 - 4 | Thu Nov 23 15:32:02.360969 2017 - 6 | Thu Nov 23 14:43:18.024104 2017 - 2 | Thu Nov 23 13:52:54.83829 2017 -(6 rows) - -SELECT et.* FROM recent_10_users JOIN events_table et USING(user_id) ORDER BY et.time DESC LIMIT 10; - user_id | time | event_type | value_2 | value_3 | value_4 ----------+---------------------------------+------------+---------+---------+--------- - 1 | Thu Nov 23 21:54:46.924477 2017 | 6 | 4 | 5 | - 4 | Thu Nov 23 18:10:21.338399 2017 | 1 | 2 | 4 | - 3 | Thu Nov 23 18:08:26.550729 2017 | 2 | 4 | 3 | - 2 | Thu Nov 23 17:26:14.563216 2017 | 1 | 5 | 3 | - 3 | Thu Nov 23 16:44:41.903713 2017 | 4 | 2 | 2 | - 3 | Thu Nov 23 16:31:56.219594 2017 | 5 | 1 | 2 | - 4 | Thu Nov 23 16:20:33.264457 2017 | 0 | 0 | 3 | - 5 | Thu Nov 23 16:11:02.929469 2017 | 4 | 2 | 0 | - 2 | Thu Nov 23 15:58:49.273421 2017 | 5 | 1 | 2 | - 5 | Thu Nov 23 14:40:40.467511 2017 | 1 | 4 | 1 | -(10 rows) - -RESET citus.subquery_pushdown; -VACUUM ANALYZE users_table; --- explain tests -EXPLAIN (COSTS FALSE) SELECT user_id FROM recent_selected_users GROUP BY 1 ORDER BY 1; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: remote_scan.user_id - -> HashAggregate - Group Key: remote_scan.user_id - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> HashAggregate - Group Key: users_table.user_id - -> Hash Join - Hash Cond: (users_table.user_id = ru.user_id) - -> Seq Scan on users_table_1400256 users_table - Filter: ((value_1 >= 1) AND (value_1 < 3)) - -> Hash - -> Subquery Scan on ru - -> Sort - Sort Key: (max(users_table_1."time")) DESC - -> HashAggregate - Group Key: users_table_1.user_id - Filter: (max(users_table_1."time") > '2017-11-23 16:20:33.264457'::timestamp without time zone) - -> Seq Scan on users_table_1400256 users_table_1 -(23 rows) - -EXPLAIN (COSTS FALSE) SELECT * - FROM ( - (SELECT user_id FROM recent_users) - UNION - (SELECT user_id FROM selected_users) ) u - WHERE user_id < 4 AND user_id > 1 - ORDER BY user_id; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: remote_scan.user_id - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Unique - -> Sort - Sort Key: recent_users.user_id - -> Append - -> Subquery Scan on recent_users - -> Sort - Sort Key: (max(users_table."time")) DESC - -> GroupAggregate - Group Key: users_table.user_id - Filter: (max(users_table."time") > '2017-11-23 16:20:33.264457'::timestamp without time zone) - -> Sort - Sort Key: users_table.user_id - -> Seq Scan on users_table_1400256 users_table - Filter: ((user_id < 4) AND (user_id > 1)) - -> Seq Scan on users_table_1400256 users_table_1 - Filter: ((value_1 >= 1) AND (value_1 < 3) AND (user_id < 4) AND (user_id > 1)) -(23 rows) - -EXPLAIN (COSTS FALSE) SELECT et.* FROM recent_10_users JOIN events_table et USING(user_id) ORDER BY et.time DESC LIMIT 10; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan."time" DESC - -> Custom Scan (Citus Real-Time) - -> Distributed Subplan 98_1 - -> Limit - -> Sort - Sort Key: max((max(remote_scan.lastseen))) DESC - -> HashAggregate - Group Key: remote_scan.user_id - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Limit - -> Sort - Sort Key: (max("time")) DESC - -> HashAggregate - Group Key: user_id - -> Seq Scan on users_table_1400256 users_table - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Limit - -> Sort - Sort Key: et."time" DESC - -> Hash Join - Hash Cond: (intermediate_result.user_id = et.user_id) - -> Function Scan on read_intermediate_result intermediate_result - -> Hash - -> Seq Scan on events_table_1400260 et -(33 rows) - -SET citus.subquery_pushdown to ON; -EXPLAIN (COSTS FALSE) SELECT et.* FROM recent_10_users JOIN events_table et USING(user_id) ORDER BY et.time DESC LIMIT 10; - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan."time" DESC - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> Limit - -> Sort - Sort Key: et."time" DESC - -> Hash Join - Hash Cond: (et.user_id = recent_10_users.user_id) - -> Seq Scan on events_table_1400260 et - -> Hash - -> Subquery Scan on recent_10_users - -> Limit - -> Sort - Sort Key: (max(users_table."time")) DESC - -> HashAggregate - Group Key: users_table.user_id - -> Seq Scan on users_table_1400256 users_table -(22 rows) - -RESET citus.subquery_pushdown; -DROP VIEW recent_10_users; -DROP VIEW router_view; -DROP VIEW cte_view_2; -DROP VIEW cte_view_1; -DROP VIEW distinct_value_1; -DROP VIEW distinct_user_with_value_1_3; -DROP VIEW recent_selected_users; -DROP VIEW selected_users; -DROP VIEW recent_events; -DROP VIEW recent_users; diff --git a/src/test/regress/expected/relation_access_tracking.out b/src/test/regress/expected/relation_access_tracking.out index 269bac822..2a67c3005 100644 --- a/src/test/regress/expected/relation_access_tracking.out +++ b/src/test/regress/expected/relation_access_tracking.out @@ -1,13 +1,6 @@ --- --- tests around access tracking within transaction blocks --- -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 10 AS version_ten_or_above; - version_ten_or_above ----------------------- - t -(1 row) - CREATE SCHEMA access_tracking; SET search_path TO 'access_tracking'; CREATE OR REPLACE FUNCTION relation_select_access_mode(relationId Oid) @@ -46,17 +39,17 @@ BEGIN RETURN 'not_accessed'; ELSIF relationShardAccess = 1 THEN RETURN 'reference_table_access'; - ELSE + ELSE RETURN 'parallel_access'; END IF; END; $$ LANGUAGE 'plpgsql' IMMUTABLE; -CREATE VIEW relation_acesses AS - SELECT table_name, +CREATE VIEW relation_acesses AS + SELECT table_name, relation_access_mode_to_text(table_name, relation_select_access_mode(table_name::regclass)) as select_access, relation_access_mode_to_text(table_name, relation_dml_access_mode(table_name::regclass)) as dml_access, relation_access_mode_to_text(table_name, relation_ddl_access_mode(table_name::regclass)) as ddl_access - FROM + FROM ((SELECT 'table_' || i as table_name FROM generate_series(1, 7) i) UNION (SELECT 'partitioning_test') UNION (SELECT 'partitioning_test_2009') UNION (SELECT 'partitioning_test_2010')) tables; SET citus.shard_replication_factor TO 1; CREATE TABLE table_1 (key int, value int); @@ -136,7 +129,7 @@ SELECT * FROM relation_acesses WHERE table_name = 'table_1'; table_1 | not_parallel_accessed | not_parallel_accessed | not_parallel_accessed (1 row) --- a very simple test that first checks sequential +-- a very simple test that first checks sequential -- and parallel SELECTs,DMLs, and DDLs BEGIN; SELECT * FROM relation_acesses WHERE table_name = 'table_1'; @@ -246,12 +239,12 @@ BEGIN; ROLLBACK; -- a simple join touches single shard per table BEGIN; - SELECT - count(*) - FROM + SELECT + count(*) + FROM table_1, table_2, table_3, table_4, table_5 WHERE - table_1.key = table_2.key AND table_2.key = table_3.key AND + table_1.key = table_2.key AND table_2.key = table_3.key AND table_3.key = table_4.key AND table_4.key = table_5.key AND table_1.key = 1; count @@ -274,9 +267,9 @@ BEGIN; ROLLBACK; -- a simple real-time join touches all shard per table BEGIN; - SELECT - count(*) - FROM + SELECT + count(*) + FROM table_1, table_2 WHERE table_1.key = table_2.key; @@ -297,9 +290,9 @@ ROLLBACK; -- in sequential mode BEGIN; SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - SELECT - count(*) - FROM + SELECT + count(*) + FROM table_1, table_2 WHERE table_1.key = table_2.key; @@ -318,16 +311,16 @@ BEGIN; ROLLBACK; -- a simple subquery pushdown that touches all shards BEGIN; - SELECT - count(*) - FROM + SELECT + count(*) + FROM ( - SELECT + SELECT random() - FROM + FROM table_1, table_2, table_3, table_4, table_5 WHERE - table_1.key = table_2.key AND table_2.key = table_3.key AND + table_1.key = table_2.key AND table_2.key = table_3.key AND table_3.key = table_4.key AND table_4.key = table_5.key ) as foo; count @@ -349,7 +342,7 @@ BEGIN; ROLLBACK; -- simple multi shard update both sequential and parallel modes --- note that in multi shard modify mode we always add select +-- note that in multi shard modify mode we always add select -- access for all the shards accessed. But, sequential mode is OK BEGIN; UPDATE table_1 SET value = 15; @@ -371,8 +364,8 @@ BEGIN; ROLLBACK; -- now UPDATE/DELETE with subselect pushdown BEGIN; - UPDATE - table_1 SET value = 15 + UPDATE + table_1 SET value = 15 WHERE key IN (SELECT key FROM table_2 JOIN table_3 USING (key) WHERE table_2.value = 15); SELECT * FROM relation_acesses WHERE table_name IN ('table_1', 'table_2', 'table_3') ORDER BY 1; table_name | select_access | dml_access | ddl_access @@ -417,19 +410,18 @@ BEGIN; (2 rows) ROLLBACK; - -- recursively planned SELECT BEGIN; - SELECT - count(*) - FROM + SELECT + count(*) + FROM ( - SELECT + SELECT random() - FROM + FROM table_1, table_2 WHERE - table_1.key = table_2.key + table_1.key = table_2.key OFFSET 0 ) as foo; count @@ -448,16 +440,16 @@ ROLLBACK; -- recursively planned SELECT and coordinator INSERT .. SELECT BEGIN; INSERT INTO table_3 (key) - SELECT + SELECT * - FROM + FROM ( - SELECT + SELECT random() * 1000 - FROM + FROM table_1, table_2 WHERE - table_1.key = table_2.key + table_1.key = table_2.key OFFSET 0 ) as foo; SELECT * FROM relation_acesses WHERE table_name IN ('table_1', 'table_2', 'table_3') ORDER BY 1; @@ -469,17 +461,17 @@ BEGIN; (3 rows) ROLLBACK; --- recursively planned SELECT and coordinator INSERT .. SELECT +-- recursively planned SELECT and coordinator INSERT .. SELECT -- but modifies single shard, marked as sequential operation BEGIN; INSERT INTO table_3 (key) - SELECT + SELECT * - FROM + FROM ( - SELECT + SELECT random() * 1000 - FROM + FROM table_1, table_2 WHERE table_1.key = table_2.key @@ -499,16 +491,16 @@ ROLLBACK; BEGIN; DELETE FROM table_3 where key IN ( - SELECT + SELECT * - FROM + FROM ( - SELECT + SELECT table_1.key - FROM + FROM table_1, table_2 WHERE - table_1.key = table_2.key + table_1.key = table_2.key OFFSET 0 ) as foo ) AND value IN (SELECT key FROM table_4); @@ -576,7 +568,7 @@ BEGIN; table_6 | reference_table_access | reference_table_access | not_accessed (1 row) - ALTER TABLE table_6 ADD COLUMN x INT; + ALTER TABLE table_6 ADD COLUMN x INT; SELECT * FROM relation_acesses WHERE table_name IN ('table_6'); table_name | select_access | dml_access | ddl_access ------------+------------------------+------------------------+------------------------ @@ -887,9 +879,8 @@ NOTICE: truncate cascades to table "table_2" (2 rows) ROLLBACK; --- CTEs with SELECT only should work fine +-- CTEs with SELECT only should work fine BEGIN; - WITH cte AS (SELECT count(*) FROM table_1) SELECT * FROM cte; count @@ -904,7 +895,7 @@ BEGIN; (1 row) COMMIT; --- CTEs with SELECT only in sequential mode should work fine +-- CTEs with SELECT only in sequential mode should work fine BEGIN; SET LOCAL citus.multi_shard_modify_mode = 'sequential'; WITH cte AS (SELECT count(*) FROM table_1) @@ -923,7 +914,6 @@ BEGIN; COMMIT; -- modifying CTEs should work fine with multi-row inserts, which are by default in sequential BEGIN; - WITH cte_1 AS (INSERT INTO table_1 VALUES (1000,1000), (1001, 1001), (1002, 1002) RETURNING *) SELECT * FROM cte_1 ORDER BY 1; key | value @@ -942,7 +932,6 @@ BEGIN; ROLLBACK; -- modifying CTEs should work fine with parallel mode BEGIN; - WITH cte_1 AS (UPDATE table_1 SET value = 15 RETURNING *) SELECT count(*) FROM cte_1 ORDER BY 1; count @@ -959,7 +948,6 @@ BEGIN; ROLLBACK; -- modifying CTEs should work fine with sequential mode BEGIN; - WITH cte_1 AS (UPDATE table_1 SET value = 15 RETURNING *) SELECT count(*) FROM cte_1 ORDER BY 1; count @@ -974,13 +962,13 @@ BEGIN; (1 row) ROLLBACK; --- create distributed table with data loading +-- create distributed table with data loading -- should mark both parallel dml and parallel ddl DROP TABLE table_3; CREATE TABLE table_3 (key int, value int); INSERT INTO table_3 SELECT i, i FROM generate_series(0,100) i; BEGIN; - SELECT create_distributed_table('table_3', 'key'); + SELECT create_distributed_table('table_3', 'key'); NOTICE: Copying data from local table... create_distributed_table -------------------------- diff --git a/src/test/regress/expected/replicated_partitioned_table.out b/src/test/regress/expected/replicated_partitioned_table.out index 72ff280a1..fb6c5eb16 100644 --- a/src/test/regress/expected/replicated_partitioned_table.out +++ b/src/test/regress/expected/replicated_partitioned_table.out @@ -6,24 +6,16 @@ CREATE SCHEMA partitioned_table_replicated; SET search_path TO partitioned_table_replicated; SET citus.shard_count TO 4; SET citus.shard_replication_factor TO 2; --- print major version number for version-specific tests -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int < 11 AS server_version_is_10; - server_version_is_10 ----------------------- - f -(1 row) - CREATE TABLE collections ( key bigint, ts timestamptz, collection_id integer, - value numeric + value numeric ) PARTITION BY LIST ( collection_id ); -CREATE TABLE collections_1 +CREATE TABLE collections_1 PARTITION OF collections (key, ts, collection_id, value) FOR VALUES IN ( 1 ); -CREATE TABLE collections_2 +CREATE TABLE collections_2 PARTITION OF collections (key, ts, collection_id, value) FOR VALUES IN ( 2 ); -- load some data data @@ -46,7 +38,7 @@ CREATE TABLE collections_3 PARTITION OF collections FOR VALUES IN ( 3 ); -- now attaching non distributed table to a distributed table CREATE TABLE collections_4 AS SELECT * FROM collections LIMIT 0; -- load some data -INSERT INTO collections_4 SELECT i, '2009-01-01', 4, i FROM generate_series (0, 10) i; +INSERT INTO collections_4 SELECT i, '2009-01-01', 4, i FROM generate_series (0, 10) i; ALTER TABLE collections ATTACH PARTITION collections_4 FOR VALUES IN ( 4 ); NOTICE: Copying data from local table... -- finally attach a distributed table to a distributed table @@ -58,15 +50,15 @@ SELECT create_distributed_table('collections_5', 'key'); (1 row) -- load some data -INSERT INTO collections_5 SELECT i, '2009-01-01', 5, i FROM generate_series (0, 10) i; +INSERT INTO collections_5 SELECT i, '2009-01-01', 5, i FROM generate_series (0, 10) i; ALTER TABLE collections ATTACH PARTITION collections_5 FOR VALUES IN ( 5 ); -- make sure that we've all the placements -SELECT +SELECT logicalrelid, count(*) as placement_count -FROM +FROM pg_dist_shard, pg_dist_shard_placement -WHERE - logicalrelid::text LIKE '%collections%' AND +WHERE + logicalrelid::text LIKE '%collections%' AND pg_dist_shard.shardid = pg_dist_shard_placement.shardid GROUP BY logicalrelid @@ -83,11 +75,11 @@ ORDER BY (6 rows) -- and, make sure that all tables are colocated -SELECT - count(DISTINCT colocationid) -FROM - pg_dist_partition -WHERE +SELECT + count(DISTINCT colocationid) +FROM + pg_dist_partition +WHERE logicalrelid::text LIKE '%collections%'; count ------- @@ -127,7 +119,7 @@ ERROR: modifications on partitions when replication factor is greater than 1 is HINT: Run the query on the parent table "collections" instead. \. invalid command \. --- DDLs are not allowed +-- DDLs are not allowed CREATE INDEX index_on_partition ON collections_1(key); ERROR: modifications on partitions when replication factor is greater than 1 is not supported HINT: Run the query on the parent table "collections" instead. @@ -143,7 +135,7 @@ TRUNCATE collections, collections_1; ERROR: modifications on partitions when replication factor is greater than 1 is not supported HINT: Run the query on the parent table "collections" instead. -- modifying CTEs are also not allowed -WITH collections_5_cte AS +WITH collections_5_cte AS ( DELETE FROM collections_5 RETURNING * ) @@ -159,10 +151,10 @@ SELECT create_distributed_table('fkey_test', 'key'); (1 row) ALTER TABLE - collections_5 -ADD CONSTRAINT - fkey_delete FOREIGN KEY(key) -REFERENCES + collections_5 +ADD CONSTRAINT + fkey_delete FOREIGN KEY(key) +REFERENCES fkey_test(key) ON DELETE CASCADE; ERROR: cannot create foreign key constraint DETAIL: Citus Community Edition currently supports foreign key constraints only for "citus.shard_replication_factor = 1". @@ -205,7 +197,7 @@ INSERT INTO collections_agg SELECT collection_id, sum(key) FROM collections_1 GR -- now make sure that repair functionality works fine -- create a table and create its distribution metadata CREATE TABLE customer_engagements (id integer, event_id int) PARTITION BY LIST ( event_id ); -CREATE TABLE customer_engagements_1 +CREATE TABLE customer_engagements_1 PARTITION OF customer_engagements FOR VALUES IN ( 1 ); CREATE TABLE customer_engagements_2 @@ -233,7 +225,7 @@ INSERT INTO customer_engagements VALUES (2, 2); -- the following queries does the following: -- (i) create a new shard -- (ii) mark the second shard placements as unhealthy --- (iii) do basic checks i.e., only allow copy from healthy placement to unhealthy ones +-- (iii) do basic checks i.e., only allow copy from healthy placement to unhealthy ones -- (iv) do a successful master_copy_shard_placement from the first placement to the second -- (v) mark the first placement as unhealthy and execute a query that is routed to the second placement SELECT groupid AS worker_2_group FROM pg_dist_node WHERE nodeport=:worker_2_port \gset diff --git a/src/test/regress/expected/replicated_partitioned_table_0.out b/src/test/regress/expected/replicated_partitioned_table_0.out deleted file mode 100644 index 08206f63f..000000000 --- a/src/test/regress/expected/replicated_partitioned_table_0.out +++ /dev/null @@ -1,299 +0,0 @@ --- --- Distributed Partitioned Table Tests --- -SET citus.next_shard_id TO 1760000; -CREATE SCHEMA partitioned_table_replicated; -SET search_path TO partitioned_table_replicated; -SET citus.shard_count TO 4; -SET citus.shard_replication_factor TO 2; --- print major version number for version-specific tests -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int < 11 AS server_version_is_10; - server_version_is_10 ----------------------- - t -(1 row) - -CREATE TABLE collections ( - key bigint, - ts timestamptz, - collection_id integer, - value numeric -) PARTITION BY LIST ( collection_id ); -CREATE TABLE collections_1 - PARTITION OF collections (key, ts, collection_id, value) - FOR VALUES IN ( 1 ); -CREATE TABLE collections_2 - PARTITION OF collections (key, ts, collection_id, value) - FOR VALUES IN ( 2 ); --- load some data data -INSERT INTO collections (key, ts, collection_id, value) VALUES (1, '2009-01-01', 1, 1); -INSERT INTO collections (key, ts, collection_id, value) VALUES (2, '2009-01-01', 1, 2); -INSERT INTO collections (key, ts, collection_id, value) VALUES (3, '2009-01-01', 2, 1); -INSERT INTO collections (key, ts, collection_id, value) VALUES (4, '2009-01-01', 2, 2); --- in the first case, we'll distributed the --- already existing partitioninong hierarcy -SELECT create_distributed_table('collections', 'key'); -NOTICE: Copying data from local table... -NOTICE: Copying data from local table... - create_distributed_table --------------------------- - -(1 row) - --- now create partition of a already distributed table -CREATE TABLE collections_3 PARTITION OF collections FOR VALUES IN ( 3 ); --- now attaching non distributed table to a distributed table -CREATE TABLE collections_4 AS SELECT * FROM collections LIMIT 0; --- load some data -INSERT INTO collections_4 SELECT i, '2009-01-01', 4, i FROM generate_series (0, 10) i; -ALTER TABLE collections ATTACH PARTITION collections_4 FOR VALUES IN ( 4 ); -NOTICE: Copying data from local table... --- finally attach a distributed table to a distributed table -CREATE TABLE collections_5 AS SELECT * FROM collections LIMIT 0; -SELECT create_distributed_table('collections_5', 'key'); - create_distributed_table --------------------------- - -(1 row) - --- load some data -INSERT INTO collections_5 SELECT i, '2009-01-01', 5, i FROM generate_series (0, 10) i; -ALTER TABLE collections ATTACH PARTITION collections_5 FOR VALUES IN ( 5 ); --- make sure that we've all the placements -SELECT - logicalrelid, count(*) as placement_count -FROM - pg_dist_shard, pg_dist_shard_placement -WHERE - logicalrelid::text LIKE '%collections%' AND - pg_dist_shard.shardid = pg_dist_shard_placement.shardid -GROUP BY - logicalrelid -ORDER BY - 1,2; - logicalrelid | placement_count ----------------+----------------- - collections | 8 - collections_1 | 8 - collections_2 | 8 - collections_3 | 8 - collections_4 | 8 - collections_5 | 8 -(6 rows) - --- and, make sure that all tables are colocated -SELECT - count(DISTINCT colocationid) -FROM - pg_dist_partition -WHERE - logicalrelid::text LIKE '%collections%'; - count -------- - 1 -(1 row) - --- make sure that any kind of modification is disallowed on partitions --- given that replication factor > 1 -INSERT INTO collections_4 (key, ts, collection_id, value) VALUES (4, '2009-01-01', 2, 2); -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- single shard update/delete not allowed -UPDATE collections_1 SET ts = now() WHERE key = 1; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. -DELETE FROM collections_1 WHERE ts = now() AND key = 1; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- multi shard update/delete are not allowed -UPDATE collections_1 SET ts = now(); -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. -DELETE FROM collections_1 WHERE ts = now(); -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- insert..select pushdown -INSERT INTO collections_1 SELECT * FROM collections_1; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- insert..select via coordinator -INSERT INTO collections_1 SELECT * FROM collections_1 OFFSET 0; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- COPY is not allowed -COPY collections_1 FROM STDIN; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. -\. -invalid command \. --- DDLs are not allowed -CREATE INDEX index_on_partition ON collections_1(key); -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- EXPLAIN with modifications is not allowed as well -UPDATE collections_1 SET ts = now() WHERE key = 1; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- TRUNCATE is also not allowed -TRUNCATE collections_1; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. -TRUNCATE collections, collections_1; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- modifying CTEs are also not allowed -WITH collections_5_cte AS -( - DELETE FROM collections_5 RETURNING * -) -SELECT * FROM collections_5_cte; -ERROR: modifications on partitions when replication factor is greater than 1 is not supported -HINT: Run the query on the parent table "collections" instead. --- foreign key creation is disallowed due to replication factor > 1 -CREATE TABLE fkey_test (key bigint PRIMARY KEY); -SELECT create_distributed_table('fkey_test', 'key'); - create_distributed_table --------------------------- - -(1 row) - -ALTER TABLE - collections_5 -ADD CONSTRAINT - fkey_delete FOREIGN KEY(key) -REFERENCES - fkey_test(key) ON DELETE CASCADE; -ERROR: cannot create foreign key constraint -DETAIL: Citus Community Edition currently supports foreign key constraints only for "citus.shard_replication_factor = 1". -HINT: Please change "citus.shard_replication_factor to 1". To learn more about using foreign keys with other replication factors, please contact us at https://citusdata.com/about/contact_us. --- we should be able to attach and detach partitions --- given that those DDLs are on the parent table -CREATE TABLE collections_6 - PARTITION OF collections (key, ts, collection_id, value) - FOR VALUES IN ( 6 ); -ALTER TABLE collections DETACH PARTITION collections_6; -ALTER TABLE collections ATTACH PARTITION collections_6 FOR VALUES IN ( 6 ); --- read queries works just fine -SELECT count(*) FROM collections_1 WHERE key = 1; - count -------- - 1 -(1 row) - -SELECT count(*) FROM collections_1 WHERE key != 1; - count -------- - 1 -(1 row) - --- rollups SELECT'ing from partitions should work just fine -CREATE TABLE collections_agg ( - key bigint, - sum_value numeric -); -SELECT create_distributed_table('collections_agg', 'key'); - create_distributed_table --------------------------- - -(1 row) - --- pushdown roll-up -INSERT INTO collections_agg SELECT key, sum(key) FROM collections_1 GROUP BY key; --- coordinator roll-up -INSERT INTO collections_agg SELECT collection_id, sum(key) FROM collections_1 GROUP BY collection_id; --- now make sure that repair functionality works fine --- create a table and create its distribution metadata -CREATE TABLE customer_engagements (id integer, event_id int) PARTITION BY LIST ( event_id ); -CREATE TABLE customer_engagements_1 - PARTITION OF customer_engagements - FOR VALUES IN ( 1 ); -CREATE TABLE customer_engagements_2 - PARTITION OF customer_engagements - FOR VALUES IN ( 2 ); --- add some indexes -CREATE INDEX ON customer_engagements (id); -ERROR: cannot create index on partitioned table "customer_engagements" -CREATE INDEX ON customer_engagements (event_id); -ERROR: cannot create index on partitioned table "customer_engagements" -CREATE INDEX ON customer_engagements (id, event_id); -ERROR: cannot create index on partitioned table "customer_engagements" --- distribute the table --- create a single shard on the first worker -SET citus.shard_count TO 1; -SET citus.shard_replication_factor TO 2; -SELECT create_distributed_table('customer_engagements', 'id', 'hash'); - create_distributed_table --------------------------- - -(1 row) - --- ingest some data for the tests -INSERT INTO customer_engagements VALUES (1, 1); -INSERT INTO customer_engagements VALUES (2, 1); -INSERT INTO customer_engagements VALUES (1, 2); -INSERT INTO customer_engagements VALUES (2, 2); --- the following queries does the following: --- (i) create a new shard --- (ii) mark the second shard placements as unhealthy --- (iii) do basic checks i.e., only allow copy from healthy placement to unhealthy ones --- (iv) do a successful master_copy_shard_placement from the first placement to the second --- (v) mark the first placement as unhealthy and execute a query that is routed to the second placement -SELECT groupid AS worker_2_group FROM pg_dist_node WHERE nodeport=:worker_2_port \gset -SELECT groupid AS worker_1_group FROM pg_dist_node WHERE nodeport=:worker_1_port \gset --- get the newshardid -SELECT shardid as newshardid FROM pg_dist_shard WHERE logicalrelid = 'customer_engagements'::regclass -\gset --- now, update the second placement as unhealthy -UPDATE pg_dist_placement SET shardstate = 3 WHERE shardid = :newshardid - AND groupid = :worker_2_group; --- cannot repair a shard after a modification (transaction still open during repair) -BEGIN; -INSERT INTO customer_engagements VALUES (1, 1); -SELECT master_copy_shard_placement(:newshardid, 'localhost', :worker_1_port, 'localhost', :worker_2_port); -ERROR: cannot open new connections after the first modification command within a transaction -ROLLBACK; --- modifications after reparing a shard are fine (will use new metadata) -BEGIN; -SELECT master_copy_shard_placement(:newshardid, 'localhost', :worker_1_port, 'localhost', :worker_2_port); - master_copy_shard_placement ------------------------------ - -(1 row) - -ALTER TABLE customer_engagements ADD COLUMN value float DEFAULT 1.0; -SELECT * FROM customer_engagements ORDER BY 1,2,3; - id | event_id | value -----+----------+------- - 1 | 1 | 1 - 1 | 2 | 1 - 2 | 1 | 1 - 2 | 2 | 1 -(4 rows) - -ROLLBACK; -BEGIN; -SELECT master_copy_shard_placement(:newshardid, 'localhost', :worker_1_port, 'localhost', :worker_2_port); - master_copy_shard_placement ------------------------------ - -(1 row) - -INSERT INTO customer_engagements VALUES (1, 1); -SELECT count(*) FROM customer_engagements; - count -------- - 5 -(1 row) - -ROLLBACK; --- TRUNCATE is allowed on the parent table --- try it just before dropping the table -TRUNCATE collections; -SET search_path TO public; -DROP SCHEMA partitioned_table_replicated CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table partitioned_table_replicated.collections -drop cascades to table partitioned_table_replicated.fkey_test -drop cascades to table partitioned_table_replicated.collections_agg -drop cascades to table partitioned_table_replicated.customer_engagements diff --git a/src/test/regress/expected/sql_procedure.out b/src/test/regress/expected/sql_procedure.out index c84db5611..3da4f0386 100644 --- a/src/test/regress/expected/sql_procedure.out +++ b/src/test/regress/expected/sql_procedure.out @@ -3,14 +3,6 @@ -- -- Tests basic PROCEDURE functionality with SQL and PLPGSQL procedures. -- --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - SET citus.next_shard_id TO 100500; CREATE SCHEMA procedure_schema; SET SEARCH_PATH = procedure_schema; diff --git a/src/test/regress/expected/sql_procedure_0.out b/src/test/regress/expected/sql_procedure_0.out deleted file mode 100644 index a8b289a63..000000000 --- a/src/test/regress/expected/sql_procedure_0.out +++ /dev/null @@ -1,284 +0,0 @@ --- --- SQL_PROCEDURE --- --- Tests basic PROCEDURE functionality with SQL and PLPGSQL procedures. --- --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - f -(1 row) - -SET citus.next_shard_id TO 100500; -CREATE SCHEMA procedure_schema; -SET SEARCH_PATH = procedure_schema; -CREATE TABLE test_table(id integer , org_id integer); -CREATE UNIQUE INDEX idx_table ON test_table(id, org_id); -SELECT create_distributed_table('test_table','id'); - create_distributed_table --------------------------- - -(1 row) - -INSERT INTO test_table VALUES(1, 1); --- test CREATE PROCEDURE -CREATE PROCEDURE test_procedure_delete_insert(id int, org_id int) LANGUAGE SQL AS $$ - DELETE FROM test_table; - INSERT INTO test_table VALUES(id, org_id); -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_delete_insert(id int, org_id... - ^ -CALL test_procedure_delete_insert(2,3); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_delete_insert(2,3); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - --- commit/rollback is not allowed in procedures in SQL --- following calls should fail -CREATE PROCEDURE test_procedure_commit(tt_id int, tt_org_id int) LANGUAGE SQL AS $$ - DELETE FROM test_table; - COMMIT; - INSERT INTO test_table VALUES(tt_id, -1); - UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id; - COMMIT; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_commit(tt_id int, tt_org_id ... - ^ -CALL test_procedure_commit(2,5); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_commit(2,5); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - -CREATE PROCEDURE test_procedure_rollback(tt_id int, tt_org_id int) LANGUAGE SQL AS $$ - DELETE FROM test_table; - ROLLBACK; - UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id; - COMMIT; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_rollback(tt_id int, tt_org_i... - ^ -CALL test_procedure_rollback(2,15); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_rollback(2,15); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - -DROP PROCEDURE test_procedure_delete_insert(int, int); -ERROR: syntax error at or near "PROCEDURE" -LINE 1: DROP PROCEDURE test_procedure_delete_insert(int, int); - ^ -DROP PROCEDURE test_procedure_commit(int, int); -ERROR: syntax error at or near "PROCEDURE" -LINE 1: DROP PROCEDURE test_procedure_commit(int, int); - ^ -DROP PROCEDURE test_procedure_rollback(int, int); -ERROR: syntax error at or near "PROCEDURE" -LINE 1: DROP PROCEDURE test_procedure_rollback(int, int); - ^ --- same tests with plpgsql --- test CREATE PROCEDURE -CREATE PROCEDURE test_procedure_delete_insert(id int, org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - DELETE FROM test_table; - INSERT INTO test_table VALUES(id, org_id); -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_delete_insert(id int, org_id... - ^ -CALL test_procedure_delete_insert(2,3); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_delete_insert(2,3); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - --- notice that the update succeed and committed -CREATE PROCEDURE test_procedure_modify_insert(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id; - COMMIT; - INSERT INTO test_table VALUES (tt_id, tt_org_id); - ROLLBACK; -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_modify_insert(tt_id int, tt_... - ^ -CALL test_procedure_modify_insert(2,12); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_modify_insert(2,12); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - -CREATE PROCEDURE test_procedure_modify_insert_commit(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id; - COMMIT; - INSERT INTO test_table VALUES (tt_id, tt_org_id); - COMMIT; -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_modify_insert_commit(tt_id i... - ^ -CALL test_procedure_modify_insert_commit(2,30); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_modify_insert_commit(2,30); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - --- delete is commited but insert is rolled back -CREATE PROCEDURE test_procedure_rollback(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - DELETE FROM test_table; - COMMIT; - INSERT INTO test_table VALUES (tt_id, tt_org_id); - ROLLBACK; -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_rollback(tt_id int, tt_org_i... - ^ -CALL test_procedure_rollback(2,5); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_rollback(2,5); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - --- rollback is successfull when insert is on multiple rows -CREATE PROCEDURE test_procedure_rollback_2(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - DELETE FROM test_table; - COMMIT; - INSERT INTO test_table VALUES (tt_id, tt_org_id), (tt_id+1, tt_org_id+1); - ROLLBACK; -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_rollback_2(tt_id int, tt_org... - ^ -CALL test_procedure_rollback_2(12, 15); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_rollback_2(12, 15); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - --- delete is rolled back, update is committed -CREATE PROCEDURE test_procedure_rollback_3(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - DELETE FROM test_table; - ROLLBACK; - UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id; - COMMIT; -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE PROCEDURE test_procedure_rollback_3(tt_id int, tt_org... - ^ -INSERT INTO test_table VALUES (1, 1), (2, 2); -ERROR: duplicate key value violates unique constraint "idx_table_100500" -DETAIL: Key (id, org_id)=(1, 1) already exists. -CONTEXT: while executing command on localhost:57638 -CALL test_procedure_rollback_3(2,15); -ERROR: syntax error at or near "CALL" -LINE 1: CALL test_procedure_rollback_3(2,15); - ^ -SELECT * FROM test_table ORDER BY 1, 2; - id | org_id -----+-------- - 1 | 1 -(1 row) - -TRUNCATE test_table; --- nested procedure calls should roll back normally -CREATE OR REPLACE PROCEDURE test_procedure_rollback(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - INSERT INTO test_table VALUES (tt_id+12, tt_org_id+12); - ROLLBACK; -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE OR REPLACE PROCEDURE test_procedure_rollback(tt_id in... - ^ -CREATE OR REPLACE PROCEDURE test_procedure_rollback_2(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - INSERT INTO test_table VALUES (tt_id+2, tt_org_id+1); - ROLLBACK; -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE OR REPLACE PROCEDURE test_procedure_rollback_2(tt_id ... - ^ -CREATE OR REPLACE PROCEDURE test_procedure(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$ -BEGIN - CALL test_procedure_rollback(tt_id, tt_org_id); - CALL test_procedure_rollback_2(tt_id, tt_org_id); - INSERT INTO test_table VALUES (tt_id+100, tt_org_id+100); - ROLLBACK; -END; -$$; -ERROR: syntax error at or near "PROCEDURE" -LINE 1: CREATE OR REPLACE PROCEDURE test_procedure(tt_id int, tt_org... - ^ -SELECT * from test_table; - id | org_id -----+-------- -(0 rows) - -call test_procedure(1,1); -ERROR: syntax error at or near "call" -LINE 1: call test_procedure(1,1); - ^ -call test_procedure(20, 20); -ERROR: syntax error at or near "call" -LINE 1: call test_procedure(20, 20); - ^ -SELECT * from test_table; - id | org_id -----+-------- -(0 rows) - -\set VERBOSITY terse -DROP SCHEMA procedure_schema CASCADE; -NOTICE: drop cascades to table test_table -\set VERBOSITY default -RESET SEARCH_PATH; diff --git a/src/test/regress/expected/window_functions.out b/src/test/regress/expected/window_functions.out index 7efbe8ee3..45dcd1d0a 100644 --- a/src/test/regress/expected/window_functions.out +++ b/src/test/regress/expected/window_functions.out @@ -1,13 +1,6 @@ -- =================================================================== -- test top level window functions that are pushdownable -- =================================================================== -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - t -(1 row) - -- a very simple window function with an aggregate and a window function -- distribution column is on the partition by clause SELECT @@ -55,9 +48,9 @@ FROM ( DISTINCT us.user_id, us.value_2, value_1, random() as r1 FROM users_table as us, events_table - WHERE + WHERE us.user_id = events_table.user_id AND event_type IN (1,2) - ORDER BY + ORDER BY user_id, value_2 ) s GROUP BY @@ -100,7 +93,7 @@ ORDER BY 5 | 0 (32 rows) --- window function operates on the results of +-- window function operates on the results of -- a join SELECT us.user_id, @@ -134,7 +127,7 @@ FROM JOIN events_table ev USING (user_id ) - ) j + ) j GROUP BY user_id, value_1 @@ -204,15 +197,15 @@ ORDER BY DROP VIEW users_view, window_view; -- window function uses columns from two different tables -SELECT +SELECT DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM +FROM events_table, users_table -WHERE +WHERE users_table.user_id = events_table.user_id WINDOW my_win AS (PARTITION BY events_table.user_id, users_table.value_1 ORDER BY events_table.time DESC) -ORDER BY +ORDER BY rnk DESC, 1 DESC LIMIT 10; user_id | rnk @@ -230,15 +223,15 @@ LIMIT 10; (10 rows) -- the same query with reference table column is also on the partition by clause -SELECT +SELECT DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM +FROM events_table, users_ref_test_table uref -WHERE +WHERE uref.id = events_table.user_id WINDOW my_win AS (PARTITION BY events_table.user_id, uref.k_no ORDER BY events_table.time DESC) -ORDER BY +ORDER BY rnk DESC, 1 DESC LIMIT 10; user_id | rnk @@ -257,15 +250,15 @@ LIMIT 10; -- similar query with no distribution column is on the partition by clause -- is not supported -SELECT +SELECT DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM +FROM events_table, users_ref_test_table uref -WHERE +WHERE uref.id = events_table.user_id WINDOW my_win AS (PARTITION BY events_table.value_2, uref.k_no ORDER BY events_table.time DESC) -ORDER BY +ORDER BY rnk DESC, 1 DESC LIMIT 10; ERROR: could not run distributed query because the window function that is used cannot be pushed down @@ -301,7 +294,7 @@ ORDER BY SELECT COUNT(*) OVER (PARTITION BY user_id, user_id + 1), rank() OVER (PARTITION BY user_id) as cnt1, - COUNT(*) OVER (PARTITION BY user_id, abs(value_1 - value_2)) as cnt2, + COUNT(*) OVER (PARTITION BY user_id, abs(value_1 - value_2)) as cnt2, date_trunc('min', lag(time) OVER (PARTITION BY user_id ORDER BY time)) as datee, rank() OVER my_win as rnnk, avg(CASE @@ -331,7 +324,7 @@ LIMIT 5; -- some tests with GROUP BY along with PARTITION BY SELECT - user_id, + user_id, rank() OVER my_win as my_rank, avg(avg(event_type)) OVER my_win_2 as avg, max(time) as mx_time @@ -664,17 +657,17 @@ ORDER BY (66 rows) -- some tests with GROUP BY, HAVING and LIMIT -SELECT +SELECT user_id, sum(event_type) OVER my_win , event_type FROM events_table GROUP BY user_id, event_type -HAVING count(*) > 2 +HAVING count(*) > 2 WINDOW my_win AS (PARTITION BY user_id, max(event_type) ORDER BY count(*) DESC) -ORDER BY +ORDER BY 2 DESC, 3 DESC, 1 DESC -LIMIT +LIMIT 5; user_id | sum | event_type ---------+-----+------------ @@ -737,11 +730,10 @@ LIMIT 4 | 2 (2 rows) - -- not a meaningful query, with interesting syntax SELECT - user_id, - AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), + user_id, + AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), AVG(avg(user_id)) OVER (PARTITION BY user_id, min(user_id), AVG(value_1)) FROM users_table @@ -762,8 +754,8 @@ ORDER BY SELECT coordinator_plan($Q$ EXPLAIN (COSTS FALSE) SELECT - user_id, - AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), + user_id, + AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), AVG(avg(user_id)) OVER (PARTITION BY user_id, min(user_id), AVG(value_1)) FROM users_table @@ -848,7 +840,7 @@ LIMIT 5; 4 | 17 | 3.5000000000000000 (5 rows) --- rank and ordering in the reverse order +-- rank and ordering in the reverse order SELECT user_id, avg(value_1), diff --git a/src/test/regress/expected/window_functions_0.out b/src/test/regress/expected/window_functions_0.out deleted file mode 100644 index de4d2f234..000000000 --- a/src/test/regress/expected/window_functions_0.out +++ /dev/null @@ -1,933 +0,0 @@ --- =================================================================== --- test top level window functions that are pushdownable --- =================================================================== -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - version_above_ten -------------------- - f -(1 row) - --- a very simple window function with an aggregate and a window function --- distribution column is on the partition by clause -SELECT - user_id, COUNT(*) OVER (PARTITION BY user_id), - rank() OVER (PARTITION BY user_id) -FROM - users_table -ORDER BY - 1 DESC, 2 DESC, 3 DESC -LIMIT 5; - user_id | count | rank ----------+-------+------ - 6 | 10 | 1 - 6 | 10 | 1 - 6 | 10 | 1 - 6 | 10 | 1 - 6 | 10 | 1 -(5 rows) - --- a more complicated window clause, including an aggregate --- in both the window clause and the target entry -SELECT - user_id, avg(avg(value_3)) OVER (PARTITION BY user_id, MIN(value_2)) -FROM - users_table -GROUP BY - 1 -ORDER BY - 2 DESC NULLS LAST, 1 DESC; - user_id | avg ----------+------------------ - 2 | 3 - 4 | 2.82608695652174 - 3 | 2.70588235294118 - 6 | 2.6 - 1 | 2.57142857142857 - 5 | 2.46153846153846 -(6 rows) - --- window clause operates on the results of a subquery -SELECT - user_id, max(value_1) OVER (PARTITION BY user_id, MIN(value_2)) -FROM ( - SELECT - DISTINCT us.user_id, us.value_2, value_1, random() as r1 - FROM - users_table as us, events_table - WHERE - us.user_id = events_table.user_id AND event_type IN (1,2) - ORDER BY - user_id, value_2 - ) s -GROUP BY - 1, value_1 -ORDER BY - 2 DESC, 1; - user_id | max ----------+----- - 1 | 5 - 3 | 5 - 3 | 5 - 4 | 5 - 5 | 5 - 5 | 5 - 6 | 5 - 6 | 5 - 1 | 4 - 2 | 4 - 3 | 4 - 3 | 4 - 3 | 4 - 4 | 4 - 4 | 4 - 5 | 4 - 5 | 4 - 1 | 3 - 2 | 3 - 2 | 3 - 2 | 3 - 6 | 3 - 2 | 2 - 4 | 2 - 4 | 2 - 4 | 2 - 6 | 2 - 1 | 1 - 3 | 1 - 5 | 1 - 6 | 1 - 5 | 0 -(32 rows) - --- window function operates on the results of --- a join -SELECT - us.user_id, - SUM(us.value_1) OVER (PARTITION BY us.user_id) -FROM - users_table us - JOIN - events_table ev - ON (us.user_id = ev.user_id) -GROUP BY - 1, - value_1 -ORDER BY - 1, - 2 -LIMIT 5; - user_id | sum ----------+----- - 1 | 13 - 1 | 13 - 1 | 13 - 1 | 13 - 2 | 10 -(5 rows) - --- the same query, but this time join with an alias -SELECT - user_id, value_1, SUM(j.value_1) OVER (PARTITION BY j.user_id) -FROM - (users_table us - JOIN - events_table ev - USING (user_id ) - ) j -GROUP BY - user_id, - value_1 -ORDER BY - 3 DESC, 2 DESC, 1 DESC -LIMIT 5; - user_id | value_1 | sum ----------+---------+----- - 5 | 5 | 15 - 4 | 5 | 15 - 3 | 5 | 15 - 5 | 4 | 15 - 4 | 4 | 15 -(5 rows) - --- querying views that have window functions should be ok -CREATE VIEW window_view AS -SELECT - DISTINCT user_id, rank() OVER (PARTITION BY user_id ORDER BY value_1) -FROM - users_table -GROUP BY - user_id, value_1 -HAVING count(*) > 1; --- Window function in View works -SELECT * -FROM - window_view -ORDER BY - 2 DESC, 1 -LIMIT 10; - user_id | rank ----------+------ - 5 | 6 - 2 | 5 - 4 | 5 - 5 | 5 - 2 | 4 - 3 | 4 - 4 | 4 - 5 | 4 - 6 | 4 - 2 | 3 -(10 rows) - --- the other way around also should work fine --- query a view using window functions -CREATE VIEW users_view AS SELECT * FROM users_table; -SELECT - DISTINCT user_id, rank() OVER (PARTITION BY user_id ORDER BY value_1) -FROM - users_view -GROUP BY - user_id, value_1 -HAVING count(*) > 4 -ORDER BY - 2 DESC, 1; - user_id | rank ----------+------ - 4 | 2 - 5 | 2 - 2 | 1 - 3 | 1 - 4 | 1 - 5 | 1 -(6 rows) - -DROP VIEW users_view, window_view; --- window function uses columns from two different tables -SELECT - DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM - events_table, users_table -WHERE - users_table.user_id = events_table.user_id -WINDOW - my_win AS (PARTITION BY events_table.user_id, users_table.value_1 ORDER BY events_table.time DESC) -ORDER BY - rnk DESC, 1 DESC -LIMIT 10; - user_id | rnk ----------+----- - 3 | 121 - 5 | 118 - 2 | 116 - 3 | 115 - 4 | 113 - 2 | 111 - 5 | 109 - 3 | 109 - 4 | 106 - 2 | 106 -(10 rows) - --- the same query with reference table column is also on the partition by clause -SELECT - DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM - events_table, users_ref_test_table uref -WHERE - uref.id = events_table.user_id -WINDOW - my_win AS (PARTITION BY events_table.user_id, uref.k_no ORDER BY events_table.time DESC) -ORDER BY - rnk DESC, 1 DESC -LIMIT 10; - user_id | rnk ----------+----- - 2 | 24 - 2 | 23 - 2 | 22 - 3 | 21 - 2 | 21 - 3 | 20 - 2 | 20 - 3 | 19 - 2 | 19 - 3 | 18 -(10 rows) - --- similar query with no distribution column is on the partition by clause --- is not supported -SELECT - DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM - events_table, users_ref_test_table uref -WHERE - uref.id = events_table.user_id -WINDOW - my_win AS (PARTITION BY events_table.value_2, uref.k_no ORDER BY events_table.time DESC) -ORDER BY - rnk DESC, 1 DESC -LIMIT 10; -ERROR: could not run distributed query because the window function that is used cannot be pushed down -HINT: Window functions are supported in two ways. Either add an equality filter on the distributed tables' partition column or use the window functions with a PARTITION BY clause containing the distribution column --- ORDER BY in the window function is an aggragate -SELECT - user_id, rank() OVER my_win as rnk, avg(value_2) as avg_val_2 -FROM - events_table -GROUP BY - user_id, date_trunc('day', time) -WINDOW - my_win AS (PARTITION BY user_id ORDER BY avg(event_type) DESC) -ORDER BY - 3 DESC, 2 DESC, 1 DESC; - user_id | rnk | avg_val_2 ----------+-----+-------------------- - 1 | 1 | 3.3750000000000000 - 3 | 2 | 3.1666666666666667 - 5 | 1 | 2.6666666666666667 - 6 | 1 | 2.5000000000000000 - 4 | 1 | 2.5000000000000000 - 2 | 1 | 2.4736842105263158 - 4 | 2 | 2.4000000000000000 - 1 | 2 | 2.1428571428571429 - 5 | 2 | 2.0909090909090909 - 6 | 2 | 2.0000000000000000 - 2 | 2 | 2.0000000000000000 - 3 | 1 | 1.8000000000000000 -(12 rows) - --- lets push the limits of writing complex expressions aling with the window functions -SELECT - COUNT(*) OVER (PARTITION BY user_id, user_id + 1), - rank() OVER (PARTITION BY user_id) as cnt1, - COUNT(*) OVER (PARTITION BY user_id, abs(value_1 - value_2)) as cnt2, - date_trunc('min', lag(time) OVER (PARTITION BY user_id ORDER BY time)) as datee, - rank() OVER my_win as rnnk, - avg(CASE - WHEN user_id > 4 - THEN value_1 - ELSE value_2 - END) FILTER (WHERE user_id > 2) OVER my_win_2 as filtered_count, - sum(user_id * (5.0 / (value_1 + value_2 + 0.1)) * value_3) FILTER (WHERE value_1::text LIKE '%1%') OVER my_win_4 as cnt_with_filter_2 -FROM - users_table -WINDOW - my_win AS (PARTITION BY user_id, (value_1%3)::int ORDER BY time DESC), - my_win_2 AS (PARTITION BY user_id, (value_1)::int ORDER BY time DESC), - my_win_3 AS (PARTITION BY user_id, date_trunc('min', time)), - my_win_4 AS (my_win_3 ORDER BY value_2, value_3) -ORDER BY - cnt_with_filter_2 DESC NULLS LAST, filtered_count DESC NULLS LAST, datee DESC NULLS LAST, rnnk DESC, cnt2 DESC, cnt1 DESC, user_id DESC -LIMIT 5; - count | cnt1 | cnt2 | datee | rnnk | filtered_count | cnt_with_filter_2 --------+------+------+--------------------------+------+------------------------+------------------- - 23 | 1 | 7 | Thu Nov 23 02:14:00 2017 | 6 | 0.00000000000000000000 | 72.7272727272727 - 10 | 1 | 3 | Wed Nov 22 23:01:00 2017 | 1 | 1.00000000000000000000 | 57.1428571428571 - 17 | 1 | 5 | Wed Nov 22 23:24:00 2017 | 8 | 3.0000000000000000 | 28.5714285714286 - 17 | 1 | 5 | | 10 | 2.6666666666666667 | 28.5714285714286 - 17 | 1 | 5 | Thu Nov 23 00:15:00 2017 | 7 | 3.6666666666666667 | 24.1935483870968 -(5 rows) - --- some tests with GROUP BY along with PARTITION BY -SELECT - user_id, - rank() OVER my_win as my_rank, - avg(avg(event_type)) OVER my_win_2 as avg, - max(time) as mx_time -FROM - events_table -GROUP BY - user_id, - value_2 -WINDOW - my_win AS (PARTITION BY user_id, max(event_type) ORDER BY count(*) DESC), - my_win_2 AS (PARTITION BY user_id, avg(user_id) ORDER BY count(*) DESC) -ORDER BY - avg DESC, - mx_time DESC, - my_rank DESC, - user_id DESC; - user_id | my_rank | avg | mx_time ----------+---------+------------------------+--------------------------------- - 6 | 1 | 3.0000000000000000 | Thu Nov 23 14:00:13.20013 2017 - 6 | 2 | 3.0000000000000000 | Thu Nov 23 11:16:13.106691 2017 - 6 | 1 | 3.0000000000000000 | Thu Nov 23 07:27:32.822068 2017 - 3 | 1 | 2.9857142857142857 | Thu Nov 23 16:31:56.219594 2017 - 4 | 2 | 2.9555555555555556 | Thu Nov 23 14:19:25.765876 2017 - 4 | 1 | 2.9555555555555556 | Thu Nov 23 08:36:53.871919 2017 - 1 | 4 | 2.8633333333333333 | Wed Nov 22 21:06:57.457147 2017 - 1 | 1 | 2.8250000000000000 | Thu Nov 23 21:54:46.924477 2017 - 2 | 2 | 2.7738095238095238 | Thu Nov 23 13:27:37.441959 2017 - 1 | 2 | 2.7722222222222222 | Thu Nov 23 09:23:30.994345 2017 - 3 | 1 | 2.7682539682539682 | Thu Nov 23 01:17:49.040685 2017 - 2 | 1 | 2.7142857142857143 | Thu Nov 23 15:58:49.273421 2017 - 1 | 3 | 2.5791666666666667 | Thu Nov 23 11:09:38.074595 2017 - 3 | 1 | 2.5714285714285714 | Thu Nov 23 16:44:41.903713 2017 - 2 | 1 | 2.5158730158730159 | Thu Nov 23 14:02:47.738901 2017 - 4 | 1 | 2.47777777777777778333 | Thu Nov 23 16:20:33.264457 2017 - 4 | 3 | 2.47777777777777778333 | Thu Nov 23 08:14:18.231273 2017 - 4 | 3 | 2.47777777777777778333 | Thu Nov 23 07:32:45.521278 2017 - 1 | 1 | 2.4000000000000000 | Thu Nov 23 10:23:27.617726 2017 - 2 | 1 | 2.3869047619047619 | Thu Nov 23 17:26:14.563216 2017 - 3 | 1 | 2.3841269841269841 | Thu Nov 23 18:08:26.550729 2017 - 3 | 1 | 2.3841269841269841 | Thu Nov 23 09:38:45.338008 2017 - 3 | 2 | 2.3841269841269841 | Thu Nov 23 06:44:50.887182 2017 - 2 | 2 | 2.3095238095238095 | Thu Nov 23 04:05:16.217731 2017 - 5 | 2 | 2.3000000000000000 | Thu Nov 23 14:28:51.833214 2017 - 5 | 2 | 2.3000000000000000 | Thu Nov 23 14:23:09.889786 2017 - 4 | 1 | 2.2000000000000000 | Thu Nov 23 18:10:21.338399 2017 - 2 | 1 | 2.09126984126984126667 | Thu Nov 23 03:35:04.321504 2017 - 5 | 1 | 2.0000000000000000 | Thu Nov 23 16:11:02.929469 2017 - 5 | 1 | 2.0000000000000000 | Thu Nov 23 14:40:40.467511 2017 - 5 | 1 | 2.0000000000000000 | Thu Nov 23 13:26:45.571108 2017 -(31 rows) - --- test for range and rows mode and different window functions --- mostly to make sure that deparsing works fine -SELECT - user_id, - rank() OVER (PARTITION BY user_id ROWS BETWEEN - UNBOUNDED PRECEDING AND CURRENT ROW), - dense_rank() OVER (PARTITION BY user_id RANGE BETWEEN - UNBOUNDED PRECEDING AND CURRENT ROW), - CUME_DIST() OVER (PARTITION BY user_id RANGE BETWEEN - UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), - PERCENT_RANK() OVER (PARTITION BY user_id ORDER BY avg(value_1) RANGE BETWEEN - UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) -FROM - users_table -GROUP BY - 1 -ORDER BY - 4 DESC,3 DESC,2 DESC ,1 DESC; - user_id | rank | dense_rank | cume_dist | percent_rank ----------+------+------------+-----------+-------------- - 6 | 1 | 1 | 1 | 0 - 5 | 1 | 1 | 1 | 0 - 4 | 1 | 1 | 1 | 0 - 3 | 1 | 1 | 1 | 0 - 2 | 1 | 1 | 1 | 0 - 1 | 1 | 1 | 1 | 0 -(6 rows) - --- test exclude supported -SELECT - user_id, - value_1, - array_agg(value_1) OVER (PARTITION BY user_id ORDER BY value_1 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), - array_agg(value_1) OVER (PARTITION BY user_id ORDER BY value_1 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) -FROM - users_table -WHERE - user_id > 2 AND user_id < 6 -ORDER BY - user_id, value_1, 3, 4; -ERROR: syntax error at or near "EXCLUDE" -LINE 5: ...ANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CU... - ^ --- test preceding and following on RANGE window -SELECT - user_id, - value_1, - array_agg(value_1) OVER range_window, - array_agg(value_1) OVER range_window_exclude -FROM - users_table -WHERE - user_id > 2 AND user_id < 6 -WINDOW - range_window as (PARTITION BY user_id ORDER BY value_1 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING), - range_window_exclude as (PARTITION BY user_id ORDER BY value_1 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) -ORDER BY - user_id, value_1, 3, 4; -ERROR: RANGE PRECEDING is only supported with UNBOUNDED -LINE 11: ..._window as (PARTITION BY user_id ORDER BY value_1 RANGE BETW... - ^ --- test preceding and following on ROW window -SELECT - user_id, - value_1, - array_agg(value_1) OVER row_window, - array_agg(value_1) OVER row_window_exclude -FROM - users_table -WHERE - user_id > 2 and user_id < 6 -WINDOW - row_window as (PARTITION BY user_id ORDER BY value_1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING), - row_window_exclude as (PARTITION BY user_id ORDER BY value_1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) -ORDER BY - user_id, value_1, 3, 4; -ERROR: syntax error at or near "EXCLUDE" -LINE 12: ...value_1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CU... - ^ --- some tests with GROUP BY, HAVING and LIMIT -SELECT - user_id, sum(event_type) OVER my_win , event_type -FROM - events_table -GROUP BY - user_id, event_type -HAVING count(*) > 2 - WINDOW my_win AS (PARTITION BY user_id, max(event_type) ORDER BY count(*) DESC) -ORDER BY - 2 DESC, 3 DESC, 1 DESC -LIMIT - 5; - user_id | sum | event_type ----------+-----+------------ - 4 | 4 | 4 - 3 | 4 | 4 - 2 | 4 | 4 - 1 | 4 | 4 - 5 | 3 | 3 -(5 rows) - --- Group by has more columns than partition by -SELECT - DISTINCT user_id, SUM(value_2) OVER (PARTITION BY user_id) -FROM - users_table -GROUP BY - user_id, value_1, value_2 -HAVING count(*) > 2 -ORDER BY - 2 DESC, 1 -LIMIT - 10; - user_id | sum ----------+----- - 5 | 3 - 4 | 2 -(2 rows) - -SELECT - DISTINCT ON (user_id) user_id, SUM(value_2) OVER (PARTITION BY user_id) -FROM - users_table -GROUP BY - user_id, value_1, value_2 -HAVING count(*) > 2 -ORDER BY - 1, 2 DESC -LIMIT - 10; - user_id | sum ----------+----- - 4 | 2 - 5 | 3 -(2 rows) - -SELECT - DISTINCT ON (SUM(value_1) OVER (PARTITION BY user_id)) user_id, SUM(value_2) OVER (PARTITION BY user_id) -FROM - users_table -GROUP BY - user_id, value_1, value_2 -HAVING count(*) > 2 -ORDER BY - (SUM(value_1) OVER (PARTITION BY user_id)) , 2 DESC, 1 -LIMIT - 10; - user_id | sum ----------+----- - 5 | 3 - 4 | 2 -(2 rows) - - --- not a meaningful query, with interesting syntax -SELECT - user_id, - AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), - AVG(avg(user_id)) OVER (PARTITION BY user_id, min(user_id), AVG(value_1)) -FROM - users_table -GROUP BY - 1 -ORDER BY - 3 DESC, 2 DESC, 1 DESC; - user_id | avg | avg ----------+--------------------+------------------------ - 6 | 2.1000000000000000 | 6.0000000000000000 - 5 | 2.6538461538461538 | 5.0000000000000000 - 4 | 2.7391304347826087 | 4.0000000000000000 - 3 | 2.3529411764705882 | 3.0000000000000000 - 2 | 2.3333333333333333 | 2.0000000000000000 - 1 | 3.2857142857142857 | 1.00000000000000000000 -(6 rows) - -SELECT coordinator_plan($Q$ -EXPLAIN (COSTS FALSE) -SELECT - user_id, - AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), - AVG(avg(user_id)) OVER (PARTITION BY user_id, min(user_id), AVG(value_1)) -FROM - users_table -GROUP BY - 1 -ORDER BY - 3 DESC, 2 DESC, 1 DESC; -$Q$); - coordinator_plan ------------------------------------------------------------------------------------- - Sort - Sort Key: remote_scan.avg_1 DESC, remote_scan.avg DESC, remote_scan.user_id DESC - -> HashAggregate - Group Key: remote_scan.user_id - -> Custom Scan (Citus Adaptive) - Task Count: 4 -(6 rows) - -SELECT - user_id, - 1 + sum(value_1), - 1 + AVG(value_2) OVER (partition by user_id) -FROM - users_table -GROUP BY - user_id, value_2 -ORDER BY - user_id, value_2; - user_id | ?column? | ?column? ----------+----------+-------------------- - 1 | 5 | 3.2500000000000000 - 1 | 4 | 3.2500000000000000 - 1 | 6 | 3.2500000000000000 - 1 | 12 | 3.2500000000000000 - 2 | 3 | 3.5000000000000000 - 2 | 5 | 3.5000000000000000 - 2 | 13 | 3.5000000000000000 - 2 | 6 | 3.5000000000000000 - 2 | 17 | 3.5000000000000000 - 2 | 4 | 3.5000000000000000 - 3 | 3 | 4.0000000000000000 - 3 | 13 | 4.0000000000000000 - 3 | 10 | 4.0000000000000000 - 3 | 2 | 4.0000000000000000 - 3 | 17 | 4.0000000000000000 - 4 | 4 | 3.5000000000000000 - 4 | 28 | 3.5000000000000000 - 4 | 1 | 3.5000000000000000 - 4 | 11 | 3.5000000000000000 - 4 | 17 | 3.5000000000000000 - 4 | 8 | 3.5000000000000000 - 5 | 7 | 3.5000000000000000 - 5 | 17 | 3.5000000000000000 - 5 | 24 | 3.5000000000000000 - 5 | 9 | 3.5000000000000000 - 5 | 8 | 3.5000000000000000 - 5 | 10 | 3.5000000000000000 - 6 | 6 | 3.0000000000000000 - 6 | 3 | 3.0000000000000000 - 6 | 9 | 3.0000000000000000 - 6 | 3 | 3.0000000000000000 - 6 | 5 | 3.0000000000000000 -(32 rows) - -SELECT - user_id, - 1 + sum(value_1), - 1 + AVG(value_2) OVER (partition by user_id) -FROM - users_table -GROUP BY - user_id, value_2 -ORDER BY - 2 DESC, 1 -LIMIT 5; - user_id | ?column? | ?column? ----------+----------+-------------------- - 4 | 28 | 3.5000000000000000 - 5 | 24 | 3.5000000000000000 - 2 | 17 | 3.5000000000000000 - 3 | 17 | 4.0000000000000000 - 4 | 17 | 3.5000000000000000 -(5 rows) - --- rank and ordering in the reverse order -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by value_2) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, value_2 DESC; - user_id | avg | rank ----------+------------------------+------ - 1 | 3.6666666666666667 | 4 - 1 | 2.5000000000000000 | 3 - 1 | 3.0000000000000000 | 2 - 1 | 4.0000000000000000 | 1 - 2 | 1.5000000000000000 | 6 - 2 | 3.2000000000000000 | 5 - 2 | 1.6666666666666667 | 4 - 2 | 3.0000000000000000 | 3 - 2 | 1.3333333333333333 | 2 - 2 | 2.0000000000000000 | 1 - 3 | 2.6666666666666667 | 5 - 3 | 1.00000000000000000000 | 4 - 3 | 3.0000000000000000 | 3 - 3 | 2.4000000000000000 | 2 - 3 | 1.00000000000000000000 | 1 - 4 | 3.5000000000000000 | 6 - 4 | 3.2000000000000000 | 5 - 4 | 3.3333333333333333 | 4 - 4 | 0.00000000000000000000 | 3 - 4 | 3.0000000000000000 | 2 - 4 | 1.00000000000000000000 | 1 - 5 | 3.0000000000000000 | 6 - 5 | 2.3333333333333333 | 5 - 5 | 1.6000000000000000 | 4 - 5 | 2.8750000000000000 | 3 - 5 | 3.2000000000000000 | 2 - 5 | 3.0000000000000000 | 1 - 6 | 1.3333333333333333 | 5 - 6 | 2.0000000000000000 | 4 - 6 | 4.0000000000000000 | 3 - 6 | 1.00000000000000000000 | 2 - 6 | 2.5000000000000000 | 1 -(32 rows) - --- order by in the window function is same as avg(value_1) DESC -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC; - user_id | avg | rank ----------+------------------------+------ - 1 | 4.0000000000000000 | 1 - 1 | 3.6666666666666667 | 2 - 1 | 3.0000000000000000 | 3 - 1 | 2.5000000000000000 | 4 - 2 | 3.2000000000000000 | 1 - 2 | 3.0000000000000000 | 2 - 2 | 2.0000000000000000 | 3 - 2 | 1.6666666666666667 | 4 - 2 | 1.5000000000000000 | 5 - 2 | 1.3333333333333333 | 6 - 3 | 3.0000000000000000 | 1 - 3 | 2.6666666666666667 | 2 - 3 | 2.4000000000000000 | 3 - 3 | 1.00000000000000000000 | 4 - 3 | 1.00000000000000000000 | 4 - 4 | 3.5000000000000000 | 1 - 4 | 3.3333333333333333 | 2 - 4 | 3.2000000000000000 | 3 - 4 | 3.0000000000000000 | 4 - 4 | 1.00000000000000000000 | 5 - 4 | 0.00000000000000000000 | 6 - 5 | 3.2000000000000000 | 1 - 5 | 3.0000000000000000 | 2 - 5 | 3.0000000000000000 | 2 - 5 | 2.8750000000000000 | 4 - 5 | 2.3333333333333333 | 5 - 5 | 1.6000000000000000 | 6 - 6 | 4.0000000000000000 | 1 - 6 | 2.5000000000000000 | 2 - 6 | 2.0000000000000000 | 3 - 6 | 1.3333333333333333 | 4 - 6 | 1.00000000000000000000 | 5 -(32 rows) - -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: remote_scan.user_id, (pg_catalog.sum(((pg_catalog.sum(remote_scan.avg) / pg_catalog.sum(remote_scan.avg_1)))) / pg_catalog.sum(remote_scan.rank)) DESC - -> HashAggregate - Group Key: remote_scan.user_id, remote_scan.worker_column_5 - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, (('1'::numeric / ('1'::numeric + avg(users_table.value_1)))) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(15 rows) - --- order by in the window function is same as avg(value_1) DESC -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC; - user_id | avg | rank ----------+------------------------+------ - 1 | 4.0000000000000000 | 1 - 1 | 3.6666666666666667 | 2 - 1 | 3.0000000000000000 | 3 - 1 | 2.5000000000000000 | 4 - 2 | 3.2000000000000000 | 1 - 2 | 3.0000000000000000 | 2 - 2 | 2.0000000000000000 | 3 - 2 | 1.6666666666666667 | 4 - 2 | 1.5000000000000000 | 5 - 2 | 1.3333333333333333 | 6 - 3 | 3.0000000000000000 | 1 - 3 | 2.6666666666666667 | 2 - 3 | 2.4000000000000000 | 3 - 3 | 1.00000000000000000000 | 4 - 3 | 1.00000000000000000000 | 4 - 4 | 3.5000000000000000 | 1 - 4 | 3.3333333333333333 | 2 - 4 | 3.2000000000000000 | 3 - 4 | 3.0000000000000000 | 4 - 4 | 1.00000000000000000000 | 5 - 4 | 0.00000000000000000000 | 6 - 5 | 3.2000000000000000 | 1 - 5 | 3.0000000000000000 | 2 - 5 | 3.0000000000000000 | 2 - 5 | 2.8750000000000000 | 4 - 5 | 2.3333333333333333 | 5 - 5 | 1.6000000000000000 | 6 - 6 | 4.0000000000000000 | 1 - 6 | 2.5000000000000000 | 2 - 6 | 2.0000000000000000 | 3 - 6 | 1.3333333333333333 | 4 - 6 | 1.00000000000000000000 | 5 -(32 rows) - --- limit is not pushed down to worker !! -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC -LIMIT 5; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.user_id, (pg_catalog.sum(((pg_catalog.sum(remote_scan.avg) / pg_catalog.sum(remote_scan.avg_1)))) / pg_catalog.sum(remote_scan.rank)) DESC - -> HashAggregate - Group Key: remote_scan.user_id, remote_scan.worker_column_5 - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, (('1'::numeric / ('1'::numeric + avg(users_table.value_1)))) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(16 rows) - -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC -LIMIT 5; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.user_id, (pg_catalog.sum(((pg_catalog.sum(remote_scan.avg) / pg_catalog.sum(remote_scan.avg_1)))) / pg_catalog.sum(remote_scan.rank)) DESC - -> HashAggregate - Group Key: remote_scan.user_id, remote_scan.worker_column_5 - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, (('1'::numeric / ('1'::numeric + avg(users_table.value_1)))) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(16 rows) - -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + sum(value_2))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC -LIMIT 5; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.user_id, (pg_catalog.sum(((pg_catalog.sum(remote_scan.avg) / pg_catalog.sum(remote_scan.avg_1)))) / pg_catalog.sum(remote_scan.rank)) DESC - -> HashAggregate - Group Key: remote_scan.user_id, remote_scan.worker_column_5 - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, ((1 / (1 + sum(users_table.value_2)))) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(16 rows) - -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by sum(value_2)) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC -LIMIT 5; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.user_id, (pg_catalog.sum(((pg_catalog.sum(remote_scan.avg) / pg_catalog.sum(remote_scan.avg_1)))) / pg_catalog.sum(remote_scan.rank)) DESC - -> HashAggregate - Group Key: remote_scan.user_id, remote_scan.worker_column_5 - -> Custom Scan (Citus Real-Time) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=57637 dbname=regression - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, (sum(users_table.value_2)) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(16 rows) - diff --git a/src/test/regress/sql/distributed_procedure.sql b/src/test/regress/sql/distributed_procedure.sql index 453eaeea9..51d52b533 100644 --- a/src/test/regress/sql/distributed_procedure.sql +++ b/src/test/regress/sql/distributed_procedure.sql @@ -1,11 +1,3 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above - \gset -\if :server_verion_eleven_and_above -\else -\q -\endif - SET citus.next_shard_id TO 20030000; CREATE USER procedureuser; diff --git a/src/test/regress/sql/failure_vacuum.sql b/src/test/regress/sql/failure_vacuum.sql index 0f929eef1..52cf39f6a 100644 --- a/src/test/regress/sql/failure_vacuum.sql +++ b/src/test/regress/sql/failure_vacuum.sql @@ -2,10 +2,6 @@ -- we don't mark transactions with ANALYZE as critical anymore, and -- get WARNINGs instead of ERRORs. --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - SET citus.next_shard_id TO 12000000; SELECT citus.mitmproxy('conn.allow()'); @@ -30,7 +26,7 @@ ANALYZE vacuum_test; -- ANALYZE transactions being critical is an open question, see #2430 -- show that we marked as INVALID on COMMIT FAILURE -SELECT shardid, shardstate FROM pg_dist_shard_placement where shardstate != 1 AND +SELECT shardid, shardstate FROM pg_dist_shard_placement where shardstate != 1 AND shardid in ( SELECT shardid FROM pg_dist_shard WHERE logicalrelid = 'vacuum_test'::regclass); UPDATE pg_dist_shard_placement SET shardstate = 1 diff --git a/src/test/regress/sql/foreign_key_restriction_enforcement.sql b/src/test/regress/sql/foreign_key_restriction_enforcement.sql index 1adafed74..8e482c857 100644 --- a/src/test/regress/sql/foreign_key_restriction_enforcement.sql +++ b/src/test/regress/sql/foreign_key_restriction_enforcement.sql @@ -1,10 +1,8 @@ --- +-- -- Tests multiple commands in transactions where -- there is foreign key relation between reference -- tables and distributed tables -- -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; CREATE SCHEMA test_fkey_to_ref_in_tx; SET search_path TO 'test_fkey_to_ref_in_tx'; @@ -50,7 +48,7 @@ BEGIN; ROLLBACK; -- case 1.2: SELECT to a reference table is followed by a multiple router SELECTs to a distributed table -BEGIN; +BEGIN; SELECT count(*) FROM reference_table; SELECT count(*) FROM on_update_fkey_table WHERE id = 15; SELECT count(*) FROM on_update_fkey_table WHERE id = 16; @@ -58,7 +56,7 @@ BEGIN; SELECT count(*) FROM on_update_fkey_table WHERE id = 18; ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM transitive_reference_table; SELECT count(*) FROM on_update_fkey_table WHERE id = 15; SELECT count(*) FROM on_update_fkey_table WHERE id = 16; @@ -95,28 +93,28 @@ BEGIN; ROLLBACK; -- case 1.5: SELECT to a reference table is followed by a DDL that touches fkey column -BEGIN; +BEGIN; SELECT count(*) FROM reference_table; ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE bigint; ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM transitive_reference_table; ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE bigint; ROLLBACK; -- case 1.6: SELECT to a reference table is followed by an unrelated DDL -BEGIN; +BEGIN; SELECT count(*) FROM reference_table; ALTER TABLE on_update_fkey_table ADD COLUMN X INT; ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM transitive_reference_table; ALTER TABLE on_update_fkey_table ADD COLUMN X INT; ROLLBACK; --- case 1.7.1: SELECT to a reference table is followed by a DDL that is on +-- case 1.7.1: SELECT to a reference table is followed by a DDL that is on -- the foreign key column BEGIN; SELECT count(*) FROM reference_table; @@ -134,31 +132,31 @@ BEGIN; ALTER TABLE on_update_fkey_table DROP COLUMN value_1 CASCADE; ROLLBACK; --- case 1.7.2: SELECT to a reference table is followed by a DDL that is on +-- case 1.7.2: SELECT to a reference table is followed by a DDL that is on -- the foreign key column after a parallel query has been executed -BEGIN; +BEGIN; SELECT count(*) FROM unrelated_dist_table; SELECT count(*) FROM reference_table; ALTER TABLE on_update_fkey_table DROP COLUMN value_1 CASCADE; ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM unrelated_dist_table; SELECT count(*) FROM transitive_reference_table; ALTER TABLE on_update_fkey_table DROP COLUMN value_1 CASCADE; ROLLBACK; --- case 1.7.3: SELECT to a reference table is followed by a DDL that is not on +-- case 1.7.3: SELECT to a reference table is followed by a DDL that is not on -- the foreign key column, and a parallel query has already been executed -BEGIN; +BEGIN; SELECT count(*) FROM unrelated_dist_table; SELECT count(*) FROM reference_table; ALTER TABLE on_update_fkey_table ADD COLUMN X INT; ROLLBACK; -BEGIN; +BEGIN; SELECT count(*) FROM unrelated_dist_table; SELECT count(*) FROM transitive_reference_table; ALTER TABLE on_update_fkey_table ADD COLUMN X INT; @@ -629,7 +627,7 @@ ROLLBACK; -- an unrelated update followed by update on the reference table and update -- on the cascading distributed table -- note that the UPDATE on the reference table will try to set the execution --- mode to sequential, which will fail since there is an already opened +-- mode to sequential, which will fail since there is an already opened -- parallel connections BEGIN; UPDATE unrelated_dist_table SET value_1 = 15; @@ -653,7 +651,7 @@ ROLLBACK; -- already executed a parallel query BEGIN; CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); + SELECT create_reference_table('test_table_1'); CREATE TABLE tt4(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES tt4(id)); SELECT create_distributed_table('tt4', 'id'); @@ -671,7 +669,7 @@ ROLLBACK; BEGIN; SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); + SELECT create_reference_table('test_table_1'); CREATE TABLE tt4(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES tt4(id)); SELECT create_distributed_table('tt4', 'id'); @@ -688,7 +686,7 @@ ROLLBACK; -- parallel connection via create_distributed_table(), later -- adding foreign key to reference table fails BEGIN; - + CREATE TABLE test_table_1(id int PRIMARY KEY); SELECT create_reference_table('test_table_1'); @@ -704,7 +702,7 @@ COMMIT; -- same test with the above on sequential mode should work fine BEGIN; - + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; CREATE TABLE test_table_1(id int PRIMARY KEY); @@ -721,7 +719,7 @@ BEGIN; COMMIT; --- similar test with the above, but this time the order of +-- similar test with the above, but this time the order of -- create_distributed_table and create_reference_table is -- changed BEGIN; @@ -778,7 +776,7 @@ ROLLBACK; -- make sure that we cannot create hash distributed tables with -- foreign keys to reference tables when they have data in it BEGIN; - + CREATE TABLE test_table_1(id int PRIMARY KEY); INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; @@ -797,7 +795,7 @@ COMMIT; -- the same test with above in sequential mode would still not work -- since COPY cannot be executed in sequential mode BEGIN; - + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; CREATE TABLE test_table_1(id int PRIMARY KEY); @@ -808,16 +806,16 @@ BEGIN; SELECT create_reference_table('test_table_1'); SELECT create_distributed_table('test_table_2', 'id'); - + -- make sure that the output isn't too verbose SET LOCAL client_min_messages TO ERROR; DROP TABLE test_table_2, test_table_1; -COMMIT; +COMMIT; -- we should be able to execute and DML/DDL/SELECT after we've -- switched to sequential via create_distributed_table BEGIN; - + CREATE TABLE test_table_1(id int PRIMARY KEY); CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_table_1(id)); @@ -848,11 +846,11 @@ SELECT create_reference_table('reference_table'); CREATE TABLE distributed_table(id int PRIMARY KEY, value_1 int); SELECT create_distributed_table('distributed_table', 'id'); -ALTER TABLE - distributed_table -ADD CONSTRAINT - fkey_delete FOREIGN KEY(value_1) -REFERENCES +ALTER TABLE + distributed_table +ADD CONSTRAINT + fkey_delete FOREIGN KEY(value_1) +REFERENCES reference_table(id) ON DELETE CASCADE; INSERT INTO reference_table SELECT i FROM generate_series(0, 10) i; @@ -860,7 +858,7 @@ INSERT INTO distributed_table SELECT i, i % 10 FROM generate_series(0, 100) i; -- this query returns 100 rows in Postgres, but not in Citus -- see https://github.com/citusdata/citus_docs/issues/664 for the discussion -WITH t1 AS (DELETE FROM reference_table RETURNING id) +WITH t1 AS (DELETE FROM reference_table RETURNING id) DELETE FROM distributed_table USING t1 WHERE value_1 = t1.id RETURNING *; -- load some more data for one more test with real-time selects @@ -869,10 +867,10 @@ INSERT INTO distributed_table SELECT i, i % 10 FROM generate_series(0, 100) i; -- this query returns 100 rows in Postgres, but not in Citus -- see https://github.com/citusdata/citus_docs/issues/664 for the discussion -WITH t1 AS (DELETE FROM reference_table RETURNING id) +WITH t1 AS (DELETE FROM reference_table RETURNING id) SELECT count(*) FROM distributed_table, t1 WHERE value_1 = t1.id; --- this query should fail since we first to a parallel access to a distributed table +-- this query should fail since we first to a parallel access to a distributed table -- with t1, and then access to t2 WITH t1 AS (DELETE FROM distributed_table RETURNING id), t2 AS (DELETE FROM reference_table RETURNING id) @@ -887,7 +885,7 @@ WITH t1 AS (DELETE FROM distributed_table RETURNING id) -- finally, make sure that we can execute the same queries -- in the sequential mode BEGIN; - + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; WITH t1 AS (DELETE FROM distributed_table RETURNING id), @@ -896,7 +894,7 @@ BEGIN; ROLLBACK; BEGIN; - + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; WITH t1 AS (DELETE FROM distributed_table RETURNING id) diff --git a/src/test/regress/sql/foreign_key_to_reference_table.sql b/src/test/regress/sql/foreign_key_to_reference_table.sql index 79c35099d..039b45890 100644 --- a/src/test/regress/sql/foreign_key_to_reference_table.sql +++ b/src/test/regress/sql/foreign_key_to_reference_table.sql @@ -1,8 +1,6 @@ -- -- FOREIGN_KEY_TO_REFERENCE_TABLE -- -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; CREATE SCHEMA fkey_reference_table; SET search_path TO 'fkey_reference_table'; @@ -13,12 +11,12 @@ SET citus.next_placement_id TO 7000000; CREATE TYPE foreign_details AS (name text, relid text, refd_relid text); -CREATE VIEW table_fkeys_in_workers AS +CREATE VIEW table_fkeys_in_workers AS SELECT -(json_populate_record(NULL::foreign_details, - json_array_elements_text((run_command_on_workers( $$ +(json_populate_record(NULL::foreign_details, + json_array_elements_text((run_command_on_workers( $$ SELECT - COALESCE(json_agg(row_to_json(d)), '[]'::json) + COALESCE(json_agg(row_to_json(d)), '[]'::json) FROM ( SELECT @@ -175,7 +173,7 @@ COMMIT; SELECT * FROM table_fkeys_in_workers WHERE relid LIKE 'fkey_reference_table.%' AND refd_relid LIKE 'fkey_reference_table.%' ORDER BY 1,2,3; DROP TABLE referencing_table; --- foreign keys are supported either in between distributed tables including the +-- foreign keys are supported either in between distributed tables including the -- distribution column or from distributed tables to reference tables. CREATE TABLE referencing_table(id int, ref_id int); SELECT create_distributed_table('referencing_table', 'ref_id', 'append'); @@ -271,7 +269,7 @@ DROP TABLE referencing_table; DROP TABLE referenced_table; -- foreign key as composite key -CREATE TYPE fkey_reference_table.composite AS (key1 int, key2 int); +CREATE TYPE fkey_reference_table.composite AS (key1 int, key2 int); CREATE TABLE referenced_table(test_column composite, PRIMARY KEY(test_column)); CREATE TABLE referencing_table(id int, referencing_composite composite); @@ -289,7 +287,7 @@ DROP TABLE referenced_table CASCADE; DROP TABLE referencing_table CASCADE; -- In the following test, we'll use a SERIAL column as the referenced column --- in the foreign constraint. We'll first show that and insert on non-serial +-- in the foreign constraint. We'll first show that and insert on non-serial -- column successfully inserts into the serial and referenced column. -- Accordingly, the inserts into the referencing table which references to the -- serial column will be successful. @@ -309,9 +307,9 @@ DROP TABLE referenced_table CASCADE; DROP TABLE referencing_table CASCADE; -- In the following test, we'll use a SERIAL column as the referencing column --- in the foreign constraint. We'll first show that the values that exist +-- in the foreign constraint. We'll first show that the values that exist -- in the referenced tables are successfully generated by the serial column --- and inserted to the distributed table. However, if the values that are generated +-- and inserted to the distributed table. However, if the values that are generated -- by serial column do not exist on the referenced table, the query fails. CREATE TABLE referenced_table(test_column int PRIMARY KEY, test_column2 int); CREATE TABLE referencing_table(id int, ref_id SERIAL); @@ -329,10 +327,10 @@ DROP TABLE referenced_table CASCADE; DROP TABLE referencing_table CASCADE; -- In the following test, we'll use a SERIAL column as the referencing column --- and referenced columns in a foreign constraint. We'll first show that the --- the inserts into referenced column will successfully generate and insert +-- and referenced columns in a foreign constraint. We'll first show that the +-- the inserts into referenced column will successfully generate and insert -- data into serial column. Then, we will be successfully insert the same amount --- of data into referencing table. However, if the values that are generated +-- of data into referencing table. However, if the values that are generated -- by serial column do not exist on the referenced table, the query fails. CREATE TABLE referenced_table(test_column SERIAL PRIMARY KEY, test_column2 int); CREATE TABLE referencing_table(id int, ref_id SERIAL); @@ -364,7 +362,7 @@ INSERT INTO referencing_table SELECT x,(random()*1000)::int FROM generate_series DROP TABLE referenced_table CASCADE; DROP TABLE referencing_table CASCADE; --- In the following tests, we create a foreign constraint with +-- In the following tests, we create a foreign constraint with -- ON UPDATE CASCADE and see if it works properly with cascading upsert CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); CREATE TABLE referencing_table(id int, ref_id int DEFAULT -1); @@ -400,11 +398,11 @@ COMMIT; DROP TABLE referenced_table CASCADE; DROP TABLE referencing_table CASCADE; --- Chained references +-- Chained references -- In the following test, we create foreign keys from one column in a distributed -- table to two reference tables. We expect to see that even if a data exist in -- one reference table, it is not going to be inserted in to referencing table --- because of lack of the key in the other table. Data can only be inserted into +-- because of lack of the key in the other table. Data can only be inserted into -- referencing table if it exists in both referenced tables. -- Additionally, delete or update in one referenced table should cascade properly. CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); @@ -455,10 +453,10 @@ DROP TABLE referenced_table2 CASCADE; DROP TABLE referencing_table CASCADE; -- In the following test, we create foreign keys from two columns in a distributed --- table to two reference tables separately. We expect to see that even if a data +-- table to two reference tables separately. We expect to see that even if a data -- exist in one reference table for one column, it is not going to be inserted in --- to referencing table because the other constraint doesn't hold. Data can only --- be inserted into referencing table if both columns exist in respective columns +-- to referencing table because the other constraint doesn't hold. Data can only +-- be inserted into referencing table if both columns exist in respective columns -- in referenced tables. -- Additionally, delete or update in one referenced table should cascade properly. CREATE TABLE referenced_table(test_column int, test_column2 int, PRIMARY KEY(test_column)); @@ -517,8 +515,8 @@ DROP TABLE referencing_table CASCADE; -- two distributed tables are referencing to one reference table and --- in the same time the distributed table 2 is referencing to --- distributed table 1. Thus, we have a triangular +-- in the same time the distributed table 2 is referencing to +-- distributed table 1. Thus, we have a triangular -- distributed table 1 has a foreign key from the distribution column to reference table -- distributed table 2 has a foreign key from a non-distribution column to reference table -- distributed table 2 has a foreign key to distributed table 1 on the distribution column @@ -580,7 +578,7 @@ DROP TABLE referencing_table CASCADE; DROP TABLE referencing_table2 CASCADE; \set VERBOSITY default --- In this test we have a chained relationship in form of +-- In this test we have a chained relationship in form of -- distributed table (referencing_referencing_table) has a foreign key with two columns -- to another distributed table (referencing_table) -- referencing_table has another foreign key with 2 columns to referenced_table. @@ -607,11 +605,11 @@ DROP TABLE referencing_table CASCADE; DROP TABLE referencing_referencing_table; -- test if create_distributed_table works in transactions with some edge cases --- the following checks if create_distributed_table works on foreign keys when +-- the following checks if create_distributed_table works on foreign keys when -- one of them is a self-referencing table of multiple distributed tables BEGIN; CREATE TABLE test_table_1(id int PRIMARY KEY); - SELECT create_reference_table('test_table_1'); + SELECT create_reference_table('test_table_1'); CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES test_table_1(id)); SELECT create_distributed_table('test_table_2', 'id'); @@ -650,7 +648,7 @@ ROLLBACK; -- make sure that we fail if we need parallel data load BEGIN; - + CREATE TABLE test_table_1(id int PRIMARY KEY); INSERT INTO test_table_1 SELECT i FROM generate_series(0,100) i; @@ -821,7 +819,7 @@ SELECT create_reference_table('test_table_1'); SELECT create_distributed_table('test_table_2', 'id'); INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); TRUNCATE test_table_1 CASCADE; SELECT * FROM test_table_2; @@ -834,7 +832,7 @@ SELECT create_reference_table('test_table_1'); SELECT create_distributed_table('test_table_2', 'id'); INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); BEGIN; TRUNCATE test_table_1 CASCADE; @@ -865,7 +863,7 @@ SELECT create_reference_table('test_table_1'); SELECT create_distributed_table('test_table_2', 'id'); INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); TRUNCATE test_table_2 CASCADE; SELECT * FROM test_table_2; @@ -879,7 +877,7 @@ SELECT create_reference_table('test_table_1'); SELECT create_distributed_table('test_table_2', 'id'); INSERT INTO test_table_1 VALUES (1),(2),(3); -INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); +INSERT INTO test_table_2 VALUES (1,1),(2,2),(3,3); BEGIN; TRUNCATE test_table_2 CASCADE; COMMIT; @@ -889,7 +887,7 @@ SELECT * FROM test_table_1; DROP TABLE test_table_1, test_table_2; -- check if we successfuly set multi_shard_modify_mode to sequential after sequentially running DDLs --- in transaction since the upcoming DDLs need to run sequentially. +-- in transaction since the upcoming DDLs need to run sequentially. CREATE TABLE test_table_1(id int PRIMARY KEY); CREATE TABLE test_table_2(id int PRIMARY KEY, value_1 int); CREATE TABLE test_table_3(id int PRIMARY KEY, value_1 int); diff --git a/src/test/regress/sql/multi_create_table_new_features.sql b/src/test/regress/sql/multi_create_table_new_features.sql index d527496ad..fea2ca990 100644 --- a/src/test/regress/sql/multi_create_table_new_features.sql +++ b/src/test/regress/sql/multi_create_table_new_features.sql @@ -2,10 +2,6 @@ -- MULTI_CREATE_TABLE_NEW_FEATURES -- --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - -- Verify that the GENERATED ... AS IDENTITY feature in PostgreSQL 10 -- is forbidden in distributed tables. diff --git a/src/test/regress/sql/multi_deparse_procedure.sql b/src/test/regress/sql/multi_deparse_procedure.sql index 334b058cf..14b00f217 100644 --- a/src/test/regress/sql/multi_deparse_procedure.sql +++ b/src/test/regress/sql/multi_deparse_procedure.sql @@ -1,10 +1,3 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_and_above - \gset -\if :server_verion_eleven_and_above -\else -\q -\endif -- -- Regression tests for deparsing ALTER/DROP PROCEDURE Queries -- @@ -26,13 +19,13 @@ SELECT substring(:'server_version', '\d+')::int >= 11 AS server_verion_eleven_an -- SET configuration_parameter FROM CURRENT -- RESET configuration_parameter -- RESET ALL --- +-- -- DROP PROCEDURE [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...] -- [ CASCADE | RESTRICT ] --- +-- -- Please note that current deparser does not return errors on some invalid queries. --- --- For example CALLED ON NULL INPUT action is valid only for FUNCTIONS, but we still +-- +-- For example CALLED ON NULL INPUT action is valid only for FUNCTIONS, but we still -- allow deparsing them here. SET citus.next_shard_id TO 20030000; @@ -43,10 +36,6 @@ SET search_path TO procedure_tests; SET citus.shard_count TO 4; SET client_min_messages TO INFO; --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - CREATE FUNCTION deparse_test(text) RETURNS text AS 'citus' @@ -56,7 +45,7 @@ CREATE FUNCTION deparse_and_run_on_workers(text) RETURNS SETOF record AS $fnc$ WITH deparsed_query AS ( SELECT deparse_test($1) qualified_query ) - SELECT run_command_on_workers(qualified_query) FROM deparsed_query d + SELECT run_command_on_workers(qualified_query) FROM deparsed_query d $fnc$ LANGUAGE SQL; diff --git a/src/test/regress/sql/multi_explain.sql b/src/test/regress/sql/multi_explain.sql index da19ca718..a9ec11eab 100644 --- a/src/test/regress/sql/multi_explain.sql +++ b/src/test/regress/sql/multi_explain.sql @@ -4,10 +4,6 @@ SET citus.next_shard_id TO 570000; --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - \a\t RESET citus.task_executor_type; @@ -390,12 +386,12 @@ SELECT true AS valid FROM explain_xml($$ SELECT true AS valid FROM explain_json($$ SELECT avg(l_linenumber) FROM lineitem WHERE l_orderkey > 9030$$); - + -- Test multi shard update EXPLAIN (COSTS FALSE) UPDATE lineitem_hash_part SET l_suppkey = 12; - + EXPLAIN (COSTS FALSE) UPDATE lineitem_hash_part SET l_suppkey = 12 @@ -468,8 +464,8 @@ SELECT true AS valid FROM explain_xml($$ AND o_custkey = c_custkey AND l_suppkey = s_suppkey$$); --- make sure that EXPLAIN works without --- problems for queries that inlvolves only +-- make sure that EXPLAIN works without +-- problems for queries that inlvolves only -- reference tables SELECT true AS valid FROM explain_xml($$ SELECT count(*) diff --git a/src/test/regress/sql/multi_index_statements.sql b/src/test/regress/sql/multi_index_statements.sql index 9a7036353..e90e595f3 100644 --- a/src/test/regress/sql/multi_index_statements.sql +++ b/src/test/regress/sql/multi_index_statements.sql @@ -5,9 +5,6 @@ -- Check that we can run CREATE INDEX and DROP INDEX statements on distributed -- tables. -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS server_version_above_ten; - -- -- CREATE TEST TABLES -- diff --git a/src/test/regress/sql/multi_metadata_attributes.sql b/src/test/regress/sql/multi_metadata_attributes.sql index f34bd51de..7f7c12cad 100644 --- a/src/test/regress/sql/multi_metadata_attributes.sql +++ b/src/test/regress/sql/multi_metadata_attributes.sql @@ -1,7 +1,6 @@ -- if the output of following query changes, we might need to change --- some heap_getattr() calls to heap_deform_tuple(). This errors out in --- postgres versions before 11. +-- some heap_getattr() calls to heap_deform_tuple(). SELECT attrelid::regclass, attname, atthasmissing, attmissingval FROM pg_attribute WHERE atthasmissing diff --git a/src/test/regress/sql/multi_multiuser.sql b/src/test/regress/sql/multi_multiuser.sql index 59bbf5457..62aebb1e2 100644 --- a/src/test/regress/sql/multi_multiuser.sql +++ b/src/test/regress/sql/multi_multiuser.sql @@ -4,10 +4,6 @@ -- Test user permissions. -- --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - SET citus.next_shard_id TO 1420000; SET citus.shard_replication_factor TO 1; @@ -300,14 +296,14 @@ SELECT run_command_on_workers($$SELECT proowner::regrole FROM pg_proc WHERE pron SELECT wait_until_metadata_sync(); --- now, make sure that the user can use the function +-- now, make sure that the user can use the function -- created in the transaction BEGIN; CREATE FUNCTION usage_access_func_second(key int, variadic v int[]) RETURNS text LANGUAGE plpgsql AS 'begin return current_user; end;'; SELECT create_distributed_function('usage_access_func_second(int,int[])', '$1'); -SELECT usage_access_func_second(1, 2,3,4,5) FROM full_access_user_schema.t1 LIMIT 1; +SELECT usage_access_func_second(1, 2,3,4,5) FROM full_access_user_schema.t1 LIMIT 1; ROLLBACK; diff --git a/src/test/regress/sql/multi_partitioning.sql b/src/test/regress/sql/multi_partitioning.sql index 4641ca166..8054463e6 100644 --- a/src/test/regress/sql/multi_partitioning.sql +++ b/src/test/regress/sql/multi_partitioning.sql @@ -6,9 +6,6 @@ SET citus.next_shard_id TO 1660000; SET citus.shard_count TO 4; SET citus.shard_replication_factor TO 1; -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS server_version_above_ten; - -- -- Distributed Partitioned Table Creation Tests -- @@ -49,34 +46,34 @@ SELECT * FROM partitioning_test ORDER BY 1; SELECT * FROM partitioning_hash_test ORDER BY 1; -- see partitioned table and its partitions are distributed -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE +SELECT + logicalrelid +FROM + pg_dist_partition +WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2009', 'partitioning_test_2010') ORDER BY 1; -SELECT - logicalrelid, count(*) -FROM pg_dist_shard +SELECT + logicalrelid, count(*) +FROM pg_dist_shard WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2009', 'partitioning_test_2010') GROUP BY logicalrelid ORDER BY 1,2; -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE +SELECT + logicalrelid +FROM + pg_dist_partition +WHERE logicalrelid IN ('partitioning_hash_test', 'partitioning_hash_test_0', 'partitioning_hash_test_1') ORDER BY 1; -SELECT - logicalrelid, count(*) -FROM pg_dist_shard +SELECT + logicalrelid, count(*) +FROM pg_dist_shard WHERE logicalrelid IN ('partitioning_hash_test', 'partitioning_hash_test_0', 'partitioning_hash_test_1') GROUP BY logicalrelid @@ -87,17 +84,17 @@ ORDER BY CREATE TABLE partitioning_test_2011 PARTITION OF partitioning_test FOR VALUES FROM ('2011-01-01') TO ('2012-01-01'); -- new partition is automatically distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE +SELECT + logicalrelid +FROM + pg_dist_partition +WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2011') ORDER BY 1; -SELECT - logicalrelid, count(*) -FROM pg_dist_shard +SELECT + logicalrelid, count(*) +FROM pg_dist_shard WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2011') GROUP BY logicalrelid @@ -114,17 +111,17 @@ INSERT INTO partitioning_test_2012 VALUES (6, '2012-07-07'); ALTER TABLE partitioning_test ATTACH PARTITION partitioning_test_2012 FOR VALUES FROM ('2012-01-01') TO ('2013-01-01'); -- attached partition is distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE +SELECT + logicalrelid +FROM + pg_dist_partition +WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2012') ORDER BY 1; -SELECT - logicalrelid, count(*) -FROM pg_dist_shard +SELECT + logicalrelid, count(*) +FROM pg_dist_shard WHERE logicalrelid IN ('partitioning_test', 'partitioning_test_2012') GROUP BY logicalrelid @@ -391,7 +388,7 @@ SELECT FROM information_schema.table_constraints WHERE - table_name = 'partitioning_test_2009' AND + table_name = 'partitioning_test_2009' AND constraint_name = 'partitioning_2009_primary'; -- however, you can add primary key if it contains both distribution and partition key @@ -473,9 +470,9 @@ SELECT right(table_name, 7)::int as shardid, * FROM ( json_array_elements_text(result::json)::json )).* FROM run_command_on_workers($$ SELECT - COALESCE(json_agg(row_to_json(q)), '[]'::json) + COALESCE(json_agg(row_to_json(q)), '[]'::json) FROM ( - SELECT + SELECT table_name, constraint_name, constraint_type FROM information_schema.table_constraints WHERE @@ -639,40 +636,40 @@ SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType FROM (SELECT *, random() FROM - (SELECT + (SELECT "t"."user_id", "t"."time", unnest("t"."collected_events") AS "event_types" FROM - (SELECT + (SELECT "t1"."user_id", min("t1"."time") AS "time", array_agg(("t1"."event") ORDER BY TIME ASC, event DESC) AS collected_events FROM( - (SELECT + (SELECT "events"."user_id", "events"."time", 0 AS event - FROM + FROM partitioned_events_table as "events" - WHERE + WHERE event_type IN (1, 2) ) - UNION - (SELECT + UNION + (SELECT "events"."user_id", "events"."time", 1 AS event - FROM + FROM partitioned_events_table as "events" - WHERE + WHERE event_type IN (3, 4) ) - UNION - (SELECT + UNION + (SELECT "events"."user_id", "events"."time", 2 AS event - FROM + FROM partitioned_events_table as "events" - WHERE + WHERE event_type IN (5, 6) ) - UNION - (SELECT + UNION + (SELECT "events"."user_id", "events"."time", 3 AS event - FROM + FROM partitioned_events_table as "events" - WHERE + WHERE event_type IN (1, 6))) t1 - GROUP BY "t1"."user_id") AS t) "q" + GROUP BY "t1"."user_id") AS t) "q" ) AS final_query GROUP BY types ORDER BY types; @@ -680,73 +677,73 @@ ORDER BY types; -- UNION and JOIN on both partitioned and regular tables SELECT ("final_query"."event_types") as types, count(*) AS sumOfEventType FROM - (SELECT + (SELECT *, random() FROM - (SELECT + (SELECT "t"."user_id", "t"."time", unnest("t"."collected_events") AS "event_types" FROM - (SELECT + (SELECT "t1"."user_id", min("t1"."time") AS "time", array_agg(("t1"."event") ORDER BY TIME ASC, event DESC) AS collected_events FROM ( - (SELECT + (SELECT * FROM - (SELECT + (SELECT "events"."time", 0 AS event, "events"."user_id" - FROM + FROM partitioned_events_table as "events" - WHERE - event_type IN (1, 2)) events_subquery_1) - UNION + WHERE + event_type IN (1, 2)) events_subquery_1) + UNION (SELECT * FROM ( SELECT * FROM ( - SELECT + SELECT max("events"."time"), 0 AS event, "events"."user_id" - FROM + FROM events_table as "events", users_table as "users" - WHERE + WHERE events.user_id = users.user_id AND event_type IN (1, 2) GROUP BY "events"."user_id" ) as events_subquery_5 ) events_subquery_2) - UNION + UNION (SELECT * FROM - (SELECT + (SELECT "events"."time", 2 AS event, "events"."user_id" - FROM + FROM partitioned_events_table as "events" - WHERE + WHERE event_type IN (3, 4)) events_subquery_3) - UNION + UNION (SELECT * FROM (SELECT "events"."time", 3 AS event, "events"."user_id" - FROM + FROM events_table as "events" - WHERE + WHERE event_type IN (5, 6)) events_subquery_4) ) t1 - GROUP BY "t1"."user_id") AS t) "q" + GROUP BY "t1"."user_id") AS t) "q" INNER JOIN - (SELECT + (SELECT "users"."user_id" - FROM + FROM partitioned_users_table as "users" - WHERE - value_1 > 2 and value_1 < 5) AS t + WHERE + value_1 > 2 and value_1 < 5) AS t ON (t.user_id = q.user_id)) as final_query -GROUP BY +GROUP BY types -ORDER BY +ORDER BY types; -- test LIST partitioning @@ -778,37 +775,37 @@ WHERE SELECT count(*) AS cnt, "generated_group_field" FROM - (SELECT + (SELECT "eventQuery"."user_id", random(), generated_group_field FROM - (SELECT + (SELECT "multi_group_wrapper_1".*, generated_group_field, random() FROM (SELECT * FROM - (SELECT + (SELECT "list_partitioned_events_table"."time", "list_partitioned_events_table"."user_id" as event_user_id - FROM + FROM list_partitioned_events_table as "list_partitioned_events_table" - WHERE + WHERE user_id > 2) "temp_data_queries" INNER JOIN - (SELECT + (SELECT "users"."user_id" - FROM + FROM partitioned_users_table as "users" - WHERE - user_id > 2 and value_2 = 1) "user_filters_1" + WHERE + user_id > 2 and value_2 = 1) "user_filters_1" ON ("temp_data_queries".event_user_id = "user_filters_1".user_id)) AS "multi_group_wrapper_1" LEFT JOIN - (SELECT + (SELECT "users"."user_id" AS "user_id", value_2 AS "generated_group_field" - FROM + FROM partitioned_users_table as "users") "left_group_by_1" - ON ("left_group_by_1".user_id = "multi_group_wrapper_1".event_user_id)) "eventQuery") "pushedDownQuery" + ON ("left_group_by_1".user_id = "multi_group_wrapper_1".event_user_id)) "eventQuery") "pushedDownQuery" GROUP BY "generated_group_field" - ORDER BY + ORDER BY cnt DESC, generated_group_field ASC LIMIT 10; @@ -1051,18 +1048,18 @@ CREATE TABLE partitioning_schema."schema-test_2009"(id int, time date); ALTER TABLE partitioning_schema."schema-test" ATTACH PARTITION partitioning_schema."schema-test_2009" FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -- attached partition is distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE +SELECT + logicalrelid +FROM + pg_dist_partition +WHERE logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) ORDER BY 1; -SELECT - logicalrelid, count(*) +SELECT + logicalrelid, count(*) FROM - pg_dist_shard + pg_dist_shard WHERE logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) GROUP BY @@ -1078,18 +1075,18 @@ SELECT create_distributed_table('partitioning_schema."schema-test"', 'id'); CREATE TABLE partitioning_schema."schema-test_2009" PARTITION OF partitioning_schema."schema-test" FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -- newly created partition is distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE +SELECT + logicalrelid +FROM + pg_dist_partition +WHERE logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) ORDER BY 1; -SELECT - logicalrelid, count(*) +SELECT + logicalrelid, count(*) FROM - pg_dist_shard + pg_dist_shard WHERE logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) GROUP BY @@ -1106,18 +1103,18 @@ SELECT create_distributed_table('"schema-test"', 'id'); CREATE TABLE partitioning_schema."schema-test_2009" PARTITION OF "schema-test" FOR VALUES FROM ('2009-01-01') TO ('2010-01-01'); -- newly created partition is distributed as well -SELECT - logicalrelid -FROM - pg_dist_partition -WHERE +SELECT + logicalrelid +FROM + pg_dist_partition +WHERE logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) ORDER BY 1; -SELECT - logicalrelid, count(*) +SELECT + logicalrelid, count(*) FROM - pg_dist_shard + pg_dist_shard WHERE logicalrelid IN ('partitioning_schema."schema-test"'::regclass, 'partitioning_schema."schema-test_2009"'::regclass) GROUP BY diff --git a/src/test/regress/sql/multi_partitioning_utils.sql b/src/test/regress/sql/multi_partitioning_utils.sql index 6eea8acbc..56dd088f8 100644 --- a/src/test/regress/sql/multi_partitioning_utils.sql +++ b/src/test/regress/sql/multi_partitioning_utils.sql @@ -1,7 +1,3 @@ --- This test has different output per major version -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - -- =================================================================== -- create test functions -- =================================================================== @@ -120,8 +116,8 @@ CREATE TABLE date_partitioned_table_100 (id int, time date) PARTITION BY RANGE ( CREATE TABLE date_partition_2007_100 (id int, time date ); -- now create the partitioning hierarcy -SELECT worker_apply_inter_shard_ddl_command(referencing_shard:=100, referencing_schema_name:='public', - referenced_shard:=100, referenced_schema_name:='public', +SELECT worker_apply_inter_shard_ddl_command(referencing_shard:=100, referencing_schema_name:='public', + referenced_shard:=100, referenced_schema_name:='public', command:='ALTER TABLE date_partitioned_table ATTACH PARTITION date_partition_2007 FOR VALUES FROM (''2007-01-01'') TO (''2008-01-02'')' ); -- the hierarcy is successfully created @@ -131,14 +127,14 @@ SELECT worker_apply_inter_shard_ddl_command(referencing_shard:=100, referencing_ SELECT master_get_table_ddl_events('date_partition_2007_100'); -- now break the partitioning hierarcy -SELECT worker_apply_inter_shard_ddl_command(referencing_shard:=100, referencing_schema_name:='public', - referenced_shard:=100, referenced_schema_name:='public', +SELECT worker_apply_inter_shard_ddl_command(referencing_shard:=100, referencing_schema_name:='public', + referenced_shard:=100, referenced_schema_name:='public', command:='ALTER TABLE date_partitioned_table DETACH PARTITION date_partition_2007' ); -- the hierarcy is successfully broken \d+ date_partitioned_table_100 --- now lets have some more complex partitioning hierarcies with +-- now lets have some more complex partitioning hierarcies with -- tables on different schemas and constraints on the tables CREATE SCHEMA partition_parent_schema; @@ -256,14 +252,14 @@ CREATE TABLE capitals ( -- returns true since capitals inherits from cities SELECT table_inherits('capitals'); --- although date_partition_2006 inherits from its parent +-- although date_partition_2006 inherits from its parent -- returns false since the hierarcy is formed via partitioning SELECT table_inherits('date_partition_2006'); -- returns true since cities inherited by capitals SELECT table_inherited('cities'); --- although date_partitioned_table inherited by its partitions +-- although date_partitioned_table inherited by its partitions -- returns false since the hierarcy is formed via partitioning SELECT table_inherited('date_partitioned_table'); diff --git a/src/test/regress/sql/multi_repartition_join_planning.sql b/src/test/regress/sql/multi_repartition_join_planning.sql index b027d416d..82c74e56b 100644 --- a/src/test/regress/sql/multi_repartition_join_planning.sql +++ b/src/test/regress/sql/multi_repartition_join_planning.sql @@ -10,10 +10,6 @@ SET citus.next_shard_id TO 690000; SET citus.enable_unique_job_ids TO off; --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - BEGIN; SET client_min_messages TO DEBUG4; SET citus.task_executor_type TO 'task-tracker'; diff --git a/src/test/regress/sql/multi_repartition_join_task_assignment.sql b/src/test/regress/sql/multi_repartition_join_task_assignment.sql index 75cbd7913..6ace61865 100644 --- a/src/test/regress/sql/multi_repartition_join_task_assignment.sql +++ b/src/test/regress/sql/multi_repartition_join_task_assignment.sql @@ -9,11 +9,6 @@ SET citus.next_shard_id TO 710000; --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - - BEGIN; SET client_min_messages TO DEBUG3; SET citus.task_executor_type TO 'task-tracker'; diff --git a/src/test/regress/sql/multi_task_assignment_policy.sql b/src/test/regress/sql/multi_task_assignment_policy.sql index 95a57f204..03ead23c2 100644 --- a/src/test/regress/sql/multi_task_assignment_policy.sql +++ b/src/test/regress/sql/multi_task_assignment_policy.sql @@ -5,13 +5,9 @@ SET citus.next_shard_id TO 880000; --- print whether we're using version > 9 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 9 AS version_above_nine; - -- the function simply parses the results and returns 'shardId@worker' -- for all the explain task outputs -CREATE OR REPLACE FUNCTION parse_explain_output(in qry text, in table_name text, out r text) +CREATE OR REPLACE FUNCTION parse_explain_output(in qry text, in table_name text, out r text) RETURNS SETOF TEXT AS $$ DECLARE portOfTheTask text; @@ -128,10 +124,10 @@ RESET client_min_messages; -- Now, lets test round-robin policy --- round-robin policy relies on PostgreSQL's local transactionId, +-- round-robin policy relies on PostgreSQL's local transactionId, -- which might change and we don't have any control over it. --- the important thing that we look for is that round-robin policy --- should give the same output for executions in the same transaction +-- the important thing that we look for is that round-robin policy +-- should give the same output for executions in the same transaction -- and different output for executions that are not inside the -- same transaction. To ensure that, we define a helper function BEGIN; @@ -141,9 +137,9 @@ SET LOCAL citus.explain_distributed_queries TO on; CREATE TEMPORARY TABLE explain_outputs (value text); SET LOCAL citus.task_assignment_policy TO 'round-robin'; -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_reference_table;', 'task_assignment_reference_table'); -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_reference_table;', 'task_assignment_reference_table'); -- given that we're in the same transaction, the count should be 1 @@ -157,9 +153,9 @@ COMMIT; SET citus.task_assignment_policy TO 'round-robin'; SET citus.explain_distributed_queries TO ON; -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_reference_table;', 'task_assignment_reference_table'); -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_reference_table;', 'task_assignment_reference_table'); -- given that we're in the same transaction, the count should be 2 @@ -169,7 +165,7 @@ TRUNCATE explain_outputs; -- same test with a distributed table -- we keep this test because as of this commit, the code --- paths for reference tables and distributed tables are +-- paths for reference tables and distributed tables are -- not the same SET citus.shard_replication_factor TO 2; @@ -181,9 +177,9 @@ BEGIN; SET LOCAL citus.explain_distributed_queries TO on; SET LOCAL citus.task_assignment_policy TO 'round-robin'; -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_replicated_hash;', 'task_assignment_replicated_hash'); -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_replicated_hash;', 'task_assignment_replicated_hash'); -- given that we're in the same transaction, the count should be 1 @@ -197,9 +193,9 @@ COMMIT; SET citus.task_assignment_policy TO 'round-robin'; SET citus.explain_distributed_queries TO ON; -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_replicated_hash;', 'task_assignment_replicated_hash'); -INSERT INTO explain_outputs +INSERT INTO explain_outputs SELECT parse_explain_output('EXPLAIN SELECT count(*) FROM task_assignment_replicated_hash;', 'task_assignment_replicated_hash'); -- given that we're in the same transaction, the count should be 2 diff --git a/src/test/regress/sql/multi_utilities.sql b/src/test/regress/sql/multi_utilities.sql index f852fef9e..38cb1fc7b 100644 --- a/src/test/regress/sql/multi_utilities.sql +++ b/src/test/regress/sql/multi_utilities.sql @@ -1,8 +1,5 @@ SET citus.next_shard_id TO 990000; --- print server version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 as version_above_ten; -- =================================================================== -- test utility statement functionality diff --git a/src/test/regress/sql/multi_view.sql b/src/test/regress/sql/multi_view.sql index c324644a7..8c170684d 100644 --- a/src/test/regress/sql/multi_view.sql +++ b/src/test/regress/sql/multi_view.sql @@ -7,10 +7,6 @@ -- router queries, single row inserts, multi row inserts via insert -- into select, multi row insert via copy commands. --- print whether we're using version > 10 to make version-specific tests clear -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - SELECT count(*) FROM lineitem_hash_part; SELECT count(*) FROM orders_hash_part; @@ -154,10 +150,10 @@ SELECT * FROM lineitems_by_shipping_method ORDER BY 1,2 LIMIT 5; -- create a view with group by on partition column CREATE VIEW lineitems_by_orderkey AS - SELECT - l_orderkey, count(*) - FROM - lineitem_hash_part + SELECT + l_orderkey, count(*) + FROM + lineitem_hash_part GROUP BY 1; -- this should work since we're able to push down this query @@ -181,7 +177,7 @@ DROP VIEW priority_orders; CREATE VIEW recent_users AS SELECT user_id, max(time) as lastseen FROM users_table GROUP BY user_id - HAVING max(time) > '2017-11-23 16:20:33.264457'::timestamp order by 2 DESC; + HAVING max(time) > '2017-11-23 16:20:33.264457'::timestamp order by 2 DESC; SELECT * FROM recent_users ORDER BY 2 DESC, 1 DESC; -- create a view for recent_events @@ -194,16 +190,16 @@ SELECT count(*) FROM recent_events; -- count number of events of recent_users SELECT count(*) FROM recent_users ru JOIN events_table et ON (ru.user_id = et.user_id); -- count number of events of per recent users order by count -SELECT ru.user_id, count(*) - FROM recent_users ru +SELECT ru.user_id, count(*) + FROM recent_users ru JOIN events_table et ON (ru.user_id = et.user_id) GROUP BY ru.user_id ORDER BY 2 DESC, 1; -- the same query with a left join however, it would still generate the same result -SELECT ru.user_id, count(*) - FROM recent_users ru +SELECT ru.user_id, count(*) + FROM recent_users ru LEFT JOIN events_table et ON (ru.user_id = et.user_id) GROUP BY ru.user_id @@ -211,8 +207,8 @@ SELECT ru.user_id, count(*) -- query wrapped inside a subquery, it needs another top level order by SELECT * FROM - (SELECT ru.user_id, count(*) - FROM recent_users ru + (SELECT ru.user_id, count(*) + FROM recent_users ru JOIN events_table et ON (ru.user_id = et.user_id) GROUP BY ru.user_id @@ -222,8 +218,8 @@ ORDER BY 2 DESC, 1; -- non-partition key joins are supported inside subquery -- via pull-push execution SELECT * FROM - (SELECT ru.user_id, count(*) - FROM recent_users ru + (SELECT ru.user_id, count(*) + FROM recent_users ru JOIN events_table et ON (ru.user_id = et.event_type) GROUP BY ru.user_id @@ -238,7 +234,7 @@ SELECT ru.user_id FROM recent_users ru JOIN recent_events re USING(user_id) GROU -- recent_events who are not done by recent users SELECT count(*) FROM ( SELECT re.*, ru.user_id AS recent_user - FROM recent_events re LEFT JOIN recent_users ru USING(user_id)) reu + FROM recent_events re LEFT JOIN recent_users ru USING(user_id)) reu WHERE recent_user IS NULL; -- same query with anti-join @@ -289,7 +285,7 @@ SELECT et.user_id, et.time FROM events_table et WHERE et.user_id IN (SELECT user SELECT count(*) FROM events_table et WHERE et.user_id IN (SELECT user_id FROM recent_selected_users WHERE user_id = 1); -- union between views is supported through recursive planning -(SELECT user_id FROM recent_users) +(SELECT user_id FROM recent_users) UNION (SELECT user_id FROM selected_users) ORDER BY 1; @@ -297,7 +293,7 @@ ORDER BY 1; -- wrapping it inside a SELECT * works SELECT * FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION (SELECT user_id FROM selected_users) ) u WHERE user_id < 2 AND user_id > 0 @@ -306,7 +302,7 @@ SELECT * -- union all also works for views SELECT * FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION ALL (SELECT user_id FROM selected_users) ) u WHERE user_id < 2 AND user_id > 0 @@ -314,7 +310,7 @@ SELECT * SELECT count(*) FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION (SELECT user_id FROM selected_users) ) u WHERE user_id < 2 AND user_id > 0; @@ -322,7 +318,7 @@ SELECT count(*) -- UNION ALL between views is supported through recursive planning SELECT count(*) FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION ALL (SELECT user_id FROM selected_users) ) u WHERE user_id < 2 AND user_id > 0; @@ -333,7 +329,7 @@ SELECT count(*) (SELECT user_id FROM (SELECT user_id, max(time) as lastseen FROM users_table GROUP BY user_id HAVING max(time) > '2017-11-22 05:45:49.978738'::timestamp order by 2 DESC) aa - ) + ) UNION (SELECT user_id FROM (SELECT * FROM users_table WHERE value_1 >= 1 and value_1 < 3) bb) ) u WHERE user_id < 2 AND user_id > 0; @@ -343,7 +339,7 @@ SELECT count(*) (SELECT user_id FROM (SELECT user_id, max(time) as lastseen FROM users_table GROUP BY user_id HAVING max(time) > '2017-11-22 05:45:49.978738'::timestamp order by 2 DESC) aa - ) + ) UNION ALL (SELECT user_id FROM (SELECT * FROM users_table WHERE value_1 >= 1 and value_1 < 3) bb) ) u WHERE user_id < 2 AND user_id > 0; @@ -411,7 +407,7 @@ EXPLAIN (COSTS FALSE) SELECT user_id FROM recent_selected_users GROUP BY 1 ORDER EXPLAIN (COSTS FALSE) SELECT * FROM ( - (SELECT user_id FROM recent_users) + (SELECT user_id FROM recent_users) UNION (SELECT user_id FROM selected_users) ) u WHERE user_id < 4 AND user_id > 1 diff --git a/src/test/regress/sql/relation_access_tracking.sql b/src/test/regress/sql/relation_access_tracking.sql index 13cb5dc92..4ce2f6b4e 100644 --- a/src/test/regress/sql/relation_access_tracking.sql +++ b/src/test/regress/sql/relation_access_tracking.sql @@ -2,8 +2,6 @@ --- --- tests around access tracking within transaction blocks --- -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 10 AS version_ten_or_above; CREATE SCHEMA access_tracking; SET search_path TO 'access_tracking'; @@ -49,7 +47,7 @@ BEGIN RETURN 'not_accessed'; ELSIF relationShardAccess = 1 THEN RETURN 'reference_table_access'; - ELSE + ELSE RETURN 'parallel_access'; END IF; END; @@ -57,12 +55,12 @@ $$ LANGUAGE 'plpgsql' IMMUTABLE; -CREATE VIEW relation_acesses AS - SELECT table_name, +CREATE VIEW relation_acesses AS + SELECT table_name, relation_access_mode_to_text(table_name, relation_select_access_mode(table_name::regclass)) as select_access, relation_access_mode_to_text(table_name, relation_dml_access_mode(table_name::regclass)) as dml_access, relation_access_mode_to_text(table_name, relation_ddl_access_mode(table_name::regclass)) as ddl_access - FROM + FROM ((SELECT 'table_' || i as table_name FROM generate_series(1, 7) i) UNION (SELECT 'partitioning_test') UNION (SELECT 'partitioning_test_2009') UNION (SELECT 'partitioning_test_2010')) tables; SET citus.shard_replication_factor TO 1; @@ -102,7 +100,7 @@ COMMIT; SELECT count(*) FROM table_1; SELECT * FROM relation_acesses WHERE table_name = 'table_1'; --- a very simple test that first checks sequential +-- a very simple test that first checks sequential -- and parallel SELECTs,DMLs, and DDLs BEGIN; SELECT * FROM relation_acesses WHERE table_name = 'table_1'; @@ -143,12 +141,12 @@ ROLLBACK; -- a simple join touches single shard per table BEGIN; - SELECT - count(*) - FROM + SELECT + count(*) + FROM table_1, table_2, table_3, table_4, table_5 WHERE - table_1.key = table_2.key AND table_2.key = table_3.key AND + table_1.key = table_2.key AND table_2.key = table_3.key AND table_3.key = table_4.key AND table_4.key = table_5.key AND table_1.key = 1; @@ -157,9 +155,9 @@ ROLLBACK; -- a simple real-time join touches all shard per table BEGIN; - SELECT - count(*) - FROM + SELECT + count(*) + FROM table_1, table_2 WHERE table_1.key = table_2.key; @@ -172,9 +170,9 @@ ROLLBACK; BEGIN; SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; - SELECT - count(*) - FROM + SELECT + count(*) + FROM table_1, table_2 WHERE table_1.key = table_2.key; @@ -184,17 +182,17 @@ ROLLBACK; -- a simple subquery pushdown that touches all shards BEGIN; - SELECT - count(*) - FROM + SELECT + count(*) + FROM ( - SELECT + SELECT random() - FROM + FROM table_1, table_2, table_3, table_4, table_5 WHERE - table_1.key = table_2.key AND table_2.key = table_3.key AND + table_1.key = table_2.key AND table_2.key = table_3.key AND table_3.key = table_4.key AND table_4.key = table_5.key ) as foo; @@ -202,7 +200,7 @@ BEGIN; ROLLBACK; -- simple multi shard update both sequential and parallel modes --- note that in multi shard modify mode we always add select +-- note that in multi shard modify mode we always add select -- access for all the shards accessed. But, sequential mode is OK BEGIN; UPDATE table_1 SET value = 15; @@ -214,8 +212,8 @@ ROLLBACK; -- now UPDATE/DELETE with subselect pushdown BEGIN; - UPDATE - table_1 SET value = 15 + UPDATE + table_1 SET value = 15 WHERE key IN (SELECT key FROM table_2 JOIN table_3 USING (key) WHERE table_2.value = 15); SELECT * FROM relation_acesses WHERE table_name IN ('table_1', 'table_2', 'table_3') ORDER BY 1; ROLLBACK; @@ -239,22 +237,22 @@ BEGIN; INSERT INTO table_2 SELECT * FROM table_1 OFFSET 0; SELECT * FROM relation_acesses WHERE table_name IN ('table_1', 'table_2') ORDER BY 1; ROLLBACK; - + -- recursively planned SELECT BEGIN; - SELECT - count(*) - FROM + SELECT + count(*) + FROM ( - SELECT + SELECT random() - FROM + FROM table_1, table_2 WHERE - table_1.key = table_2.key + table_1.key = table_2.key OFFSET 0 ) as foo; @@ -264,35 +262,35 @@ ROLLBACK; -- recursively planned SELECT and coordinator INSERT .. SELECT BEGIN; INSERT INTO table_3 (key) - SELECT + SELECT * - FROM + FROM ( - SELECT + SELECT random() * 1000 - FROM + FROM table_1, table_2 WHERE - table_1.key = table_2.key + table_1.key = table_2.key OFFSET 0 ) as foo; SELECT * FROM relation_acesses WHERE table_name IN ('table_1', 'table_2', 'table_3') ORDER BY 1; ROLLBACK; --- recursively planned SELECT and coordinator INSERT .. SELECT +-- recursively planned SELECT and coordinator INSERT .. SELECT -- but modifies single shard, marked as sequential operation BEGIN; INSERT INTO table_3 (key) - SELECT + SELECT * - FROM + FROM ( - SELECT + SELECT random() * 1000 - FROM + FROM table_1, table_2 WHERE table_1.key = table_2.key @@ -307,16 +305,16 @@ ROLLBACK; BEGIN; DELETE FROM table_3 where key IN ( - SELECT + SELECT * - FROM + FROM ( - SELECT + SELECT table_1.key - FROM + FROM table_1, table_2 WHERE - table_1.key = table_2.key + table_1.key = table_2.key OFFSET 0 ) as foo ) AND value IN (SELECT key FROM table_4); @@ -358,7 +356,7 @@ BEGIN; UPDATE table_6 SET value = 15; SELECT * FROM relation_acesses WHERE table_name IN ('table_6'); - ALTER TABLE table_6 ADD COLUMN x INT; + ALTER TABLE table_6 ADD COLUMN x INT; SELECT * FROM relation_acesses WHERE table_name IN ('table_6'); ROLLBACK; @@ -515,15 +513,15 @@ BEGIN; SELECT * FROM relation_acesses WHERE table_name IN ('table_1', 'table_2') ORDER BY 1; ROLLBACK; --- CTEs with SELECT only should work fine +-- CTEs with SELECT only should work fine BEGIN; - + WITH cte AS (SELECT count(*) FROM table_1) SELECT * FROM cte; SELECT * FROM relation_acesses WHERE table_name IN ('table_1') ORDER BY 1; COMMIT; --- CTEs with SELECT only in sequential mode should work fine +-- CTEs with SELECT only in sequential mode should work fine BEGIN; SET LOCAL citus.multi_shard_modify_mode = 'sequential'; @@ -534,7 +532,7 @@ COMMIT; -- modifying CTEs should work fine with multi-row inserts, which are by default in sequential BEGIN; - + WITH cte_1 AS (INSERT INTO table_1 VALUES (1000,1000), (1001, 1001), (1002, 1002) RETURNING *) SELECT * FROM cte_1 ORDER BY 1; SELECT * FROM relation_acesses WHERE table_name IN ('table_1') ORDER BY 1; @@ -542,7 +540,7 @@ ROLLBACK; -- modifying CTEs should work fine with parallel mode BEGIN; - + WITH cte_1 AS (UPDATE table_1 SET value = 15 RETURNING *) SELECT count(*) FROM cte_1 ORDER BY 1; SELECT * FROM relation_acesses WHERE table_name IN ('table_1') ORDER BY 1; @@ -550,19 +548,19 @@ ROLLBACK; -- modifying CTEs should work fine with sequential mode BEGIN; - + WITH cte_1 AS (UPDATE table_1 SET value = 15 RETURNING *) SELECT count(*) FROM cte_1 ORDER BY 1; SELECT * FROM relation_acesses WHERE table_name IN ('table_1') ORDER BY 1; ROLLBACK; --- create distributed table with data loading +-- create distributed table with data loading -- should mark both parallel dml and parallel ddl DROP TABLE table_3; CREATE TABLE table_3 (key int, value int); INSERT INTO table_3 SELECT i, i FROM generate_series(0,100) i; BEGIN; - SELECT create_distributed_table('table_3', 'key'); + SELECT create_distributed_table('table_3', 'key'); SELECT * FROM relation_acesses WHERE table_name IN ('table_3') ORDER BY 1; COMMIT; diff --git a/src/test/regress/sql/replicated_partitioned_table.sql b/src/test/regress/sql/replicated_partitioned_table.sql index 44c787adf..f0573bb34 100644 --- a/src/test/regress/sql/replicated_partitioned_table.sql +++ b/src/test/regress/sql/replicated_partitioned_table.sql @@ -9,24 +9,19 @@ SET search_path TO partitioned_table_replicated; SET citus.shard_count TO 4; SET citus.shard_replication_factor TO 2; --- print major version number for version-specific tests -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int < 11 AS server_version_is_10; - - CREATE TABLE collections ( key bigint, ts timestamptz, collection_id integer, - value numeric + value numeric ) PARTITION BY LIST ( collection_id ); -CREATE TABLE collections_1 +CREATE TABLE collections_1 PARTITION OF collections (key, ts, collection_id, value) FOR VALUES IN ( 1 ); -CREATE TABLE collections_2 +CREATE TABLE collections_2 PARTITION OF collections (key, ts, collection_id, value) FOR VALUES IN ( 2 ); @@ -47,7 +42,7 @@ CREATE TABLE collections_3 PARTITION OF collections FOR VALUES IN ( 3 ); CREATE TABLE collections_4 AS SELECT * FROM collections LIMIT 0; -- load some data -INSERT INTO collections_4 SELECT i, '2009-01-01', 4, i FROM generate_series (0, 10) i; +INSERT INTO collections_4 SELECT i, '2009-01-01', 4, i FROM generate_series (0, 10) i; ALTER TABLE collections ATTACH PARTITION collections_4 FOR VALUES IN ( 4 ); @@ -56,16 +51,16 @@ CREATE TABLE collections_5 AS SELECT * FROM collections LIMIT 0; SELECT create_distributed_table('collections_5', 'key'); -- load some data -INSERT INTO collections_5 SELECT i, '2009-01-01', 5, i FROM generate_series (0, 10) i; +INSERT INTO collections_5 SELECT i, '2009-01-01', 5, i FROM generate_series (0, 10) i; ALTER TABLE collections ATTACH PARTITION collections_5 FOR VALUES IN ( 5 ); -- make sure that we've all the placements -SELECT +SELECT logicalrelid, count(*) as placement_count -FROM +FROM pg_dist_shard, pg_dist_shard_placement -WHERE - logicalrelid::text LIKE '%collections%' AND +WHERE + logicalrelid::text LIKE '%collections%' AND pg_dist_shard.shardid = pg_dist_shard_placement.shardid GROUP BY logicalrelid @@ -73,11 +68,11 @@ ORDER BY 1,2; -- and, make sure that all tables are colocated -SELECT - count(DISTINCT colocationid) -FROM - pg_dist_partition -WHERE +SELECT + count(DISTINCT colocationid) +FROM + pg_dist_partition +WHERE logicalrelid::text LIKE '%collections%'; -- make sure that any kind of modification is disallowed on partitions @@ -102,7 +97,7 @@ INSERT INTO collections_1 SELECT * FROM collections_1 OFFSET 0; COPY collections_1 FROM STDIN; \. --- DDLs are not allowed +-- DDLs are not allowed CREATE INDEX index_on_partition ON collections_1(key); -- EXPLAIN with modifications is not allowed as well @@ -113,7 +108,7 @@ TRUNCATE collections_1; TRUNCATE collections, collections_1; -- modifying CTEs are also not allowed -WITH collections_5_cte AS +WITH collections_5_cte AS ( DELETE FROM collections_5 RETURNING * ) @@ -124,10 +119,10 @@ CREATE TABLE fkey_test (key bigint PRIMARY KEY); SELECT create_distributed_table('fkey_test', 'key'); ALTER TABLE - collections_5 -ADD CONSTRAINT - fkey_delete FOREIGN KEY(key) -REFERENCES + collections_5 +ADD CONSTRAINT + fkey_delete FOREIGN KEY(key) +REFERENCES fkey_test(key) ON DELETE CASCADE; -- we should be able to attach and detach partitions @@ -161,7 +156,7 @@ INSERT INTO collections_agg SELECT collection_id, sum(key) FROM collections_1 GR -- create a table and create its distribution metadata CREATE TABLE customer_engagements (id integer, event_id int) PARTITION BY LIST ( event_id ); -CREATE TABLE customer_engagements_1 +CREATE TABLE customer_engagements_1 PARTITION OF customer_engagements FOR VALUES IN ( 1 ); @@ -189,7 +184,7 @@ INSERT INTO customer_engagements VALUES (2, 2); -- the following queries does the following: -- (i) create a new shard -- (ii) mark the second shard placements as unhealthy --- (iii) do basic checks i.e., only allow copy from healthy placement to unhealthy ones +-- (iii) do basic checks i.e., only allow copy from healthy placement to unhealthy ones -- (iv) do a successful master_copy_shard_placement from the first placement to the second -- (v) mark the first placement as unhealthy and execute a query that is routed to the second placement diff --git a/src/test/regress/sql/sql_procedure.sql b/src/test/regress/sql/sql_procedure.sql index 6c665181c..63b9eed80 100644 --- a/src/test/regress/sql/sql_procedure.sql +++ b/src/test/regress/sql/sql_procedure.sql @@ -3,10 +3,6 @@ -- -- Tests basic PROCEDURE functionality with SQL and PLPGSQL procedures. -- --- print whether we're using version > 10 to make version-specific tests clear - -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; SET citus.next_shard_id TO 100500; diff --git a/src/test/regress/sql/window_functions.sql b/src/test/regress/sql/window_functions.sql index 69f208d89..728abf2f2 100644 --- a/src/test/regress/sql/window_functions.sql +++ b/src/test/regress/sql/window_functions.sql @@ -2,9 +2,6 @@ -- test top level window functions that are pushdownable -- =================================================================== -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 10 AS version_above_ten; - -- a very simple window function with an aggregate and a window function -- distribution column is on the partition by clause SELECT @@ -35,9 +32,9 @@ FROM ( DISTINCT us.user_id, us.value_2, value_1, random() as r1 FROM users_table as us, events_table - WHERE + WHERE us.user_id = events_table.user_id AND event_type IN (1,2) - ORDER BY + ORDER BY user_id, value_2 ) s GROUP BY @@ -45,7 +42,7 @@ GROUP BY ORDER BY 2 DESC, 1; --- window function operates on the results of +-- window function operates on the results of -- a join SELECT us.user_id, @@ -71,7 +68,7 @@ FROM JOIN events_table ev USING (user_id ) - ) j + ) j GROUP BY user_id, value_1 @@ -114,42 +111,42 @@ ORDER BY DROP VIEW users_view, window_view; -- window function uses columns from two different tables -SELECT +SELECT DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM +FROM events_table, users_table -WHERE +WHERE users_table.user_id = events_table.user_id WINDOW my_win AS (PARTITION BY events_table.user_id, users_table.value_1 ORDER BY events_table.time DESC) -ORDER BY +ORDER BY rnk DESC, 1 DESC LIMIT 10; -- the same query with reference table column is also on the partition by clause -SELECT +SELECT DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM +FROM events_table, users_ref_test_table uref -WHERE +WHERE uref.id = events_table.user_id WINDOW my_win AS (PARTITION BY events_table.user_id, uref.k_no ORDER BY events_table.time DESC) -ORDER BY +ORDER BY rnk DESC, 1 DESC LIMIT 10; -- similar query with no distribution column is on the partition by clause -- is not supported -SELECT +SELECT DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM +FROM events_table, users_ref_test_table uref -WHERE +WHERE uref.id = events_table.user_id WINDOW my_win AS (PARTITION BY events_table.value_2, uref.k_no ORDER BY events_table.time DESC) -ORDER BY +ORDER BY rnk DESC, 1 DESC LIMIT 10; @@ -169,7 +166,7 @@ ORDER BY SELECT COUNT(*) OVER (PARTITION BY user_id, user_id + 1), rank() OVER (PARTITION BY user_id) as cnt1, - COUNT(*) OVER (PARTITION BY user_id, abs(value_1 - value_2)) as cnt2, + COUNT(*) OVER (PARTITION BY user_id, abs(value_1 - value_2)) as cnt2, date_trunc('min', lag(time) OVER (PARTITION BY user_id ORDER BY time)) as datee, rank() OVER my_win as rnnk, avg(CASE @@ -191,7 +188,7 @@ LIMIT 5; -- some tests with GROUP BY along with PARTITION BY SELECT - user_id, + user_id, rank() OVER my_win as my_rank, avg(avg(event_type)) OVER my_win_2 as avg, max(time) as mx_time @@ -274,17 +271,17 @@ ORDER BY user_id, value_1, 3, 4; -- some tests with GROUP BY, HAVING and LIMIT -SELECT +SELECT user_id, sum(event_type) OVER my_win , event_type FROM events_table GROUP BY user_id, event_type -HAVING count(*) > 2 +HAVING count(*) > 2 WINDOW my_win AS (PARTITION BY user_id, max(event_type) ORDER BY count(*) DESC) -ORDER BY +ORDER BY 2 DESC, 3 DESC, 1 DESC -LIMIT +LIMIT 5; -- Group by has more columns than partition by @@ -323,11 +320,11 @@ ORDER BY (SUM(value_1) OVER (PARTITION BY user_id)) , 2 DESC, 1 LIMIT 10; - + -- not a meaningful query, with interesting syntax SELECT - user_id, - AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), + user_id, + AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), AVG(avg(user_id)) OVER (PARTITION BY user_id, min(user_id), AVG(value_1)) FROM users_table @@ -339,8 +336,8 @@ ORDER BY SELECT coordinator_plan($Q$ EXPLAIN (COSTS FALSE) SELECT - user_id, - AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), + user_id, + AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), AVG(avg(user_id)) OVER (PARTITION BY user_id, min(user_id), AVG(value_1)) FROM users_table @@ -373,7 +370,7 @@ ORDER BY 2 DESC, 1 LIMIT 5; --- rank and ordering in the reverse order +-- rank and ordering in the reverse order SELECT user_id, avg(value_1),