diff --git a/.circleci/config.yml b/.circleci/config.yml index 3ff4cb826..7ec198b50 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -199,7 +199,7 @@ jobs: make -C src/test/regress \ check-citus-upgrade \ bindir=/usr/lib/postgresql/${PG_MAJOR}/bin \ - citus-pre-tar=/install-pg11-citus${citus_version}.tar \ + citus-pre-tar=/install-pg${PG_MAJOR}-citus${citus_version}.tar \ citus-post-tar=/home/circleci/project/install-$PG_MAJOR.tar; \ done; @@ -210,7 +210,7 @@ jobs: make -C src/test/regress \ check-citus-upgrade-mixed \ bindir=/usr/lib/postgresql/${PG_MAJOR}/bin \ - citus-pre-tar=/install-pg11-citus${citus_version}.tar \ + citus-pre-tar=/install-pg${PG_MAJOR}-citus${citus_version}.tar \ citus-post-tar=/home/circleci/project/install-$PG_MAJOR.tar; \ done; no_output_timeout: 2m @@ -426,10 +426,6 @@ workflows: ignore: - /release-[0-9]+\.[0-9]+.*/ # match with releaseX.Y.* - - build: - name: build-11 - pg_major: 11 - image_tag: '11.9' - build: name: build-12 pg_major: 12 @@ -442,56 +438,6 @@ workflows: - check-style - check-sql-snapshots - - test-citus: - name: 'test-11_check-multi' - pg_major: 11 - image_tag: '11.9' - make: check-multi - requires: [build-11] - - test-citus: - name: 'test-11_check-mx' - pg_major: 11 - image_tag: '11.9' - make: check-multi-mx - requires: [build-11] - - test-citus: - name: 'test-11_check-vanilla' - pg_major: 11 - image_tag: '11.9' - make: check-vanilla - requires: [build-11] - - test-citus: - name: 'test-11_check-isolation' - pg_major: 11 - image_tag: '11.9' - make: check-isolation - requires: [build-11] - - test-citus: - name: 'test-11_check-worker' - pg_major: 11 - image_tag: '11.9' - make: check-worker - requires: [build-11] - - test-citus: - name: 'test-11_check-operations' - pg_major: 11 - image_tag: '11.9' - make: check-operations - requires: [build-11] - - test-citus: - name: 'test-11_check-follower-cluster' - pg_major: 11 - image_tag: '11.9' - make: check-follower-cluster - requires: [build-11] - - test-citus: - name: 'test-11_check-failure' - pg_major: 11 - image: citus/failtester - image_tag: '11.9' - make: check-failure - requires: [build-11] - - test-citus: name: 'test-12_check-multi' pg_major: 12 @@ -628,13 +574,6 @@ workflows: make: check-failure requires: [build-13] - - test-pg-upgrade: - name: 'test-11-12_check-pg-upgrade' - old_pg_major: 11 - new_pg_major: 12 - image_tag: 11-12-13 - requires: [build-11,build-12] - - test-pg-upgrade: name: 'test-12-13_check-pg-upgrade' old_pg_major: 12 @@ -643,10 +582,10 @@ workflows: requires: [build-12,build-13] - test-citus-upgrade: - name: test-11_check-citus-upgrade - pg_major: 11 - image_tag: '11.9' - requires: [build-11] + name: test-12_check-citus-upgrade + pg_major: 12 + image_tag: '12.4' + requires: [build-12] - ch_benchmark: requires: [build-13] diff --git a/configure b/configure index f6a9f8d97..3bd60aacc 100755 --- a/configure +++ b/configure @@ -2556,7 +2556,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" != '11' -a "$version_num" != '12' -a "$version_num" != '13'; then +if test "$version_num" != '12' -a "$version_num" != '13'; 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 @@ -4533,16 +4533,9 @@ cat >>confdefs.h <<_ACEOF _ACEOF -if test "$version_num" != '11'; then - HAS_TABLEAM=yes - +HAS_TABLEAM=yes $as_echo "#define HAS_TABLEAM 1" >>confdefs.h -else - { $as_echo "$as_me:${as_lineno-$LINENO}: postgres version does not support table access methods" >&5 -$as_echo "$as_me: postgres version does not support table access methods" >&6;} -fi; - # Require lz4 & zstd only if we are compiling columnar if test "$HAS_TABLEAM" == 'yes'; then # diff --git a/configure.in b/configure.in index 2e769c920..4b2e1d6dc 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" != '11' -a "$version_num" != '12' -a "$version_num" != '13'; then +if test "$version_num" != '12' -a "$version_num" != '13'; then AC_MSG_ERROR([Citus is not compatible with the detected PostgreSQL version ${version_num}.]) else AC_MSG_NOTICE([building against PostgreSQL $version_num]) @@ -216,12 +216,8 @@ PGAC_ARG_REQ(with, reports-hostname, [HOSTNAME], AC_DEFINE_UNQUOTED(REPORTS_BASE_URL, "$REPORTS_BASE_URL", [Base URL for statistics collection and update checks]) -if test "$version_num" != '11'; then - HAS_TABLEAM=yes - AC_DEFINE([HAS_TABLEAM], 1, [Define to 1 to build with table access method support, pg12 and up]) -else - AC_MSG_NOTICE([postgres version does not support table access methods]) -fi; +HAS_TABLEAM=yes +AC_DEFINE([HAS_TABLEAM], 1, [Define to 1 to build with table access method support, pg12 and up]) # Require lz4 & zstd only if we are compiling columnar if test "$HAS_TABLEAM" == 'yes'; then diff --git a/src/backend/columnar/columnar_compression.c b/src/backend/columnar/columnar_compression.c index 2306aeda1..961048018 100644 --- a/src/backend/columnar/columnar_compression.c +++ b/src/backend/columnar/columnar_compression.c @@ -226,10 +226,8 @@ DecompressBuffer(StringInfo buffer, case COMPRESSION_PG_LZ: { - StringInfo decompressedBuffer = NULL; uint32 compressedDataSize = VARSIZE(buffer->data) - COLUMNAR_COMPRESS_HDRSZ; uint32 decompressedDataSize = COLUMNAR_COMPRESS_RAWSIZE(buffer->data); - int32 decompressedByteCount = 0; if (compressedDataSize + COLUMNAR_COMPRESS_HDRSZ != buffer->len) { @@ -240,17 +238,11 @@ DecompressBuffer(StringInfo buffer, char *decompressedData = palloc0(decompressedDataSize); - #if PG_VERSION_NUM >= 120000 - decompressedByteCount = pglz_decompress(COLUMNAR_COMPRESS_RAWDATA( - buffer->data), - compressedDataSize, decompressedData, - decompressedDataSize, true); - #else - decompressedByteCount = pglz_decompress(COLUMNAR_COMPRESS_RAWDATA( - buffer->data), - compressedDataSize, decompressedData, - decompressedDataSize); - #endif + int32 decompressedByteCount = pglz_decompress(COLUMNAR_COMPRESS_RAWDATA( + buffer->data), + compressedDataSize, + decompressedData, + decompressedDataSize, true); if (decompressedByteCount < 0) { @@ -258,7 +250,7 @@ DecompressBuffer(StringInfo buffer, errdetail("compressed data is corrupted"))); } - decompressedBuffer = palloc0(sizeof(StringInfoData)); + StringInfo decompressedBuffer = palloc0(sizeof(StringInfoData)); decompressedBuffer->data = decompressedData; decompressedBuffer->len = decompressedDataSize; decompressedBuffer->maxlen = decompressedDataSize; diff --git a/src/backend/columnar/columnar_debug.c b/src/backend/columnar/columnar_debug.c index 28efac0eb..babdce5c3 100644 --- a/src/backend/columnar/columnar_debug.c +++ b/src/backend/columnar/columnar_debug.c @@ -39,14 +39,9 @@ PG_FUNCTION_INFO_V1(column_store_memory_stats); Datum column_store_memory_stats(PG_FUNCTION_ARGS) { - TupleDesc tupleDescriptor = NULL; const int resultColumnCount = 3; -#if PG_VERSION_NUM >= PG_VERSION_12 - tupleDescriptor = CreateTemplateTupleDesc(resultColumnCount); -#else - tupleDescriptor = CreateTemplateTupleDesc(resultColumnCount, false); -#endif + TupleDesc tupleDescriptor = CreateTemplateTupleDesc(resultColumnCount); TupleDescInitEntry(tupleDescriptor, (AttrNumber) 1, "TopMemoryContext", INT8OID, -1, 0); diff --git a/src/backend/columnar/columnar_metadata.c b/src/backend/columnar/columnar_metadata.c index d7c253862..16979a358 100644 --- a/src/backend/columnar/columnar_metadata.c +++ b/src/backend/columnar/columnar_metadata.c @@ -1067,15 +1067,10 @@ InsertTupleAndEnforceConstraints(ModifyState *state, Datum *values, bool *nulls) TupleDesc tupleDescriptor = RelationGetDescr(state->rel); HeapTuple tuple = heap_form_tuple(tupleDescriptor, values, nulls); -#if PG_VERSION_NUM >= 120000 TupleTableSlot *slot = ExecInitExtraTupleSlot(state->estate, tupleDescriptor, &TTSOpsHeapTuple); ExecStoreHeapTuple(tuple, slot, false); -#else - TupleTableSlot *slot = ExecInitExtraTupleSlot(state->estate, tupleDescriptor); - ExecStoreTuple(tuple, slot, InvalidBuffer, false); -#endif /* use ExecSimpleRelationInsert to enforce constraints */ ExecSimpleRelationInsert(state->estate, slot); @@ -1127,20 +1122,16 @@ FinishModifyRelation(ModifyState *state) static EState * create_estate_for_relation(Relation rel) { - ResultRelInfo *resultRelInfo; - EState *estate = CreateExecutorState(); RangeTblEntry *rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = RelationGetRelid(rel); rte->relkind = rel->rd_rel->relkind; -#if PG_VERSION_NUM >= 120000 rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); -#endif - resultRelInfo = makeNode(ResultRelInfo); + ResultRelInfo *resultRelInfo = makeNode(ResultRelInfo); InitResultRelInfo(resultRelInfo, rel, 1, NULL, 0); estate->es_result_relations = resultRelInfo; @@ -1149,15 +1140,6 @@ create_estate_for_relation(Relation rel) estate->es_output_cid = GetCurrentCommandId(true); -#if PG_VERSION_NUM < 120000 - - /* Triggers might need a slot */ - if (resultRelInfo->ri_TrigDesc) - { - estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL); - } -#endif - /* Prepare to catch AFTER triggers. */ AfterTriggerBeginQuery(); diff --git a/src/backend/columnar/columnar_reader.c b/src/backend/columnar/columnar_reader.c index fc35fc7a7..0a5273c65 100644 --- a/src/backend/columnar/columnar_reader.c +++ b/src/backend/columnar/columnar_reader.c @@ -23,13 +23,10 @@ #include "commands/defrem.h" #include "distributed/listutils.h" #include "nodes/makefuncs.h" -#if PG_VERSION_NUM >= 120000 #include "nodes/nodeFuncs.h" #include "optimizer/optimizer.h" -#else #include "optimizer/clauses.h" -#include "optimizer/predtest.h" -#endif +#include "optimizer/restrictinfo.h" #include "storage/fd.h" #include "utils/guc.h" #include "utils/memutils.h" diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index c10b4dce2..afac591a8 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -435,11 +435,6 @@ AlterDistributedTable(TableConversionParameters *params) TableConversionReturn * AlterTableSetAccessMethod(TableConversionParameters *params) { -#if PG_VERSION_NUM < PG_VERSION_12 - ereport(ERROR, (errmsg("table access methods are not supported " - "for Postgres versions earlier than 12"))); -#endif - EnsureRelationExists(params->relationId); EnsureTableOwner(params->relationId); @@ -963,7 +958,6 @@ CreateTableConversion(TableConversionParameters *params) BuildDistributionKeyFromColumnName(relation, con->distributionColumn); con->originalAccessMethod = NULL; -#if PG_VERSION_NUM >= PG_VERSION_12 if (!PartitionedTable(con->relationId)) { HeapTuple amTuple = SearchSysCache1(AMOID, ObjectIdGetDatum( @@ -977,7 +971,6 @@ CreateTableConversion(TableConversionParameters *params) con->originalAccessMethod = NameStr(amForm->amname); ReleaseSysCache(amTuple); } -#endif con->colocatedTableList = NIL; @@ -1296,12 +1289,10 @@ GetNonGeneratedStoredColumnNameList(Oid relationId) continue; } -#if PG_VERSION_NUM >= 120000 if (currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED) { continue; } -#endif const char *quotedColumnName = quote_identifier(NameStr(currentColumn->attname)); nonStoredColumnNameList = lappend(nonStoredColumnNameList, diff --git a/src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c b/src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c index 472408548..079bc702b 100644 --- a/src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c +++ b/src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c @@ -14,9 +14,6 @@ #include "distributed/pg_version_constants.h" -#if (PG_VERSION_NUM < PG_VERSION_12) -#include "access/htup_details.h" -#endif #include "access/xact.h" #include "catalog/pg_constraint.h" #include "distributed/commands/utility_hook.h" diff --git a/src/backend/distributed/commands/collation.c b/src/backend/distributed/commands/collation.c index 602e1493a..98e8e7e85 100644 --- a/src/backend/distributed/commands/collation.c +++ b/src/backend/distributed/commands/collation.c @@ -50,35 +50,21 @@ static void EnsureSequentialModeForCollationDDL(void); static char * CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollationName) { - char *schemaName = NULL; StringInfoData collationNameDef; - const char *providerString = NULL; - HeapTuple heapTuple = NULL; - Form_pg_collation collationForm = NULL; - char collprovider; - const char *collcollate; - const char *collctype; - const char *collname; - Oid collnamespace; -#if PG_VERSION_NUM >= PG_VERSION_12 - bool collisdeterministic; -#endif - heapTuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationId)); + HeapTuple heapTuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationId)); if (!HeapTupleIsValid(heapTuple)) { elog(ERROR, "citus cache lookup failed for collation %u", collationId); } - collationForm = (Form_pg_collation) GETSTRUCT(heapTuple); - collprovider = collationForm->collprovider; - collcollate = NameStr(collationForm->collcollate); - collctype = NameStr(collationForm->collctype); - collnamespace = collationForm->collnamespace; - collname = NameStr(collationForm->collname); -#if PG_VERSION_NUM >= PG_VERSION_12 - collisdeterministic = collationForm->collisdeterministic; -#endif + Form_pg_collation collationForm = (Form_pg_collation) GETSTRUCT(heapTuple); + char collprovider = collationForm->collprovider; + const char *collcollate = NameStr(collationForm->collcollate); + const char *collctype = NameStr(collationForm->collctype); + Oid collnamespace = collationForm->collnamespace; + const char *collname = NameStr(collationForm->collname); + bool collisdeterministic = collationForm->collisdeterministic; if (collowner != NULL) { @@ -86,9 +72,9 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati } ReleaseSysCache(heapTuple); - schemaName = get_namespace_name(collnamespace); + char *schemaName = get_namespace_name(collnamespace); *quotedCollationName = quote_qualified_identifier(schemaName, collname); - providerString = + const char *providerString = collprovider == COLLPROVIDER_DEFAULT ? "default" : collprovider == COLLPROVIDER_ICU ? "icu" : collprovider == COLLPROVIDER_LIBC ? "libc" : NULL; @@ -117,12 +103,10 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati quote_literal_cstr(collctype)); } -#if PG_VERSION_NUM >= PG_VERSION_12 if (!collisdeterministic) { appendStringInfoString(&collationNameDef, ", deterministic = false"); } -#endif appendStringInfoChar(&collationNameDef, ')'); diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 744f08196..8d424a04f 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -29,9 +29,7 @@ #include "catalog/pg_extension.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" -#if PG_VERSION_NUM >= 12000 #include "catalog/pg_proc.h" -#endif #include "catalog/pg_trigger.h" #include "commands/defrem.h" #include "commands/extension.h" @@ -819,18 +817,6 @@ EnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn, ErrorIfTableIsACatalogTable(relation); -#if PG_VERSION_NUM < PG_VERSION_12 - - /* verify target relation does not use WITH (OIDS) PostgreSQL feature */ - if (relationDesc->tdhasoid) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot distribute relation: %s", relationName), - errdetail("Distributed relations must not specify the WITH " - "(OIDS) option in their definitions."))); - } -#endif - /* verify target relation does not use identity columns */ if (RelationUsesIdentityColumns(relationDesc)) { @@ -866,7 +852,6 @@ EnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn, "defined to use hash partitioning."))); } -#if PG_VERSION_NUM >= PG_VERSION_12 if (distributionColumn->varcollid != InvalidOid && !get_collation_isdeterministic(distributionColumn->varcollid)) { @@ -874,7 +859,6 @@ EnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn, errmsg("Hash distributed partition columns may not use " "a non deterministic collation"))); } -#endif } else if (distributionMethod == DISTRIBUTE_BY_RANGE) { @@ -1539,12 +1523,8 @@ DoCopyFromLocalTableIntoShards(Relation distributedRelation, EState *estate) { /* begin reading from local table */ -#if PG_VERSION_NUM >= PG_VERSION_12 TableScanDesc scan = table_beginscan(distributedRelation, GetActiveSnapshot(), 0, NULL); -#else - HeapScanDesc scan = heap_beginscan(distributedRelation, GetActiveSnapshot(), 0, NULL); -#endif MemoryContext oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); @@ -1593,11 +1573,7 @@ DoCopyFromLocalTableIntoShards(Relation distributedRelation, MemoryContextSwitchTo(oldContext); /* finish reading from the local table */ -#if PG_VERSION_NUM >= PG_VERSION_12 table_endscan(scan); -#else - heap_endscan(scan); -#endif } @@ -1615,10 +1591,8 @@ TupleDescColumnNameList(TupleDesc tupleDescriptor) Form_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex); char *columnName = NameStr(currentColumn->attname); - if (currentColumn->attisdropped -#if PG_VERSION_NUM >= PG_VERSION_12 - || currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED -#endif + if (currentColumn->attisdropped || + currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED ) { continue; @@ -1660,7 +1634,6 @@ static bool DistributionColumnUsesGeneratedStoredColumn(TupleDesc relationDesc, Var *distributionColumn) { -#if PG_VERSION_NUM >= PG_VERSION_12 Form_pg_attribute attributeForm = TupleDescAttr(relationDesc, distributionColumn->varattno - 1); @@ -1668,7 +1641,6 @@ DistributionColumnUsesGeneratedStoredColumn(TupleDesc relationDesc, { return true; } -#endif return false; } diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index dd456ec4d..5f4b94961 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -62,7 +62,7 @@ static DistributeObjectOps Any_AlterEnum = { .deparse = DeparseAlterEnumStmt, .qualify = QualifyAlterEnumStmt, .preprocess = PreprocessAlterEnumStmt, - .postprocess = PostprocessAlterEnumStmt, + .postprocess = NULL, .address = AlterEnumStmtObjectAddress, }; static DistributeObjectOps Any_AlterExtension = { diff --git a/src/backend/distributed/commands/foreign_constraint.c b/src/backend/distributed/commands/foreign_constraint.c index 2224ee59a..8fdc932ee 100644 --- a/src/backend/distributed/commands/foreign_constraint.c +++ b/src/backend/distributed/commands/foreign_constraint.c @@ -19,9 +19,7 @@ #include "access/xact.h" #include "catalog/namespace.h" #include "catalog/pg_constraint.h" -#if (PG_VERSION_NUM >= PG_VERSION_12) #include "access/genam.h" -#endif #include "catalog/pg_type.h" #include "distributed/colocation_utils.h" #include "distributed/commands.h" @@ -715,14 +713,9 @@ get_relation_constraint_oid_compat(HeapTuple heapTuple) { Assert(heapTuple != NULL); - Oid constraintOid = InvalidOid; -#if PG_VERSION_NUM >= PG_VERSION_12 Form_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple); - constraintOid = constraintForm->oid; -#else - constraintOid = HeapTupleGetOid(heapTuple); -#endif + Oid constraintOid = constraintForm->oid; return constraintOid; } @@ -1284,8 +1277,6 @@ GetForeignConstraintCommandsToReferenceTable(ShardInterval *shardInterval) static void UpdateConstraintIsValid(Oid constraintId, bool isValid) { - HeapTuple heapTuple = NULL; - SysScanDesc scanDescriptor; ScanKeyData scankey[1]; Relation pgConstraint = table_open(ConstraintRelationId, AccessShareLock); TupleDesc tupleDescriptor = RelationGetDescr(pgConstraint); @@ -1294,21 +1285,17 @@ UpdateConstraintIsValid(Oid constraintId, bool isValid) bool replace[Natts_pg_constraint]; ScanKeyInit(&scankey[0], -#if PG_VERSION_NUM >= 120000 Anum_pg_constraint_oid, -#else - ObjectIdAttributeNumber, -#endif BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(constraintId)); - scanDescriptor = systable_beginscan(pgConstraint, - ConstraintOidIndexId, - true, - NULL, - 1, - scankey); - heapTuple = systable_getnext(scanDescriptor); + SysScanDesc scanDescriptor = systable_beginscan(pgConstraint, + ConstraintOidIndexId, + true, + NULL, + 1, + scankey); + HeapTuple heapTuple = systable_getnext(scanDescriptor); if (!HeapTupleIsValid(heapTuple)) { elog(ERROR, "could not find tuple for constraint %u", constraintId); diff --git a/src/backend/distributed/commands/function.c b/src/backend/distributed/commands/function.c index 5ab19f88c..028882bcd 100644 --- a/src/backend/distributed/commands/function.c +++ b/src/backend/distributed/commands/function.c @@ -21,9 +21,7 @@ #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/genam.h" -#endif #include "access/htup_details.h" #include "access/xact.h" #include "catalog/pg_aggregate.h" @@ -736,9 +734,6 @@ static char * GetAggregateDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace) { StringInfoData buf = { 0 }; - HeapTuple aggtup = NULL; - Form_pg_aggregate agg = NULL; - int numargs = 0; int i = 0; Oid *argtypes = NULL; char **argnames = NULL; @@ -762,7 +757,6 @@ GetAggregateDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace) const char *name = NameStr(proc->proname); const char *nsp = get_namespace_name(proc->pronamespace); -#if PG_VERSION_NUM >= PG_VERSION_12 if (useCreateOrReplace) { appendStringInfo(&buf, "CREATE OR REPLACE AGGREGATE %s(", @@ -773,20 +767,16 @@ GetAggregateDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace) appendStringInfo(&buf, "CREATE AGGREGATE %s(", quote_qualified_identifier(nsp, name)); } -#else - appendStringInfo(&buf, "CREATE AGGREGATE %s(", - quote_qualified_identifier(nsp, name)); -#endif /* Parameters, borrows heavily from print_function_arguments in postgres */ - numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes); + int numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes); - aggtup = SearchSysCache1(AGGFNOID, funcOid); + HeapTuple aggtup = SearchSysCache1(AGGFNOID, funcOid); if (!HeapTupleIsValid(aggtup)) { elog(ERROR, "cache lookup failed for %d", funcOid); } - agg = (Form_pg_aggregate) GETSTRUCT(aggtup); + Form_pg_aggregate agg = (Form_pg_aggregate) GETSTRUCT(aggtup); if (AGGKIND_IS_ORDERED_SET(agg->aggkind)) { @@ -1066,12 +1056,6 @@ GetAggregateDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace) ReleaseSysCache(aggtup); ReleaseSysCache(proctup); -#if PG_VERSION_NUM < PG_VERSION_12 - if (useCreateOrReplace) - { - return WrapCreateOrReplace(buf.data); - } -#endif return buf.data; } diff --git a/src/backend/distributed/commands/index.c b/src/backend/distributed/commands/index.c index 8404dc0c1..1f66e512b 100644 --- a/src/backend/distributed/commands/index.c +++ b/src/backend/distributed/commands/index.c @@ -11,9 +11,7 @@ #include "postgres.h" #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/genam.h" -#endif #include "access/htup_details.h" #include "access/xact.h" #include "catalog/catalog.h" @@ -93,9 +91,7 @@ struct DropRelationCallbackState */ struct ReindexIndexCallbackState { -#if PG_VERSION_NUM >= PG_VERSION_12 bool concurrent; -#endif Oid locked_table_oid; }; @@ -544,13 +540,8 @@ PreprocessReindexStmt(Node *node, const char *reindexCommand, { Relation relation = NULL; Oid relationId = InvalidOid; - bool isCitusRelation = false; -#if PG_VERSION_NUM >= PG_VERSION_12 LOCKMODE lockmode = reindexStatement->concurrent ? ShareUpdateExclusiveLock : AccessExclusiveLock; -#else - LOCKMODE lockmode = AccessExclusiveLock; -#endif MemoryContext relationContext = NULL; Assert(reindexStatement->kind == REINDEX_OBJECT_INDEX || @@ -558,17 +549,14 @@ PreprocessReindexStmt(Node *node, const char *reindexCommand, if (reindexStatement->kind == REINDEX_OBJECT_INDEX) { - Oid indOid; struct ReindexIndexCallbackState state; -#if PG_VERSION_NUM >= PG_VERSION_12 state.concurrent = reindexStatement->concurrent; -#endif state.locked_table_oid = InvalidOid; - indOid = RangeVarGetRelidExtended(reindexStatement->relation, - lockmode, 0, - RangeVarCallbackForReindexIndex, - &state); + Oid indOid = RangeVarGetRelidExtended(reindexStatement->relation, + lockmode, 0, + RangeVarCallbackForReindexIndex, + &state); relation = index_open(indOid, NoLock); relationId = IndexGetRelation(indOid, false); } @@ -581,7 +569,7 @@ PreprocessReindexStmt(Node *node, const char *reindexCommand, relationId = RelationGetRelid(relation); } - isCitusRelation = IsCitusTable(relationId); + bool isCitusRelation = IsCitusTable(relationId); if (reindexStatement->relation->schemaname == NULL) { @@ -613,12 +601,8 @@ PreprocessReindexStmt(Node *node, const char *reindexCommand, { DDLJob *ddlJob = palloc0(sizeof(DDLJob)); ddlJob->targetRelationId = relationId; -#if PG_VERSION_NUM >= PG_VERSION_12 ddlJob->concurrentIndexCmd = reindexStatement->concurrent; ddlJob->startNewTransaction = reindexStatement->concurrent; -#else - ddlJob->concurrentIndexCmd = false; -#endif ddlJob->commandString = reindexCommand; ddlJob->taskList = CreateReindexTaskList(relationId, reindexStatement); @@ -1053,11 +1037,7 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, Oid relId, Oid oldRelI * non-concurrent case and table locks used by index_concurrently_*() for * concurrent case. */ -#if PG_VERSION_NUM >= PG_VERSION_12 table_lockmode = state->concurrent ? ShareUpdateExclusiveLock : ShareLock; -#else - table_lockmode = ShareLock; -#endif /* * If we previously locked some other index's heap, and the name we're diff --git a/src/backend/distributed/commands/multi_copy.c b/src/backend/distributed/commands/multi_copy.c index 8ba97f14a..4141bf5fd 100644 --- a/src/backend/distributed/commands/multi_copy.c +++ b/src/backend/distributed/commands/multi_copy.c @@ -423,21 +423,13 @@ CopyToExistingShards(CopyStmt *copyStatement, QueryCompletionCompat *completionT { Oid tableId = RangeVarGetRelid(copyStatement->relation, NoLock, false); - CitusCopyDestReceiver *copyDest = NULL; - DestReceiver *dest = NULL; - Relation copiedDistributedRelation = NULL; - Form_pg_class copiedDistributedRelationTuple = NULL; List *columnNameList = NIL; int partitionColumnIndex = INVALID_PARTITION_COLUMN_INDEX; - EState *executorState = NULL; - MemoryContext executorTupleContext = NULL; - ExprContext *executorExpressionContext = NULL; bool stopOnFailure = false; - CopyState copyState = NULL; uint64 processedRowCount = 0; ErrorContextCallback errorCallback; @@ -469,10 +461,8 @@ CopyToExistingShards(CopyStmt *copyStatement, QueryCompletionCompat *completionT Form_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex); char *columnName = NameStr(currentColumn->attname); - if (currentColumn->attisdropped -#if PG_VERSION_NUM >= PG_VERSION_12 - || currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED -#endif + if (currentColumn->attisdropped || + currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED ) { continue; @@ -481,9 +471,9 @@ CopyToExistingShards(CopyStmt *copyStatement, QueryCompletionCompat *completionT columnNameList = lappend(columnNameList, columnName); } - executorState = CreateExecutorState(); - executorTupleContext = GetPerTupleMemoryContext(executorState); - executorExpressionContext = GetPerTupleExprContext(executorState); + EState *executorState = CreateExecutorState(); + MemoryContext executorTupleContext = GetPerTupleMemoryContext(executorState); + ExprContext *executorExpressionContext = GetPerTupleExprContext(executorState); if (IsCitusTableType(tableId, REFERENCE_TABLE)) { @@ -491,9 +481,11 @@ CopyToExistingShards(CopyStmt *copyStatement, QueryCompletionCompat *completionT } /* set up the destination for the COPY */ - copyDest = CreateCitusCopyDestReceiver(tableId, columnNameList, partitionColumnIndex, - executorState, stopOnFailure, NULL); - dest = (DestReceiver *) copyDest; + CitusCopyDestReceiver *copyDest = CreateCitusCopyDestReceiver(tableId, columnNameList, + partitionColumnIndex, + executorState, + stopOnFailure, NULL); + DestReceiver *dest = (DestReceiver *) copyDest; dest->rStartup(dest, 0, tupleDescriptor); /* @@ -501,8 +493,9 @@ CopyToExistingShards(CopyStmt *copyStatement, QueryCompletionCompat *completionT * of BeginCopyFrom. However, we obviously should not do this in relcache * and therefore make a copy of the Relation. */ - copiedDistributedRelation = (Relation) palloc(sizeof(RelationData)); - copiedDistributedRelationTuple = (Form_pg_class) palloc(CLASS_TUPLE_SIZE); + Relation copiedDistributedRelation = (Relation) palloc(sizeof(RelationData)); + Form_pg_class copiedDistributedRelationTuple = + (Form_pg_class) palloc(CLASS_TUPLE_SIZE); /* * There is no need to deep copy everything. We will just deep copy of the fields @@ -527,13 +520,13 @@ CopyToExistingShards(CopyStmt *copyStatement, QueryCompletionCompat *completionT } /* initialize copy state to read from COPY data source */ - copyState = BeginCopyFrom(NULL, - copiedDistributedRelation, - copyStatement->filename, - copyStatement->is_program, - NULL, - copyStatement->attlist, - copyStatement->options); + CopyState copyState = BeginCopyFrom(NULL, + copiedDistributedRelation, + copyStatement->filename, + copyStatement->is_program, + NULL, + copyStatement->attlist, + copyStatement->options); /* set up callback to identify error line number */ errorCallback.callback = CopyFromErrorCallback; @@ -945,18 +938,15 @@ CanUseBinaryCopyFormat(TupleDesc tupleDescription) for (int columnIndex = 0; columnIndex < totalColumnCount; columnIndex++) { Form_pg_attribute currentColumn = TupleDescAttr(tupleDescription, columnIndex); - Oid typeId = InvalidOid; - if (currentColumn->attisdropped -#if PG_VERSION_NUM >= PG_VERSION_12 - || currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED -#endif + if (currentColumn->attisdropped || + currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED ) { continue; } - typeId = currentColumn->atttypid; + Oid typeId = currentColumn->atttypid; if (!CanUseBinaryCopyFormatForType(typeId)) { useBinaryCopyFormat = false; @@ -1417,10 +1407,8 @@ TypeArrayFromTupleDescriptor(TupleDesc tupleDescriptor) for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) { Form_pg_attribute attr = TupleDescAttr(tupleDescriptor, columnIndex); - if (attr->attisdropped -#if PG_VERSION_NUM >= PG_VERSION_12 - || attr->attgenerated == ATTRIBUTE_GENERATED_STORED -#endif + if (attr->attisdropped || + attr->attgenerated == ATTRIBUTE_GENERATED_STORED ) { typeArray[columnIndex] = InvalidOid; @@ -1589,10 +1577,8 @@ AppendCopyRowData(Datum *valueArray, bool *isNullArray, TupleDesc rowDescriptor, value = CoerceColumnValue(value, &columnCoercionPaths[columnIndex]); } - if (currentColumn->attisdropped -#if PG_VERSION_NUM >= PG_VERSION_12 - || currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED -#endif + if (currentColumn->attisdropped || + currentColumn->attgenerated == ATTRIBUTE_GENERATED_STORED ) { continue; @@ -1712,10 +1698,8 @@ AvailableColumnCount(TupleDesc tupleDescriptor) { Form_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex); - if (!currentColumn->attisdropped -#if PG_VERSION_NUM >= PG_VERSION_12 - && currentColumn->attgenerated != ATTRIBUTE_GENERATED_STORED -#endif + if (!currentColumn->attisdropped && + currentColumn->attgenerated != ATTRIBUTE_GENERATED_STORED ) { columnCount++; @@ -2993,13 +2977,11 @@ ProcessCopyStmt(CopyStmt *copyStatement, QueryCompletionCompat *completionTag, c { if (copyStatement->is_from) { -#if PG_VERSION_NUM >= PG_VERSION_12 if (copyStatement->whereClause) { ereport(ERROR, (errmsg( "Citus does not support COPY FROM with WHERE"))); } -#endif /* check permissions, we're bypassing postgres' normal checks */ CheckCopyPermissions(copyStatement); @@ -3054,10 +3036,8 @@ CitusCopySelect(CopyStmt *copyStatement) { Form_pg_attribute attr = &tupleDescriptor->attrs[i]; - if (attr->attisdropped -#if PG_VERSION_NUM >= PG_VERSION_12 - || attr->attgenerated -#endif + if (attr->attisdropped || + attr->attgenerated ) { continue; @@ -3312,10 +3292,8 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) { if (TupleDescAttr(tupDesc, i)->attisdropped) continue; -#if PG_VERSION_NUM >= PG_VERSION_12 if (TupleDescAttr(tupDesc, i)->attgenerated) continue; -#endif attnums = lappend_int(attnums, i + 1); } } @@ -3340,14 +3318,12 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) continue; if (namestrcmp(&(att->attname), name) == 0) { -#if PG_VERSION_NUM >= PG_VERSION_12 if (att->attgenerated) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("column \"%s\" is a generated column", name), errdetail("Generated columns cannot be used in COPY."))); -#endif attnum = att->attnum; break; } diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index 9c1cff062..0723888c0 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -14,9 +14,7 @@ #include "access/heapam.h" #include "access/htup_details.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/table.h" -#endif #include "catalog/catalog.h" #include "catalog/pg_auth_members.h" #include "catalog/pg_authid.h" @@ -543,11 +541,7 @@ GenerateAlterRoleSetCommandForRole(Oid roleid) List *commands = NIL; -#if PG_VERSION_NUM >= PG_VERSION_12 TableScanDesc scan = table_beginscan_catalog(DbRoleSetting, 0, NULL); -#else - HeapScanDesc scan = heap_beginscan_catalog(DbRoleSetting, 0, NULL); -#endif while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { diff --git a/src/backend/distributed/commands/sequence.c b/src/backend/distributed/commands/sequence.c index 64ae4e246..7a509aa19 100644 --- a/src/backend/distributed/commands/sequence.c +++ b/src/backend/distributed/commands/sequence.c @@ -178,13 +178,11 @@ ExtractDefaultColumnsAndOwnedSequences(Oid relationId, List **columnNameList, continue; } -#if PG_VERSION_NUM >= PG_VERSION_12 if (attributeForm->attgenerated == ATTRIBUTE_GENERATED_STORED) { /* skip columns with GENERATED AS ALWAYS expressions */ continue; } -#endif char *columnName = NameStr(attributeForm->attname); *columnNameList = lappend(*columnNameList, columnName); diff --git a/src/backend/distributed/commands/statistics.c b/src/backend/distributed/commands/statistics.c index f7c65167f..3eed687d9 100644 --- a/src/backend/distributed/commands/statistics.c +++ b/src/backend/distributed/commands/statistics.c @@ -594,14 +594,9 @@ GetExplicitStatisticsIdList(Oid relationId) HeapTuple heapTuple = systable_getnext(scanDescriptor); while (HeapTupleIsValid(heapTuple)) { - Oid statisticsId = InvalidOid; -#if PG_VERSION_NUM >= PG_VERSION_12 FormData_pg_statistic_ext *statisticsForm = (FormData_pg_statistic_ext *) GETSTRUCT(heapTuple); - statisticsId = statisticsForm->oid; -#else - statisticsId = HeapTupleGetOid(heapTuple); -#endif + Oid statisticsId = statisticsForm->oid; statisticsIdList = lappend_oid(statisticsIdList, statisticsId); heapTuple = systable_getnext(scanDescriptor); diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 1760adb1d..a0f5cb09b 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -10,9 +10,7 @@ #include "postgres.h" #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/genam.h" -#endif #include "access/htup_details.h" #include "access/xact.h" #include "catalog/index.h" diff --git a/src/backend/distributed/commands/trigger.c b/src/backend/distributed/commands/trigger.c index d965986a3..d4d08e753 100644 --- a/src/backend/distributed/commands/trigger.c +++ b/src/backend/distributed/commands/trigger.c @@ -12,13 +12,7 @@ #include "distributed/pg_version_constants.h" #include "access/genam.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/table.h" -#else -#include "access/heapam.h" -#include "access/htup_details.h" -#include "access/sysattr.h" -#endif #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_trigger.h" @@ -101,11 +95,7 @@ GetTriggerTupleById(Oid triggerId, bool missingOk) int scanKeyCount = 1; ScanKeyData scanKey[1]; -#if PG_VERSION_NUM >= PG_VERSION_12 AttrNumber attrNumber = Anum_pg_trigger_oid; -#else - AttrNumber attrNumber = ObjectIdAttributeNumber; -#endif ScanKeyInit(&scanKey[0], attrNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(triggerId)); @@ -198,14 +188,9 @@ get_relation_trigger_oid_compat(HeapTuple heapTuple) { Assert(HeapTupleIsValid(heapTuple)); - Oid triggerOid = InvalidOid; -#if PG_VERSION_NUM >= PG_VERSION_12 Form_pg_trigger triggerForm = (Form_pg_trigger) GETSTRUCT(heapTuple); - triggerOid = triggerForm->oid; -#else - triggerOid = HeapTupleGetOid(heapTuple); -#endif + Oid triggerOid = triggerForm->oid; return triggerOid; } diff --git a/src/backend/distributed/commands/type.c b/src/backend/distributed/commands/type.c index dbcbcdab1..c170d2129 100644 --- a/src/backend/distributed/commands/type.c +++ b/src/backend/distributed/commands/type.c @@ -322,8 +322,6 @@ List * PreprocessAlterEnumStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext) { - List *commands = NIL; - ObjectAddress typeAddress = GetObjectAddressFromParseTree(node, false); if (!ShouldPropagateObject(&typeAddress)) { @@ -352,98 +350,15 @@ PreprocessAlterEnumStmt(Node *node, const char *queryString, * creating a DDLTaksList we won't return anything here. During the processing phase * we directly connect to workers and execute the commands remotely. */ -#if PG_VERSION_NUM < PG_VERSION_12 - if (AlterEnumIsAddValue(castNode(AlterEnumStmt, node))) - { - /* - * a plan cannot be made as it will be committed via 2PC when ran through the - * executor, instead we directly distributed during processing phase - */ - return NIL; - } -#endif - commands = list_make3(DISABLE_DDL_PROPAGATION, - (void *) alterEnumStmtSql, - ENABLE_DDL_PROPAGATION); + List *commands = list_make3(DISABLE_DDL_PROPAGATION, + (void *) alterEnumStmtSql, + ENABLE_DDL_PROPAGATION); return NodeDDLTaskList(NON_COORDINATOR_NODES, commands); } -/* - * PostprocessAlterEnumStmt is called after the AlterEnumStmt has been applied locally. - * - * This function is used for ALTER ENUM ... ADD VALUE for postgres versions lower than 12 - * to distribute the call. Before pg12 these statements could not be called in a - * transaction. If we would plan the distirbution of these statements the same as we do - * with the other statements they would get executed in a transaction to perform 2PC, that - * would error out. - * - * If it would error on some workers we provide a warning to the user that the statement - * failed to distributed with some detail on what to call after the cluster has been - * repaired. - * - * For pg12 the statements can be called in a transaction but will only become visible - * when the transaction commits. This is behaviour that is ok to perform in a 2PC. - */ -List * -PostprocessAlterEnumStmt(Node *node, const char *queryString) -{ - /* - * Before pg12 ALTER ENUM ... ADD VALUE could not be within a xact block. Normally we - * would propagate the statements in a xact block to perform 2pc on changes via ddl. - * Instead we need to connect directly to the workers here and execute the command. - * - * From pg12 and up we use the normal infrastructure and create the ddl jobs during - * planning. - */ -#if PG_VERSION_NUM < PG_VERSION_12 - AlterEnumStmt *stmt = castNode(AlterEnumStmt, node); - ObjectAddress typeAddress = GetObjectAddressFromParseTree((Node *) stmt, false); - if (!ShouldPropagateObject(&typeAddress)) - { - return NIL; - } - - if (AlterEnumIsAddValue(stmt)) - { - /* - * ADD VALUE can't be executed in a transaction, we will execute optimistically - * and on an error we will advise to fix the issue with the worker and rerun the - * query with the IF NOT EXTISTS modifier. The modifier is needed as the value - * might already be added to some nodes, but not all. - */ - - - /* qualification of the stmt happened during planning */ - const char *alterEnumStmtSql = DeparseTreeNode((Node *) stmt); - - List *commands = list_make2(DISABLE_DDL_PROPAGATION, (void *) alterEnumStmtSql); - - int result = SendBareOptionalCommandListToAllWorkersAsUser(commands, NULL); - - if (result != RESPONSE_OKAY) - { - bool oldSkipIfNewValueExists = stmt->skipIfNewValExists; - - /* deparse the query with IF NOT EXISTS */ - stmt->skipIfNewValExists = true; - const char *alterEnumStmtIfNotExistsSql = DeparseTreeNode((Node *) stmt); - stmt->skipIfNewValExists = oldSkipIfNewValueExists; - - ereport(WARNING, (errmsg("not all workers applied change to enum"), - errdetail("retry with: %s", alterEnumStmtIfNotExistsSql), - errhint("make sure the coordinators can communicate with " - "all workers"))); - } - } -#endif - - return NIL; -} - - /* * PreprocessDropTypeStmt is called for all DROP TYPE statements. For all types in the list that * citus has distributed to the workers it will drop the type on the workers as well. If diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index adde39dbf..31af1ad47 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -568,22 +568,6 @@ ProcessUtilityInternal(PlannedStmt *pstmt, PostprocessAlterExtensionCitusUpdateStmt(parsetree); } - /* - * Postgres added the following CommandCounterIncrement as a patch in: - * - 10.7 -> 10.8 - * - 11.2 -> 11.3 - * The patch was a response to bug #15631. - * - * CommandCounterIncrement is used to make changes to the catalog visible for post - * processing of create commands (eg. create type). It is safe to call - * CommandCounterIncrement twice, as the call is a no-op if the command id is not - * used yet. - * - * Once versions older than above are not deemed important anymore this patch can - * be remove from citus. - */ - CommandCounterIncrement(); - PostStandardProcessUtility(parsetree); } PG_CATCH(); diff --git a/src/backend/distributed/commands/vacuum.c b/src/backend/distributed/commands/vacuum.c index 55b30ebfb..e4569e3f7 100644 --- a/src/backend/distributed/commands/vacuum.c +++ b/src/backend/distributed/commands/vacuum.c @@ -12,9 +12,7 @@ #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "commands/defrem.h" -#endif #include "commands/vacuum.h" #include "distributed/adaptive_executor.h" #include "distributed/commands.h" @@ -41,14 +39,12 @@ typedef struct CitusVacuumParams { int options; -#if PG_VERSION_NUM >= PG_VERSION_12 VacOptTernaryValue truncate; VacOptTernaryValue index_cleanup; -#endif -#if PG_VERSION_NUM >= PG_VERSION_13 + #if PG_VERSION_NUM >= PG_VERSION_13 int nworkers; -#endif + #endif } CitusVacuumParams; /* Local functions forward declarations for processing distributed table commands */ @@ -349,11 +345,9 @@ DeparseVacuumStmtPrefix(CitusVacuumParams vacuumParams) } /* if no flags remain, exit early */ - if (vacuumFlags == 0 -#if PG_VERSION_NUM >= PG_VERSION_12 - && vacuumParams.truncate == VACOPT_TERNARY_DEFAULT && + if (vacuumFlags == 0 && + vacuumParams.truncate == VACOPT_TERNARY_DEFAULT && vacuumParams.index_cleanup == VACOPT_TERNARY_DEFAULT -#endif #if PG_VERSION_NUM >= PG_VERSION_13 && vacuumParams.nworkers == VACUUM_PARALLEL_NOTSET #endif @@ -390,7 +384,6 @@ DeparseVacuumStmtPrefix(CitusVacuumParams vacuumParams) appendStringInfoString(vacuumPrefix, "VERBOSE,"); } -#if PG_VERSION_NUM >= PG_VERSION_12 if (vacuumFlags & VACOPT_SKIP_LOCKED) { appendStringInfoString(vacuumPrefix, "SKIP_LOCKED,"); @@ -411,7 +404,6 @@ DeparseVacuumStmtPrefix(CitusVacuumParams vacuumParams) "INDEX_CLEANUP," : "INDEX_CLEANUP false," ); } -#endif #if PG_VERSION_NUM >= PG_VERSION_13 if (vacuumParams.nworkers != VACUUM_PARALLEL_NOTSET) @@ -496,7 +488,6 @@ ExtractVacuumTargetRels(VacuumStmt *vacuumStmt) /* * VacuumStmtParams returns a CitusVacuumParams based on the supplied VacuumStmt. */ -#if PG_VERSION_NUM >= PG_VERSION_12 /* * This is mostly ExecVacuum from Postgres's commands/vacuum.c @@ -611,16 +602,3 @@ VacuumStmtParams(VacuumStmt *vacstmt) (disable_page_skipping ? VACOPT_DISABLE_PAGE_SKIPPING : 0); return params; } - - -#else -static CitusVacuumParams -VacuumStmtParams(VacuumStmt *vacuumStmt) -{ - CitusVacuumParams params; - params.options = vacuumStmt->options; - return params; -} - - -#endif diff --git a/src/backend/distributed/deparser/citus_ruleutils.c b/src/backend/distributed/deparser/citus_ruleutils.c index c2f924b12..78af394c7 100644 --- a/src/backend/distributed/deparser/citus_ruleutils.c +++ b/src/backend/distributed/deparser/citus_ruleutils.c @@ -125,22 +125,17 @@ get_extension_schema(Oid ext_oid) /* *INDENT-OFF* */ Oid result; Relation rel; - SysScanDesc scandesc; HeapTuple tuple; ScanKeyData entry[1]; rel = table_open(ExtensionRelationId, AccessShareLock); ScanKeyInit(&entry[0], -#if PG_VERSION_NUM >= PG_VERSION_12 Anum_pg_extension_oid, -#else - ObjectIdAttributeNumber, -#endif BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(ext_oid)); - scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, + SysScanDesc scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, NULL, 1, entry); tuple = systable_getnext(scandesc); @@ -251,7 +246,6 @@ char * pg_get_tableschemadef_string(Oid tableRelationId, bool includeSequenceDefaults, char *accessMethod) { - char relationKind = 0; bool firstAttributePrinted = false; AttrNumber defaultValueIndex = 0; AttrNumber constraintIndex = 0; @@ -361,7 +355,6 @@ pg_get_tableschemadef_string(Oid tableRelationId, bool includeSequenceDefaults, defaultString = deparse_expression(defaultNode, defaultContext, false, false); -#if PG_VERSION_NUM >= PG_VERSION_12 if (attributeForm->attgenerated == ATTRIBUTE_GENERATED_STORED) { appendStringInfo(&buffer, " GENERATED ALWAYS AS (%s) STORED", @@ -371,9 +364,6 @@ pg_get_tableschemadef_string(Oid tableRelationId, bool includeSequenceDefaults, { appendStringInfo(&buffer, " DEFAULT %s", defaultString); } -#else - appendStringInfo(&buffer, " DEFAULT %s", defaultString); -#endif } } @@ -436,7 +426,7 @@ pg_get_tableschemadef_string(Oid tableRelationId, bool includeSequenceDefaults, * If the relation is a foreign table, append the server name and options to * the create table statement. */ - relationKind = relation->rd_rel->relkind; + char relationKind = relation->rd_rel->relkind; if (relationKind == RELKIND_FOREIGN_TABLE) { ForeignTable *foreignTable = GetForeignTable(tableRelationId); @@ -452,8 +442,6 @@ pg_get_tableschemadef_string(Oid tableRelationId, bool includeSequenceDefaults, appendStringInfo(&buffer, " PARTITION BY %s ", partitioningInformation); } -#if PG_VERSION_NUM >= 120000 - /* * Add table access methods for pg12 and higher when the table is configured with an * access method @@ -475,7 +463,6 @@ pg_get_tableschemadef_string(Oid tableRelationId, bool includeSequenceDefaults, appendStringInfo(&buffer, " USING %s", quote_identifier(NameStr(amForm->amname))); ReleaseSysCache(amTup); } -#endif /* * Add any reloptions (storage parameters) defined on the table in a WITH @@ -745,11 +732,7 @@ deparse_shard_reindex_statement(ReindexStmt *origStmt, Oid distrelid, int64 shar { ReindexStmt *reindexStmt = copyObject(origStmt); /* copy to avoid modifications */ char *relationName = NULL; -#if PG_VERSION_NUM >= PG_VERSION_12 const char *concurrentlyString = reindexStmt->concurrent ? "CONCURRENTLY " : ""; -#else - const char *concurrentlyString = ""; -#endif if (reindexStmt->kind == REINDEX_OBJECT_INDEX || diff --git a/src/backend/distributed/deparser/ruleutils_11.c b/src/backend/distributed/deparser/ruleutils_11.c deleted file mode 100644 index b11c71749..000000000 --- a/src/backend/distributed/deparser/ruleutils_11.c +++ /dev/null @@ -1,8436 +0,0 @@ -/*------------------------------------------------------------------------- - * - * ruleutils_11.c - * Functions to convert stored expressions/querytrees back to - * source text - * - * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/backend/distributed/utils/ruleutils_11.c - * - * This needs to be closely in sync with the core code. - *------------------------------------------------------------------------- - */ - -#include "distributed/pg_version_constants.h" - -#include "pg_config.h" - -#if (PG_VERSION_NUM >= PG_VERSION_11) && (PG_VERSION_NUM < PG_VERSION_12) - -#include "postgres.h" - -#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 "distributed/namespace_utils.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 0x0001 -#define PRETTYFLAG_INDENT 0x0002 -#define PRETTYFLAG_SCHEMA 0x0004 - -/* Default line length for pretty-print wrapping: 0 means wrap always */ -#define WRAP_COLUMN_DEFAULT 0 - -/* macros to test if pretty action needed */ -#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN) -#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT) -#define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA) - - -/* ---------- - * 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(const 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); -char *pg_get_statisticsobj_worker(Oid statextid, bool missing_ok); -static char *pg_get_triggerdef_worker(Oid trigid, bool pretty); -static void set_simple_column_names(deparse_namespace *dpns); -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_rte_shard_name(RangeTblEntry *rangeTableEntry); -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); - -#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; - StringInfo buffer = makeStringInfo(); - - PushOverrideEmptySearchPath(CurrentMemoryContext); - - 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; - bool has_anonymous; - 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++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - - if (attr->attisdropped) - real_colnames[i] = NULL; - else - real_colnames[i] = pstrdup(NameStr(attr->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; - has_anonymous = 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; - - /* - * Remember if there is a reference to an anonymous column as named by - * char * FigureColname(Node *node) - */ - if (!has_anonymous && strcmp(real_colname, "?column?") == 0) - has_anonymous = 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 || has_anonymous; -} - -/* - * 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(const 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; - - /* 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); - - PushOverrideEmptySearchPath(CurrentMemoryContext); - - 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(TupleDescAttr(resultDesc, 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) - appendStringInfoChar(context->buf, '('); - get_rule_expr(expr, context, true); - if (need_paren) - appendStringInfoChar(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) - appendStringInfoChar(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) - appendStringInfoChar(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 = ", "; - } - - appendStringInfoChar(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 if (wc->frameOptions & FRAMEOPTION_GROUPS) - appendStringInfoString(buf, "GROUPS "); - 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_OFFSET) - { - get_rule_expr(wc->startOffset, context, false); - if (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING) - appendStringInfoString(buf, " PRECEDING "); - else if (wc->frameOptions & FRAMEOPTION_START_OFFSET_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_OFFSET) - { - get_rule_expr(wc->endOffset, context, false); - if (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING) - appendStringInfoString(buf, " PRECEDING "); - else if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING) - appendStringInfoString(buf, " FOLLOWING "); - else - Assert(false); - } - else - Assert(false); - } - if (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) - appendStringInfoString(buf, "EXCLUDE CURRENT ROW "); - else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP) - appendStringInfoString(buf, "EXCLUDE GROUP "); - else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES) - appendStringInfoString(buf, "EXCLUDE TIES "); - /* 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(get_rtable_name(query->resultRelation, context))); - - /* - * 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_attname(rte->relid, - tle->resno, - false))); - - /* - * 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(get_rtable_name(query->resultRelation, context))); - } - 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(get_rtable_name(query->resultRelation, context))); - } - - 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_attname(rte->relid, - tle->resno, - false))); - - /* - * 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(get_rtable_name(query->resultRelation, context))); - } - 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(get_rtable_name(query->resultRelation, context))); - } - - /* 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_attname(rte->relid, attnum, false); - } - 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) - { - if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - /* use rel.*::shard_name instead of rel.*::table_name */ - appendStringInfo(buf, "::%s", - generate_rte_shard_name(rte)); - } - else - { - 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_tupdesc(). - */ - if (!IsA(var, Var) || - var->vartype != RECORDOID) - { - tupleDesc = get_expr_result_tupdesc((Node *) var, false); - /* Got the tupdesc, so we can extract the field name */ - Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); - return NameStr(TupleDescAttr(tupleDesc, 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_tupdesc() can do anything with it. - */ - tupleDesc = get_expr_result_tupdesc(expr, false); - /* Got the tupdesc, so we can extract the field name */ - Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); - return NameStr(TupleDescAttr(tupleDesc, 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: for base types just print $N. - * For composite types, add cast to the parameter to ease remote node detect - * the type. - */ - if (param->paramtype >= FirstNormalObjectId) - { - char *typeName = format_type_with_typemod(param->paramtype, param->paramtypmod); - - appendStringInfo(context->buf, "$%d::%s", param->paramid, typeName); - } - else - { - 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; - - /* - * This is a Citus specific modification - * The planner converts CollateExpr to RelabelType - * and here we convert back. - */ - if (relabel->resultcollid != InvalidOid) - { - CollateExpr *collate = RelabelTypeToCollateExpr(relabel); - 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, ')'); - } - else - { - 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 || - !TupleDescAttr(tupdesc, 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 (!TupleDescAttr(tupdesc, 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; - - if (spec->is_default) - { - appendStringInfoString(buf, "DEFAULT"); - break; - } - - switch (spec->strategy) - { - case PARTITION_STRATEGY_HASH: - Assert(spec->modulus > 0 && spec->remainder >= 0); - Assert(spec->modulus > spec->remainder); - - appendStringInfoString(buf, "FOR VALUES"); - appendStringInfo(buf, " WITH (modulus %d, remainder %d)", - spec->modulus, spec->remainder); - break; - - 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 = ", "; - } - - appendStringInfoChar(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, ')'); - } -} - -char * -pg_get_triggerdef_command(Oid triggerId) -{ - Assert(OidIsValid(triggerId)); - - /* no need to have pretty SQL command */ - bool prettyOutput = false; - return pg_get_triggerdef_worker(triggerId, prettyOutput); -} - - -char * -pg_get_statisticsobj_worker(Oid statextid, bool missing_ok) -{ - StringInfoData buf; - int colno; - bool isnull; - int i; - - HeapTuple statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid)); - - if (!HeapTupleIsValid(statexttup)) - { - if (missing_ok) - { - return NULL; - } - elog(ERROR, "cache lookup failed for statistics object %u", statextid); - } - - Form_pg_statistic_ext statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup); - - initStringInfo(&buf); - - char *nsp = get_namespace_name(statextrec->stxnamespace); - appendStringInfo(&buf, "CREATE STATISTICS %s", - quote_qualified_identifier(nsp, - NameStr(statextrec->stxname))); - - /* - * Decode the stxkind column so that we know which stats types to print. - */ - Datum datum = SysCacheGetAttr(STATEXTOID, statexttup, - Anum_pg_statistic_ext_stxkind, &isnull); - Assert(!isnull); - ArrayType *arr = DatumGetArrayTypeP(datum); - if (ARR_NDIM(arr) != 1 || - ARR_HASNULL(arr) || - ARR_ELEMTYPE(arr) != CHAROID) - { - elog(ERROR, "stxkind is not a 1-D char array"); - } - char *enabled = (char *) ARR_DATA_PTR(arr); - - bool ndistinct_enabled = false; - bool dependencies_enabled = false; - bool mcv_enabled = false; - - for (i = 0; i < ARR_DIMS(arr)[0]; i++) - { - if (enabled[i] == STATS_EXT_NDISTINCT) - { - ndistinct_enabled = true; - } - if (enabled[i] == STATS_EXT_DEPENDENCIES) - { - dependencies_enabled = true; - } - } - - /* - * If any option is disabled, then we'll need to append the types clause - * to show which options are enabled. We omit the types clause on purpose - * when all options are enabled, so a pg_dump/pg_restore will create all - * statistics types on a newer postgres version, if the statistics had all - * options enabled on the original version. - */ - if (!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) - { - bool gotone = false; - - appendStringInfoString(&buf, " ("); - - if (ndistinct_enabled) - { - appendStringInfoString(&buf, "ndistinct"); - gotone = true; - } - - if (dependencies_enabled) - { - appendStringInfo(&buf, "%sdependencies", gotone ? ", " : ""); - gotone = true; - } - - if (mcv_enabled) - { - appendStringInfo(&buf, "%smcv", gotone ? ", " : ""); - } - - appendStringInfoChar(&buf, ')'); - } - - appendStringInfoString(&buf, " ON "); - - for (colno = 0; colno < statextrec->stxkeys.dim1; colno++) - { - AttrNumber attnum = statextrec->stxkeys.values[colno]; - - if (colno > 0) - { - appendStringInfoString(&buf, ", "); - } - - char *attname = get_attname(statextrec->stxrelid, attnum, false); - - appendStringInfoString(&buf, quote_identifier(attname)); - } - - appendStringInfo(&buf, " FROM %s", - generate_relation_name(statextrec->stxrelid, NIL)); - - ReleaseSysCache(statexttup); - - return buf.data; -} - - -static char * -pg_get_triggerdef_worker(Oid trigid, bool pretty) -{ - HeapTuple ht_trig; - Form_pg_trigger trigrec; - StringInfoData buf; - Relation tgrel; - ScanKeyData skey[1]; - SysScanDesc tgscan; - int findx = 0; - char *tgname; - char *tgoldtable; - char *tgnewtable; - Oid argtypes[1]; /* dummy */ - Datum value; - bool isnull; - - /* - * Fetch the pg_trigger tuple by the Oid of the trigger - */ - tgrel = heap_open(TriggerRelationId, AccessShareLock); - - ScanKeyInit(&skey[0], - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(trigid)); - - tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true, - NULL, 1, skey); - - ht_trig = systable_getnext(tgscan); - - if (!HeapTupleIsValid(ht_trig)) - { - systable_endscan(tgscan); - heap_close(tgrel, AccessShareLock); - return NULL; - } - - trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig); - - /* - * Start the trigger definition. Note that the trigger's name should never - * be schema-qualified, but the trigger rel's name may be. - */ - initStringInfo(&buf); - - tgname = NameStr(trigrec->tgname); - appendStringInfo(&buf, "CREATE %sTRIGGER %s ", - OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "", - quote_identifier(tgname)); - - if (TRIGGER_FOR_BEFORE(trigrec->tgtype)) - appendStringInfoString(&buf, "BEFORE"); - else if (TRIGGER_FOR_AFTER(trigrec->tgtype)) - appendStringInfoString(&buf, "AFTER"); - else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype)) - appendStringInfoString(&buf, "INSTEAD OF"); - else - elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype); - - if (TRIGGER_FOR_INSERT(trigrec->tgtype)) - { - appendStringInfoString(&buf, " INSERT"); - findx++; - } - if (TRIGGER_FOR_DELETE(trigrec->tgtype)) - { - if (findx > 0) - appendStringInfoString(&buf, " OR DELETE"); - else - appendStringInfoString(&buf, " DELETE"); - findx++; - } - if (TRIGGER_FOR_UPDATE(trigrec->tgtype)) - { - if (findx > 0) - appendStringInfoString(&buf, " OR UPDATE"); - else - appendStringInfoString(&buf, " UPDATE"); - findx++; - /* tgattr is first var-width field, so OK to access directly */ - if (trigrec->tgattr.dim1 > 0) - { - int i; - - appendStringInfoString(&buf, " OF "); - for (i = 0; i < trigrec->tgattr.dim1; i++) - { - char *attname; - - if (i > 0) - appendStringInfoString(&buf, ", "); - attname = get_attname(trigrec->tgrelid, - trigrec->tgattr.values[i], false); - appendStringInfoString(&buf, quote_identifier(attname)); - } - } - } - if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype)) - { - if (findx > 0) - appendStringInfoString(&buf, " OR TRUNCATE"); - else - appendStringInfoString(&buf, " TRUNCATE"); - findx++; - } - - /* - * In non-pretty mode, always schema-qualify the target table name for - * safety. In pretty mode, schema-qualify only if not visible. - */ - appendStringInfo(&buf, " ON %s ", - pretty ? - generate_relation_name(trigrec->tgrelid, NIL) : - generate_qualified_relation_name(trigrec->tgrelid)); - - if (OidIsValid(trigrec->tgconstraint)) - { - if (OidIsValid(trigrec->tgconstrrelid)) - appendStringInfo(&buf, "FROM %s ", - generate_relation_name(trigrec->tgconstrrelid, NIL)); - if (!trigrec->tgdeferrable) - appendStringInfoString(&buf, "NOT "); - appendStringInfoString(&buf, "DEFERRABLE INITIALLY "); - if (trigrec->tginitdeferred) - appendStringInfoString(&buf, "DEFERRED "); - else - appendStringInfoString(&buf, "IMMEDIATE "); - } - - value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable, - tgrel->rd_att, &isnull); - if (!isnull) - tgoldtable = NameStr(*DatumGetName(value)); - else - tgoldtable = NULL; - value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable, - tgrel->rd_att, &isnull); - if (!isnull) - tgnewtable = NameStr(*DatumGetName(value)); - else - tgnewtable = NULL; - if (tgoldtable != NULL || tgnewtable != NULL) - { - appendStringInfoString(&buf, "REFERENCING "); - if (tgoldtable != NULL) - appendStringInfo(&buf, "OLD TABLE AS %s ", - quote_identifier(tgoldtable)); - if (tgnewtable != NULL) - appendStringInfo(&buf, "NEW TABLE AS %s ", - quote_identifier(tgnewtable)); - } - - if (TRIGGER_FOR_ROW(trigrec->tgtype)) - appendStringInfoString(&buf, "FOR EACH ROW "); - else - appendStringInfoString(&buf, "FOR EACH STATEMENT "); - - /* If the trigger has a WHEN qualification, add that */ - value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual, - tgrel->rd_att, &isnull); - if (!isnull) - { - Node *qual; - char relkind; - deparse_context context; - deparse_namespace dpns; - RangeTblEntry *oldrte; - RangeTblEntry *newrte; - - appendStringInfoString(&buf, "WHEN ("); - - qual = stringToNode(TextDatumGetCString(value)); - - relkind = get_rel_relkind(trigrec->tgrelid); - - /* Build minimal OLD and NEW RTEs for the rel */ - oldrte = makeNode(RangeTblEntry); - oldrte->rtekind = RTE_RELATION; - oldrte->relid = trigrec->tgrelid; - oldrte->relkind = relkind; - oldrte->alias = makeAlias("old", NIL); - oldrte->eref = oldrte->alias; - oldrte->lateral = false; - oldrte->inh = false; - oldrte->inFromCl = true; - - newrte = makeNode(RangeTblEntry); - newrte->rtekind = RTE_RELATION; - newrte->relid = trigrec->tgrelid; - newrte->relkind = relkind; - newrte->alias = makeAlias("new", NIL); - newrte->eref = newrte->alias; - newrte->lateral = false; - newrte->inh = false; - newrte->inFromCl = true; - - /* Build two-element rtable */ - memset(&dpns, 0, sizeof(dpns)); - dpns.rtable = list_make2(oldrte, newrte); - dpns.ctes = NIL; - set_rtable_names(&dpns, NIL, NULL); - set_simple_column_names(&dpns); - - /* Set up context with one-deep namespace stack */ - context.buf = &buf; - context.namespaces = list_make1(&dpns); - context.windowClause = NIL; - context.windowTList = NIL; - context.varprefix = true; - context.prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT; - context.wrapColumn = WRAP_COLUMN_DEFAULT; - context.indentLevel = PRETTYINDENT_STD; - context.special_exprkind = EXPR_KIND_NONE; - - get_rule_expr(qual, &context, false); - - appendStringInfoString(&buf, ") "); - } - - appendStringInfo(&buf, "EXECUTE PROCEDURE %s(", - generate_function_name(trigrec->tgfoid, 0, - NIL, argtypes, - false, NULL, EXPR_KIND_NONE)); - - if (trigrec->tgnargs > 0) - { - char *p; - int i; - - value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs, - tgrel->rd_att, &isnull); - if (isnull) - elog(ERROR, "tgargs is null for trigger %u", trigid); - p = (char *) VARDATA_ANY(DatumGetByteaPP(value)); - for (i = 0; i < trigrec->tgnargs; i++) - { - if (i > 0) - appendStringInfoString(&buf, ", "); - simple_quote_literal(&buf, p); - /* advance p to next string embedded in tgargs */ - while (*p) - p++; - p++; - } - } - - /* We deliberately do not put semi-colon at end */ - appendStringInfoChar(&buf, ')'); - - /* Clean up */ - systable_endscan(tgscan); - - heap_close(tgrel, AccessShareLock); - - return buf.data; -} - -/* - * set_simple_column_names: fill in column aliases for non-query situations - * - * This handles EXPLAIN and cases where we only have relation RTEs. Without - * a join tree, we can't do anything smart about join RTEs, but we don't - * need to (note that EXPLAIN should never see join alias Vars anyway). - * If we do hit a join RTE we'll just process it like a non-table base RTE. - */ -static void -set_simple_column_names(deparse_namespace *dpns) -{ - ListCell *lc; - ListCell *lc2; - - /* 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))); - - /* Assign unique column aliases within each RTE */ - forboth(lc, dpns->rtable, lc2, dpns->rtable_columns) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - deparse_columns *colinfo = (deparse_columns *) lfirst(lc2); - - set_relation_column_names(dpns, rte, colinfo); - } -} - -/* - * 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_attname(typrelid, - linitial_int(fstore->fieldnums), false); - 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_rte_shard_name returns the qualified name of the shard given a - * CITUS_RTE_SHARD range table entry. - */ -static char * -generate_rte_shard_name(RangeTblEntry *rangeTableEntry) -{ - char *shardSchemaName = NULL; - char *shardTableName = NULL; - - Assert(GetRangeTblKind(rangeTableEntry) == CITUS_RTE_SHARD); - - ExtractRangeTblExtraData(rangeTableEntry, NULL, &shardSchemaName, &shardTableName, - NULL); - - return generate_fragment_name(shardSchemaName, shardTableName); -} - - -/* - * 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. - */ -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 = ", "; - } - appendStringInfoChar(buf, ')'); - - return buf->data; -} - -#endif /* (PG_VERSION_NUM >= PG_VERSION_11) && (PG_VERSION_NUM < PG_VERSION_12) */ diff --git a/src/backend/distributed/executor/citus_custom_scan.c b/src/backend/distributed/executor/citus_custom_scan.c index 39abd6e84..7c873b2d2 100644 --- a/src/backend/distributed/executor/citus_custom_scan.c +++ b/src/backend/distributed/executor/citus_custom_scan.c @@ -36,11 +36,7 @@ #include "distributed/worker_protocol.h" #include "executor/executor.h" #include "nodes/makefuncs.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/planner.h" -#endif #include "optimizer/clauses.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -157,7 +153,6 @@ CitusBeginScan(CustomScanState *node, EState *estate, int eflags) */ EnableWorkerMessagePropagation(); -#if PG_VERSION_NUM >= PG_VERSION_12 /* * Since we are using a tuplestore we cannot use the virtual tuples postgres had @@ -176,7 +171,6 @@ CitusBeginScan(CustomScanState *node, EState *estate, int eflags) ExecAssignScanProjectionInfoWithVarno(&node->ss, INDEX_VAR); node->ss.ps.qual = ExecInitQual(node->ss.ps.plan->qual, (PlanState *) node); -#endif DistributedPlan *distributedPlan = scanState->distributedPlan; if (distributedPlan->insertSelectQuery != NULL) diff --git a/src/backend/distributed/executor/distributed_intermediate_results.c b/src/backend/distributed/executor/distributed_intermediate_results.c index 0c9bcdff9..c30ea8cbb 100644 --- a/src/backend/distributed/executor/distributed_intermediate_results.c +++ b/src/backend/distributed/executor/distributed_intermediate_results.c @@ -272,14 +272,9 @@ WrapTasksForPartitioning(const char *resultIdPrefix, List *selectTaskList, static PartitioningTupleDest * CreatePartitioningTupleDest(CitusTableCacheEntry *targetRelation) { - TupleDesc tupleDescriptor = NULL; int resultColumnCount = 3; -#if PG_VERSION_NUM >= PG_VERSION_12 - tupleDescriptor = CreateTemplateTupleDesc(resultColumnCount); -#else - tupleDescriptor = CreateTemplateTupleDesc(resultColumnCount, false); -#endif + TupleDesc tupleDescriptor = CreateTemplateTupleDesc(resultColumnCount); TupleDescInitEntry(tupleDescriptor, (AttrNumber) 1, "partition_index", INT4OID, -1, 0); @@ -686,14 +681,9 @@ QueryStringForFragmentsTransfer(NodeToNodeFragmentsTransfer *fragmentsTransfer) static void ExecuteFetchTaskList(List *taskList) { - TupleDesc resultDescriptor = NULL; int resultColumnCount = 1; -#if PG_VERSION_NUM >= PG_VERSION_12 - resultDescriptor = CreateTemplateTupleDesc(resultColumnCount); -#else - resultDescriptor = CreateTemplateTupleDesc(resultColumnCount, false); -#endif + TupleDesc resultDescriptor = CreateTemplateTupleDesc(resultColumnCount); TupleDescInitEntry(resultDescriptor, (AttrNumber) 1, "byte_count", INT8OID, -1, 0); diff --git a/src/backend/distributed/executor/local_executor.c b/src/backend/distributed/executor/local_executor.c index 786d5a29d..9de14db2c 100644 --- a/src/backend/distributed/executor/local_executor.c +++ b/src/backend/distributed/executor/local_executor.c @@ -100,11 +100,7 @@ #include "distributed/worker_protocol.h" #include "executor/tstoreReceiver.h" #include "executor/tuptable.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/planner.h" -#endif #include "nodes/params.h" #include "utils/snapmgr.h" diff --git a/src/backend/distributed/executor/multi_executor.c b/src/backend/distributed/executor/multi_executor.c index 5bff8e949..121766a02 100644 --- a/src/backend/distributed/executor/multi_executor.c +++ b/src/backend/distributed/executor/multi_executor.c @@ -181,9 +181,6 @@ CitusExecutorRun(QueryDesc *queryDesc, EState *estate = queryDesc->estate; estate->es_processed = 0; -#if PG_VERSION_NUM < PG_VERSION_12 - estate->es_lastoid = InvalidOid; -#endif /* start and shutdown tuple receiver to simulate empty result */ dest->rStartup(queryDesc->dest, CMD_SELECT, queryDesc->tupDesc); diff --git a/src/backend/distributed/executor/tuple_destination.c b/src/backend/distributed/executor/tuple_destination.c index 0fe1442fa..13dedac08 100644 --- a/src/backend/distributed/executor/tuple_destination.c +++ b/src/backend/distributed/executor/tuple_destination.c @@ -280,11 +280,7 @@ TupleDestDestReceiverReceive(TupleTableSlot *slot, Assert(task->queryCount == 1); int queryNumber = 0; -#if PG_VERSION_NUM >= PG_VERSION_12 HeapTuple heapTuple = ExecFetchSlotHeapTuple(slot, true, NULL); -#else - HeapTuple heapTuple = ExecFetchSlotTuple(slot); -#endif uint64 tupleLibpqSize = 0; diff --git a/src/backend/distributed/metadata/dependency.c b/src/backend/distributed/metadata/dependency.c index e116ae3ad..f84e1152a 100644 --- a/src/backend/distributed/metadata/dependency.c +++ b/src/backend/distributed/metadata/dependency.c @@ -548,7 +548,6 @@ SupportedDependencyByCitus(const ObjectAddress *address) */ switch (getObjectClass(address)) { -#if PG_VERSION_NUM >= 120000 case OCLASS_AM: { /* @@ -559,7 +558,6 @@ SupportedDependencyByCitus(const ObjectAddress *address) */ return IsObjectAddressOwnedByExtension(address, NULL); } -#endif case OCLASS_COLLATION: case OCLASS_SCHEMA: @@ -1188,17 +1186,10 @@ GetDependingView(Form_pg_depend pg_depend) Relation rewriteRel = table_open(RewriteRelationId, AccessShareLock); ScanKeyData rkey[1]; -#if PG_VERSION_NUM >= PG_VERSION_12 ScanKeyInit(&rkey[0], Anum_pg_rewrite_oid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(pg_depend->objid)); -#else - ScanKeyInit(&rkey[0], - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(pg_depend->objid)); -#endif SysScanDesc rscan = systable_beginscan(rewriteRel, RewriteOidIndexId, true, NULL, 1, rkey); diff --git a/src/backend/distributed/metadata/distobject.c b/src/backend/distributed/metadata/distobject.c index 9f4c3ddc4..ef2adf641 100644 --- a/src/backend/distributed/metadata/distobject.c +++ b/src/backend/distributed/metadata/distobject.c @@ -114,15 +114,11 @@ ObjectExists(const ObjectAddress *address) if (is_objectclass_supported(address->classId)) { - HeapTuple objtup; Relation catalog = table_open(address->classId, AccessShareLock); -#if PG_VERSION_NUM >= PG_VERSION_12 - objtup = get_catalog_object_by_oid(catalog, get_object_attnum_oid( - address->classId), address->objectId); -#else - objtup = get_catalog_object_by_oid(catalog, address->objectId); -#endif + HeapTuple objtup = get_catalog_object_by_oid(catalog, get_object_attnum_oid( + address->classId), + address->objectId); table_close(catalog, AccessShareLock); if (objtup != NULL) { diff --git a/src/backend/distributed/metadata/metadata_cache.c b/src/backend/distributed/metadata/metadata_cache.c index df228f252..aa5a61886 100644 --- a/src/backend/distributed/metadata/metadata_cache.c +++ b/src/backend/distributed/metadata/metadata_cache.c @@ -2552,7 +2552,6 @@ LookupTypeOid(char *schemaNameSting, char *typeNameString) List *qualifiedName = list_make2(schemaName, typeName); TypeName *enumTypeName = makeTypeNameFromNameList(qualifiedName); - Oid nodeRoleTypId; /* typenameTypeId but instead of raising an error return InvalidOid */ Type tup = LookupTypeName(NULL, enumTypeName, NULL, false); @@ -2561,11 +2560,7 @@ LookupTypeOid(char *schemaNameSting, char *typeNameString) return InvalidOid; } -#if PG_VERSION_NUM >= PG_VERSION_12 - nodeRoleTypId = ((Form_pg_type) GETSTRUCT(tup))->oid; -#else - nodeRoleTypId = HeapTupleGetOid(tup); -#endif + Oid nodeRoleTypId = ((Form_pg_type) GETSTRUCT(tup))->oid; ReleaseSysCache(tup); return nodeRoleTypId; diff --git a/src/backend/distributed/metadata/metadata_utility.c b/src/backend/distributed/metadata/metadata_utility.c index 6a10f8d4b..3bf034ad9 100644 --- a/src/backend/distributed/metadata/metadata_utility.c +++ b/src/backend/distributed/metadata/metadata_utility.c @@ -17,9 +17,7 @@ #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/genam.h" -#endif #include "access/htup_details.h" #include "access/sysattr.h" #include "access/xact.h" diff --git a/src/backend/distributed/operations/delete_protocol.c b/src/backend/distributed/operations/delete_protocol.c index 84a0bc9a9..3ee3752b7 100644 --- a/src/backend/distributed/operations/delete_protocol.c +++ b/src/backend/distributed/operations/delete_protocol.c @@ -48,21 +48,14 @@ #include "distributed/worker_protocol.h" #include "distributed/worker_transaction.h" #include "lib/stringinfo.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/nodeFuncs.h" -#endif #include "nodes/nodes.h" #include "nodes/parsenodes.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" #include "optimizer/clauses.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" #include "optimizer/optimizer.h" -#else -#include "nodes/relation.h" -#include "optimizer/predtest.h" -#endif #include "optimizer/restrictinfo.h" #include "storage/lock.h" #include "storage/lmgr.h" diff --git a/src/backend/distributed/operations/modify_multiple_shards.c b/src/backend/distributed/operations/modify_multiple_shards.c index 62a5cab70..caac4cb64 100644 --- a/src/backend/distributed/operations/modify_multiple_shards.c +++ b/src/backend/distributed/operations/modify_multiple_shards.c @@ -48,12 +48,7 @@ #include "distributed/worker_protocol.h" #include "distributed/worker_transaction.h" #include "optimizer/clauses.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/predtest.h" -#include "optimizer/var.h" -#endif #include "optimizer/restrictinfo.h" #include "nodes/makefuncs.h" #include "tcop/tcopprot.h" diff --git a/src/backend/distributed/operations/node_protocol.c b/src/backend/distributed/operations/node_protocol.c index 3ae91611a..f4def2511 100644 --- a/src/backend/distributed/operations/node_protocol.c +++ b/src/backend/distributed/operations/node_protocol.c @@ -451,15 +451,12 @@ Datum citus_get_active_worker_nodes(PG_FUNCTION_ARGS) { FuncCallContext *functionContext = NULL; - uint32 workerNodeIndex = 0; uint32 workerNodeCount = 0; CheckCitusVersion(ERROR); if (SRF_IS_FIRSTCALL()) { - TupleDesc tupleDescriptor = NULL; - /* create a function context for cross-call persistence */ functionContext = SRF_FIRSTCALL_INIT(); @@ -477,11 +474,7 @@ citus_get_active_worker_nodes(PG_FUNCTION_ARGS) * This tuple descriptor must match the output parameters declared for * the function in pg_proc. */ -#if PG_VERSION_NUM < PG_VERSION_12 - tupleDescriptor = CreateTemplateTupleDesc(WORKER_NODE_FIELDS, false); -#else - tupleDescriptor = CreateTemplateTupleDesc(WORKER_NODE_FIELDS); -#endif + TupleDesc tupleDescriptor = CreateTemplateTupleDesc(WORKER_NODE_FIELDS); TupleDescInitEntry(tupleDescriptor, (AttrNumber) 1, "node_name", TEXTOID, -1, 0); TupleDescInitEntry(tupleDescriptor, (AttrNumber) 2, "node_port", @@ -493,7 +486,7 @@ citus_get_active_worker_nodes(PG_FUNCTION_ARGS) } functionContext = SRF_PERCALL_SETUP(); - workerNodeIndex = functionContext->call_cntr; + uint32 workerNodeIndex = functionContext->call_cntr; workerNodeCount = functionContext->max_calls; if (workerNodeIndex < workerNodeCount) @@ -668,7 +661,6 @@ GetPreLoadTableCreationCommands(Oid relationId, bool includeSequenceDefaults, tableColumnOptionsDef)); } -#if PG_VERSION_NUM >= 120000 /* add columnar options for cstore tables */ if (accessMethod == NULL && IsColumnarTableAmTable(relationId)) @@ -679,7 +671,6 @@ GetPreLoadTableCreationCommands(Oid relationId, bool includeSequenceDefaults, tableDDLEventList = lappend(tableDDLEventList, cstoreOptionsDDL); } } -#endif char *tableOwnerDef = TableOwnerResetCommand(relationId); if (tableOwnerDef != NULL) diff --git a/src/backend/distributed/planner/combine_query_planner.c b/src/backend/distributed/planner/combine_query_planner.c index 8f2512753..37bb443e3 100644 --- a/src/backend/distributed/planner/combine_query_planner.c +++ b/src/backend/distributed/planner/combine_query_planner.c @@ -346,11 +346,7 @@ FindCitusExtradataContainerRTE(Node *node, RangeTblEntry **result) } else if (IsA(node, Query)) { -#if PG_VERSION_NUM >= PG_VERSION_12 const int flags = QTW_EXAMINE_RTES_BEFORE; -#else - const int flags = QTW_EXAMINE_RTES; -#endif return query_tree_walker((Query *) node, FindCitusExtradataContainerRTE, result, flags); } diff --git a/src/backend/distributed/planner/cte_inline.c b/src/backend/distributed/planner/cte_inline.c index 22b17c3d5..4dfcfef0a 100644 --- a/src/backend/distributed/planner/cte_inline.c +++ b/src/backend/distributed/planner/cte_inline.c @@ -7,8 +7,6 @@ * planning, the query pushdown planning, kicks in and the CTEs can actually * be pushed down as long as it is safe to pushdown as a subquery. * - * Most of the logic in this function is inspired (and some is copy & pasted) - * from PostgreSQL 12's CTE inlining feature. * * Copyright (c) Citus Data, Inc. *------------------------------------------------------------------------- @@ -18,29 +16,9 @@ #include "distributed/cte_inline.h" #include "nodes/nodeFuncs.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/cost.h" -#include "optimizer/clauses.h" -#endif #include "rewrite/rewriteManip.h" -#if PG_VERSION_NUM < PG_VERSION_12 - -/* copy & paste from PG 12 */ -#define PG_12_QTW_EXAMINE_RTES_BEFORE 0x10 -#define PG_12_QTW_EXAMINE_RTES_AFTER 0x20 -bool pg_12_query_tree_walker(Query *query, - bool (*walker)(), - void *context, - int flags); -bool pg_12_range_table_walker(List *rtable, - bool (*walker)(), - void *context, - int flags); -#endif - typedef struct inline_cte_walker_context { const char *ctename; /* name and relative level of target CTE */ @@ -233,18 +211,9 @@ PostgreSQLCTEInlineCondition(CommonTableExpr *cte, CmdType cmdType) * will be inlined even if multiply referenced. */ if ( -#if PG_VERSION_NUM >= PG_VERSION_12 (cte->ctematerialized == CTEMaterializeNever || (cte->ctematerialized == CTEMaterializeDefault && cte->cterefcount == 1)) && -#else - - /* - * If referenced only once inlining would probably perform - * better, so for pg < 12, try inlining - */ - cte->cterefcount == 1 && -#endif !cte->cterecursive && cmdType == CMD_SELECT && !contain_dml(cte->ctequery) && @@ -294,18 +263,8 @@ inline_cte_walker(Node *node, inline_cte_walker_context *context) context->levelsup++; - /* - * Visit the query's RTE nodes after their contents; otherwise - * query_tree_walker would descend into the newly inlined CTE query, - * which we don't want. - */ -#if PG_VERSION_NUM < PG_VERSION_12 - (void) pg_12_query_tree_walker(query, inline_cte_walker, context, - PG_12_QTW_EXAMINE_RTES_AFTER); -#else (void) query_tree_walker(query, inline_cte_walker, context, QTW_EXAMINE_RTES_AFTER); -#endif context->levelsup--; return false; @@ -411,123 +370,4 @@ contain_dml_walker(Node *node, void *context) return expression_tree_walker(node, contain_dml_walker, context); } - -#if PG_VERSION_NUM < PG_VERSION_12 -/* - * pg_12_query_tree_walker is copied from Postgres 12's source - * code. The only difference between query_tree_walker the new - * two flags added in range_table_walker: QTW_EXAMINE_RTES_AFTER - * and QTW_EXAMINE_RTES_BEFORE. - */ -bool -pg_12_query_tree_walker(Query *query, - bool (*walker) (), - void *context, - int flags) -{ - Assert(query != NULL && IsA(query, Query)); - - if (walker((Node *) query->targetList, context)) - return true; - if (walker((Node *) query->withCheckOptions, context)) - return true; - if (walker((Node *) query->onConflict, context)) - return true; - if (walker((Node *) query->returningList, context)) - return true; - if (walker((Node *) query->jointree, context)) - return true; - if (walker(query->setOperations, context)) - return true; - if (walker(query->havingQual, context)) - return true; - if (walker(query->limitOffset, context)) - return true; - if (walker(query->limitCount, context)) - return true; - if (!(flags & QTW_IGNORE_CTE_SUBQUERIES)) - { - if (walker((Node *) query->cteList, context)) - return true; - } - if (!(flags & QTW_IGNORE_RANGE_TABLE)) - { - if (pg_12_range_table_walker(query->rtable, walker, context, flags)) - return true; - } - return false; -} - -/* - * pg_12_range_table_walker is copied from Postgres 12's source - * code. The only difference between range_table_walker the new - * two flags added in range_table_walker: QTW_EXAMINE_RTES_AFTER - * and QTW_EXAMINE_RTES_BEFORE. - */ -bool -pg_12_range_table_walker(List *rtable, - bool (*walker) (), - void *context, - int flags) -{ - ListCell *rt; - - foreach(rt, rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); - - /* - * Walkers might need to examine the RTE node itself either before or - * after visiting its contents (or, conceivably, both). Note that if - * you specify neither flag, the walker won't visit the RTE at all. - */ - if (flags & PG_12_QTW_EXAMINE_RTES_BEFORE) - if (walker(rte, context)) - return true; - - switch (rte->rtekind) - { - case RTE_RELATION: - if (walker(rte->tablesample, context)) - return true; - break; - case RTE_CTE: - case RTE_NAMEDTUPLESTORE: - /* nothing to do */ - break; - case RTE_SUBQUERY: - if (!(flags & QTW_IGNORE_RT_SUBQUERIES)) - if (walker(rte->subquery, context)) - return true; - break; - case RTE_JOIN: - if (!(flags & QTW_IGNORE_JOINALIASES)) - if (walker(rte->joinaliasvars, context)) - return true; - break; - case RTE_FUNCTION: - if (walker(rte->functions, context)) - return true; - break; - case RTE_TABLEFUNC: - if (walker(rte->tablefunc, context)) - return true; - break; - case RTE_VALUES: - if (walker(rte->values_lists, context)) - return true; - break; - } - - if (walker(rte->securityQuals, context)) - return true; - - if (flags & PG_12_QTW_EXAMINE_RTES_AFTER) - if (walker(rte, context)) - return true; - } - return false; -} -#endif - /* *INDENT-ON* */ diff --git a/src/backend/distributed/planner/distributed_planner.c b/src/backend/distributed/planner/distributed_planner.c index 84cd3b7a9..c36bbda81 100644 --- a/src/backend/distributed/planner/distributed_planner.c +++ b/src/backend/distributed/planner/distributed_planner.c @@ -52,12 +52,8 @@ #include "nodes/pg_list.h" #include "parser/parsetree.h" #include "parser/parse_type.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" #include "optimizer/plancat.h" -#else -#include "optimizer/cost.h" -#endif #include "optimizer/pathnode.h" #include "optimizer/planner.h" #include "optimizer/planmain.h" @@ -751,24 +747,6 @@ InlineCtesAndCreateDistributedPlannedStmt(uint64 planId, /* after inlining, we shouldn't have any inlinable CTEs */ Assert(!QueryTreeContainsInlinableCTE(copyOfOriginalQuery)); -#if PG_VERSION_NUM < PG_VERSION_12 - Query *query = planContext->query; - - /* - * We had to implement this hack because on Postgres11 and below, the originalQuery - * and the query would have significant differences in terms of CTEs where CTEs - * would not be inlined on the query (as standard_planner() wouldn't inline CTEs - * on PG 11 and below). - * - * Instead, we prefer to pass the inlined query to the distributed planning. We rely - * on the fact that the query includes subqueries, and it'd definitely go through - * query pushdown planning. During query pushdown planning, the only relevant query - * tree is the original query. - */ - planContext->query = copyObject(copyOfOriginalQuery); -#endif - - /* simply recurse into CreateDistributedPlannedStmt() in a PG_TRY() block */ PlannedStmt *result = TryCreateDistributedPlannedStmt(planContext->plan, copyOfOriginalQuery, @@ -777,15 +755,6 @@ InlineCtesAndCreateDistributedPlannedStmt(uint64 planId, planContext-> plannerRestrictionContext); -#if PG_VERSION_NUM < PG_VERSION_12 - - /* - * Set back the original query, in case the planning failed and we need to go - * into distributed planning again. - */ - planContext->query = query; -#endif - return result; } @@ -1509,15 +1478,10 @@ BlessRecordExpression(Expr *expr) * Handle row expressions, e.g. SELECT (1,2); */ RowExpr *rowExpr = (RowExpr *) expr; - TupleDesc rowTupleDesc = NULL; ListCell *argCell = NULL; int currentResno = 1; -#if PG_VERSION_NUM >= PG_VERSION_12 - rowTupleDesc = CreateTemplateTupleDesc(list_length(rowExpr->args)); -#else - rowTupleDesc = CreateTemplateTupleDesc(list_length(rowExpr->args), false); -#endif + TupleDesc rowTupleDesc = CreateTemplateTupleDesc(list_length(rowExpr->args)); foreach(argCell, rowExpr->args) { @@ -2015,16 +1979,11 @@ AdjustReadIntermediateResultsCostInternal(RelOptInfo *relOptInfo, List *columnTy { PathTarget *reltarget = relOptInfo->reltarget; List *pathList = relOptInfo->pathlist; - Path *path = NULL; double rowCost = 0.; double rowSizeEstimate = 0; double rowCountEstimate = 0.; double ioCost = 0.; -#if PG_VERSION_NUM >= PG_VERSION_12 QualCost funcCost = { 0., 0. }; -#else - double funcCost = 0.; -#endif int64 totalResultSize = 0; ListCell *typeCell = NULL; @@ -2083,17 +2042,9 @@ AdjustReadIntermediateResultsCostInternal(RelOptInfo *relOptInfo, List *columnTy /* add the cost of parsing a column */ -#if PG_VERSION_NUM >= PG_VERSION_12 add_function_cost(NULL, inputFunctionId, NULL, &funcCost); -#else - funcCost += get_func_cost(inputFunctionId); -#endif } -#if PG_VERSION_NUM >= PG_VERSION_12 rowCost += funcCost.per_tuple; -#else - rowCost += funcCost * cpu_operator_cost; -#endif /* estimate the number of rows based on the file size and estimated row size */ rowCountEstimate = Max(1, (double) totalResultSize / rowSizeEstimate); @@ -2104,13 +2055,11 @@ AdjustReadIntermediateResultsCostInternal(RelOptInfo *relOptInfo, List *columnTy Assert(pathList != NIL); /* tell the planner about the cost and row count of the function */ - path = (Path *) linitial(pathList); + Path *path = (Path *) linitial(pathList); path->rows = rowCountEstimate; path->total_cost = rowCountEstimate * rowCost + ioCost; -#if PG_VERSION_NUM >= PG_VERSION_12 path->startup_cost = funcCost.startup + relOptInfo->baserestrictcost.startup; -#endif } diff --git a/src/backend/distributed/planner/extended_op_node_utils.c b/src/backend/distributed/planner/extended_op_node_utils.c index 22a950424..0a2a8b834 100644 --- a/src/backend/distributed/planner/extended_op_node_utils.c +++ b/src/backend/distributed/planner/extended_op_node_utils.c @@ -16,11 +16,7 @@ #include "distributed/metadata_cache.h" #include "distributed/multi_logical_optimizer.h" #include "distributed/pg_dist_partition.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/var.h" -#endif #include "optimizer/restrictinfo.h" #include "nodes/nodeFuncs.h" #include "nodes/pg_list.h" diff --git a/src/backend/distributed/planner/fast_path_router_planner.c b/src/backend/distributed/planner/fast_path_router_planner.c index 2d45abf66..8a2d87fe7 100644 --- a/src/backend/distributed/planner/fast_path_router_planner.c +++ b/src/backend/distributed/planner/fast_path_router_planner.c @@ -44,17 +44,11 @@ #include "distributed/pg_dist_partition.h" #include "distributed/shardinterval_utils.h" #include "distributed/shard_pruning.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/makefuncs.h" -#endif #include "nodes/nodeFuncs.h" #include "nodes/parsenodes.h" #include "nodes/pg_list.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/clauses.h" -#endif #include "tcop/pquery.h" bool EnableFastPathRouterPlanner = true; diff --git a/src/backend/distributed/planner/function_call_delegation.c b/src/backend/distributed/planner/function_call_delegation.c index e389a2b92..5e1ec0afd 100644 --- a/src/backend/distributed/planner/function_call_delegation.c +++ b/src/backend/distributed/planner/function_call_delegation.c @@ -43,9 +43,7 @@ #include "nodes/primnodes.h" #include "optimizer/clauses.h" #include "parser/parse_coerce.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "parser/parsetree.h" -#endif #include "miscadmin.h" #include "tcop/dest.h" #include "utils/lsyscache.h" @@ -102,18 +100,8 @@ contain_param_walker(Node *node, void *context) PlannedStmt * TryToDelegateFunctionCall(DistributedPlanningContext *planContext) { - List *targetList = NIL; - TargetEntry *targetEntry = NULL; - FuncExpr *funcExpr = NULL; - DistObjectCacheEntry *procedure = NULL; - Oid colocatedRelationId = InvalidOid; bool colocatedWithReferenceTable = false; - CitusTableCacheEntry *distTable = NULL; - Var *partitionColumn = NULL; ShardPlacement *placement = NULL; - WorkerNode *workerNode = NULL; - Task *task = NULL; - Job *job = NULL; DistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan); struct ParamWalkerContext walkerParamContext = { 0 }; @@ -157,8 +145,6 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) if (joinTree->fromlist != NIL) { -#if PG_VERSION_NUM >= PG_VERSION_12 - /* * In pg12's planning phase empty FROMs are represented with an RTE_RESULT. * When we arrive here, standard_planner has already been called which calls @@ -194,29 +180,25 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) Assert(list_length(joinTree->fromlist) > 1); return NULL; } -#else - - /* query has a FROM section */ - return NULL; -#endif } - targetList = planContext->query->targetList; + List *targetList = planContext->query->targetList; if (list_length(planContext->query->targetList) != 1) { /* multiple target list items */ return NULL; } - targetEntry = (TargetEntry *) linitial(targetList); + TargetEntry *targetEntry = (TargetEntry *) linitial(targetList); if (!IsA(targetEntry->expr, FuncExpr)) { /* target list item is not a function call */ return NULL; } - funcExpr = (FuncExpr *) targetEntry->expr; - procedure = LookupDistObjectCacheEntry(ProcedureRelationId, funcExpr->funcid, 0); + FuncExpr *funcExpr = (FuncExpr *) targetEntry->expr; + DistObjectCacheEntry *procedure = LookupDistObjectCacheEntry(ProcedureRelationId, + funcExpr->funcid, 0); if (procedure == NULL || !procedure->isDistributed) { /* not a distributed function call */ @@ -252,15 +234,15 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) return NULL; } - colocatedRelationId = ColocatedTableId(procedure->colocationId); + Oid colocatedRelationId = ColocatedTableId(procedure->colocationId); if (colocatedRelationId == InvalidOid) { ereport(DEBUG1, (errmsg("function does not have co-located tables"))); return NULL; } - distTable = GetCitusTableCacheEntry(colocatedRelationId); - partitionColumn = distTable->partitionColumn; + CitusTableCacheEntry *distTable = GetCitusTableCacheEntry(colocatedRelationId); + Var *partitionColumn = distTable->partitionColumn; if (partitionColumn == NULL) { colocatedWithReferenceTable = true; @@ -295,7 +277,7 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) return false; } - workerNode = FindWorkerNode(placement->nodeName, placement->nodePort); + WorkerNode *workerNode = FindWorkerNode(placement->nodeName, placement->nodePort); if (workerNode == NULL || !workerNode->hasMetadata || !workerNode->metadataSynced) { @@ -334,14 +316,14 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) ereport(DEBUG1, (errmsg("pushing down the function call"))); - task = CitusMakeNode(Task); + Task *task = CitusMakeNode(Task); task->taskType = READ_TASK; task->taskPlacementList = list_make1(placement); SetTaskQueryIfShouldLazyDeparse(task, planContext->query); task->anchorShardId = placement->shardId; task->replicationModel = distTable->replicationModel; - job = CitusMakeNode(Job); + Job *job = CitusMakeNode(Job); job->jobId = UniqueJobId(); job->jobQuery = planContext->query; job->taskList = list_make1(task); diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index f07803866..137e19fc7 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -40,11 +40,7 @@ #include "optimizer/planner.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/var.h" -#endif #include "parser/parsetree.h" #include "parser/parse_coerce.h" #include "parser/parse_relation.h" diff --git a/src/backend/distributed/planner/local_distributed_join_planner.c b/src/backend/distributed/planner/local_distributed_join_planner.c index 87208ff17..bb7c1d4de 100644 --- a/src/backend/distributed/planner/local_distributed_join_planner.c +++ b/src/backend/distributed/planner/local_distributed_join_planner.c @@ -103,11 +103,7 @@ #include "distributed/version_compat.h" #include "lib/stringinfo.h" #include "optimizer/clauses.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/var.h" -#endif #include "optimizer/planner.h" #include "optimizer/prep.h" #include "parser/parsetree.h" @@ -117,11 +113,7 @@ #include "nodes/nodeFuncs.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" -#else -#include "nodes/relation.h" -#endif #include "utils/builtins.h" #include "utils/guc.h" #include "utils/lsyscache.h" diff --git a/src/backend/distributed/planner/local_plan_cache.c b/src/backend/distributed/planner/local_plan_cache.c index ae5fa6f15..77a80a892 100644 --- a/src/backend/distributed/planner/local_plan_cache.c +++ b/src/backend/distributed/planner/local_plan_cache.c @@ -18,11 +18,7 @@ #include "distributed/citus_ruleutils.h" #include "distributed/metadata_cache.h" #include "distributed/version_compat.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/planner.h" -#endif #include "optimizer/clauses.h" diff --git a/src/backend/distributed/planner/multi_explain.c b/src/backend/distributed/planner/multi_explain.c index de1e444ff..df7db64a0 100644 --- a/src/backend/distributed/planner/multi_explain.c +++ b/src/backend/distributed/planner/multi_explain.c @@ -1273,11 +1273,7 @@ CreateExplainAnlyzeDestination(Task *task, TupleDestination *taskDest) tupleDestination->originalTask = task; tupleDestination->originalTaskDestination = taskDest; -#if PG_VERSION_NUM >= PG_VERSION_12 TupleDesc lastSavedExplainAnalyzeTupDesc = CreateTemplateTupleDesc(2); -#else - TupleDesc lastSavedExplainAnalyzeTupDesc = CreateTemplateTupleDesc(2, false); -#endif TupleDescInitEntry(lastSavedExplainAnalyzeTupDesc, 1, "explain analyze", TEXTOID, 0, 0); diff --git a/src/backend/distributed/planner/multi_join_order.c b/src/backend/distributed/planner/multi_join_order.c index f0c20d61e..a2b59e1a9 100644 --- a/src/backend/distributed/planner/multi_join_order.c +++ b/src/backend/distributed/planner/multi_join_order.c @@ -28,11 +28,7 @@ #include "distributed/pg_dist_partition.h" #include "distributed/worker_protocol.h" #include "lib/stringinfo.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/var.h" -#endif #include "utils/builtins.h" #include "nodes/nodeFuncs.h" #include "utils/builtins.h" diff --git a/src/backend/distributed/planner/multi_logical_optimizer.c b/src/backend/distributed/planner/multi_logical_optimizer.c index 3e66bed80..15c1a61a0 100644 --- a/src/backend/distributed/planner/multi_logical_optimizer.c +++ b/src/backend/distributed/planner/multi_logical_optimizer.c @@ -47,11 +47,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/tlist.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/var.h" -#endif #include "parser/parse_agg.h" #include "parser/parse_coerce.h" #include "parser/parse_oper.h" @@ -3608,11 +3604,7 @@ AggregateFunctionOid(const char *functionName, Oid inputType) if (procForm->proargtypes.values[0] == inputType || procForm->proargtypes.values[0] == ANYELEMENTOID) { -#if PG_VERSION_NUM < PG_VERSION_12 - functionOid = HeapTupleGetOid(heapTuple); -#else functionOid = procForm->oid; -#endif break; } } diff --git a/src/backend/distributed/planner/multi_logical_planner.c b/src/backend/distributed/planner/multi_logical_planner.c index 8d0289d7c..2b5e192c6 100644 --- a/src/backend/distributed/planner/multi_logical_planner.c +++ b/src/backend/distributed/planner/multi_logical_planner.c @@ -38,13 +38,8 @@ #include "distributed/version_compat.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" #include "optimizer/optimizer.h" -#else -#include "nodes/relation.h" -#include "optimizer/var.h" -#endif #include "optimizer/clauses.h" #include "optimizer/prep.h" #include "optimizer/tlist.h" diff --git a/src/backend/distributed/planner/multi_physical_planner.c b/src/backend/distributed/planner/multi_physical_planner.c index 5093a9d2d..e43c0fa56 100644 --- a/src/backend/distributed/planner/multi_physical_planner.c +++ b/src/backend/distributed/planner/multi_physical_planner.c @@ -61,13 +61,8 @@ #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" #include "optimizer/optimizer.h" -#else -#include "nodes/relation.h" -#include "optimizer/var.h" -#endif #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parse_relation.h" diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 87bdcf79e..b21553301 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -65,12 +65,7 @@ #include "optimizer/joininfo.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/var.h" -#include "optimizer/predtest.h" -#endif #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" #include "parser/parse_oper.h" @@ -967,10 +962,8 @@ ModifyQuerySupported(Query *queryTree, Query *originalQuery, bool multiShardQuer queryTableCount++; } - else if (rangeTableEntry->rtekind == RTE_VALUES -#if PG_VERSION_NUM >= PG_VERSION_12 - || rangeTableEntry->rtekind == RTE_RESULT -#endif + else if (rangeTableEntry->rtekind == RTE_VALUES || + rangeTableEntry->rtekind == RTE_RESULT ) { /* do nothing, this type is supported */ diff --git a/src/backend/distributed/planner/query_colocation_checker.c b/src/backend/distributed/planner/query_colocation_checker.c index 33357b624..b7cc41068 100644 --- a/src/backend/distributed/planner/query_colocation_checker.c +++ b/src/backend/distributed/planner/query_colocation_checker.c @@ -23,11 +23,7 @@ #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/relation.h" -#else -#include "access/heapam.h" -#endif #include "distributed/multi_logical_planner.h" #include "distributed/query_colocation_checker.h" #include "distributed/pg_dist_partition.h" diff --git a/src/backend/distributed/planner/query_pushdown_planning.c b/src/backend/distributed/planner/query_pushdown_planning.c index 043bf7a96..be3116fca 100644 --- a/src/backend/distributed/planner/query_pushdown_planning.c +++ b/src/backend/distributed/planner/query_pushdown_planning.c @@ -38,12 +38,8 @@ #include "distributed/relation_restriction_equivalence.h" #include "distributed/version_compat.h" #include "nodes/nodeFuncs.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" -#else -#include "optimizer/var.h" -#endif #include "nodes/pg_list.h" #include "optimizer/clauses.h" #include "parser/parsetree.h" @@ -235,8 +231,6 @@ HasEmptyJoinTree(Query *query) { return true; } - -#if PG_VERSION_NUM >= PG_VERSION_12 else if (list_length(query->rtable) == 1) { RangeTblEntry *rte = (RangeTblEntry *) linitial(query->rtable); @@ -245,7 +239,6 @@ HasEmptyJoinTree(Query *query) return true; } } -#endif return false; } @@ -1153,10 +1146,8 @@ DeferErrorIfUnsupportedTableCombination(Query *queryTree) * subquery, or immutable function. */ if (rangeTableEntry->rtekind == RTE_RELATION || - rangeTableEntry->rtekind == RTE_SUBQUERY -#if PG_VERSION_NUM >= PG_VERSION_12 - || rangeTableEntry->rtekind == RTE_RESULT -#endif + rangeTableEntry->rtekind == RTE_SUBQUERY || + rangeTableEntry->rtekind == RTE_RESULT ) { /* accepted */ @@ -1472,13 +1463,11 @@ HasRecurringTuples(Node *node, RecurringTuplesType *recurType) */ return true; } -#if PG_VERSION_NUM >= PG_VERSION_12 else if (rangeTableEntry->rtekind == RTE_RESULT) { *recurType = RECURRING_TUPLES_EMPTY_JOIN_TREE; return true; } -#endif return false; } diff --git a/src/backend/distributed/planner/recursive_planning.c b/src/backend/distributed/planner/recursive_planning.c index bb8591a8c..209714f74 100644 --- a/src/backend/distributed/planner/recursive_planning.c +++ b/src/backend/distributed/planner/recursive_planning.c @@ -77,11 +77,7 @@ #include "distributed/version_compat.h" #include "lib/stringinfo.h" #include "optimizer/clauses.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#else -#include "optimizer/var.h" -#endif #include "optimizer/planner.h" #include "optimizer/prep.h" #include "parser/parsetree.h" @@ -91,11 +87,7 @@ #include "nodes/nodeFuncs.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" -#else -#include "nodes/relation.h" -#endif #include "utils/builtins.h" #include "utils/guc.h" #include "utils/lsyscache.h" diff --git a/src/backend/distributed/planner/relation_restriction_equivalence.c b/src/backend/distributed/planner/relation_restriction_equivalence.c index e5aec8215..a253eae2d 100644 --- a/src/backend/distributed/planner/relation_restriction_equivalence.c +++ b/src/backend/distributed/planner/relation_restriction_equivalence.c @@ -28,14 +28,8 @@ #include "nodes/nodeFuncs.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" #include "optimizer/optimizer.h" -#else -#include "optimizer/cost.h" -#include "nodes/relation.h" -#include "optimizer/var.h" -#endif #include "nodes/makefuncs.h" #include "optimizer/paths.h" #include "parser/parsetree.h" diff --git a/src/backend/distributed/planner/shard_pruning.c b/src/backend/distributed/planner/shard_pruning.c index 8d606cc14..02c799b63 100644 --- a/src/backend/distributed/planner/shard_pruning.c +++ b/src/backend/distributed/planner/shard_pruning.c @@ -188,16 +188,12 @@ typedef struct PendingPruningInstance PruningTreeNode *continueAt; } PendingPruningInstance; -#if PG_VERSION_NUM >= PG_VERSION_12 typedef union \ { \ FunctionCallInfoBaseData fcinfo; \ /* ensure enough space for nargs args is available */ \ char fcinfo_data[SizeForFunctionCallInfo(2)]; \ } FunctionCall2InfoData; -#else -typedef FunctionCallInfoData FunctionCall2InfoData; -#endif /* * We also ignore this warning in ./configure, but that's not always enough. diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index b0499c8d8..7da35c7c0 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -125,7 +125,6 @@ static void CitusAuthHook(Port *port, int status); static ClientAuthentication_hook_type original_client_auth_hook = NULL; - /* *INDENT-OFF* */ /* GUC enum definitions */ static const struct config_enum_entry propagate_set_commands_options[] = { @@ -1076,12 +1075,13 @@ RegisterCitusConfigVariables(void) */ DefineCustomBoolVariable( "citus.enable_cte_inlining", - gettext_noop("When set to false, CTE inlining feature is disabled"), - gettext_noop("This feature is not intended for users. It is developed " - "to get consistent regression test outputs between Postgres 11" - "and Postgres 12. In Postgres 12+, the user can control the behaviour" - "by [NOT] MATERIALIZED keyword on CTEs. However, in PG 11, we cannot do " - "that."), + gettext_noop("When set to false, CTE inlining feature is disabled."), + gettext_noop( + "This feature is not intended for users and it is deprecated. It is developed " + "to get consistent regression test outputs between Postgres 11" + "and Postgres 12. In Postgres 12+, the user can control the behaviour" + "by [NOT] MATERIALIZED keyword on CTEs. However, in PG 11, we cannot do " + "that."), &EnableCTEInlining, true, PGC_SUSET, diff --git a/src/backend/distributed/test/fake_am.c b/src/backend/distributed/test/fake_am.c index 2d743d36c..39140eab7 100644 --- a/src/backend/distributed/test/fake_am.c +++ b/src/backend/distributed/test/fake_am.c @@ -21,7 +21,6 @@ #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/amapi.h" #include "access/heapam.h" @@ -586,6 +585,3 @@ fake_am_handler(PG_FUNCTION_ARGS) { PG_RETURN_POINTER(&fake_methods); } - - -#endif diff --git a/src/backend/distributed/test/fake_fdw.c b/src/backend/distributed/test/fake_fdw.c index 2b2a29318..4784248c0 100644 --- a/src/backend/distributed/test/fake_fdw.c +++ b/src/backend/distributed/test/fake_fdw.c @@ -25,11 +25,7 @@ #include "nodes/nodes.h" #include "nodes/pg_list.h" #include "nodes/plannodes.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" -#else -#include "nodes/relation.h" -#endif #include "optimizer/pathnode.h" #include "optimizer/planmain.h" #include "optimizer/restrictinfo.h" diff --git a/src/backend/distributed/test/prune_shard_list.c b/src/backend/distributed/test/prune_shard_list.c index f5505407b..98aa8e7ff 100644 --- a/src/backend/distributed/test/prune_shard_list.c +++ b/src/backend/distributed/test/prune_shard_list.c @@ -28,10 +28,8 @@ #include "distributed/multi_physical_planner.h" #include "distributed/resource_lock.h" #include "distributed/shard_pruning.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" -#endif #include "nodes/nodes.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" diff --git a/src/backend/distributed/transaction/backend_data.c b/src/backend/distributed/transaction/backend_data.c index 88dbff271..8026e50c1 100644 --- a/src/backend/distributed/transaction/backend_data.c +++ b/src/backend/distributed/transaction/backend_data.c @@ -32,9 +32,7 @@ #include "distributed/tuplestore.h" #include "nodes/execnodes.h" #include "postmaster/autovacuum.h" /* to access autovacuum_max_workers */ -#if PG_VERSION_NUM >= PG_VERSION_12 #include "replication/walsender.h" -#endif #include "storage/ipc.h" #include "storage/lmgr.h" #include "storage/lwlock.h" @@ -598,9 +596,7 @@ TotalProcCount(void) */ totalProcs = maxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts; -#if PG_VERSION_NUM >= PG_VERSION_12 totalProcs += max_wal_senders; -#endif return totalProcs; } diff --git a/src/backend/distributed/transaction/transaction_recovery.c b/src/backend/distributed/transaction/transaction_recovery.c index ac1f704d7..1f42ab2e6 100644 --- a/src/backend/distributed/transaction/transaction_recovery.c +++ b/src/backend/distributed/transaction/transaction_recovery.c @@ -22,9 +22,7 @@ #include #include -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/genam.h" -#endif #include "access/heapam.h" #include "access/htup_details.h" #include "access/relscan.h" diff --git a/src/backend/distributed/utils/citus_clauses.c b/src/backend/distributed/utils/citus_clauses.c index 630adaf98..99f1a3ac6 100644 --- a/src/backend/distributed/utils/citus_clauses.c +++ b/src/backend/distributed/utils/citus_clauses.c @@ -21,9 +21,7 @@ #include "nodes/nodes.h" #include "nodes/primnodes.h" #include "optimizer/clauses.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "optimizer/optimizer.h" -#endif #include "optimizer/planmain.h" #include "utils/datum.h" #include "utils/lsyscache.h" diff --git a/src/backend/distributed/utils/citus_nodefuncs.c b/src/backend/distributed/utils/citus_nodefuncs.c index 6b8635e13..aee1ff48a 100644 --- a/src/backend/distributed/utils/citus_nodefuncs.c +++ b/src/backend/distributed/utils/citus_nodefuncs.c @@ -310,13 +310,11 @@ GetRangeTblKind(RangeTblEntry *rte) case RTE_JOIN: case RTE_VALUES: case RTE_CTE: -#if PG_VERSION_NUM >= PG_VERSION_12 case RTE_RESULT: -#endif - { - rteKind = (CitusRTEKind) rte->rtekind; - break; - } + { + rteKind = (CitusRTEKind) rte->rtekind; + break; + } case RTE_FUNCTION: { diff --git a/src/backend/distributed/utils/citus_outfuncs.c b/src/backend/distributed/utils/citus_outfuncs.c index abf18aa27..f8bd9bbc7 100644 --- a/src/backend/distributed/utils/citus_outfuncs.c +++ b/src/backend/distributed/utils/citus_outfuncs.c @@ -34,11 +34,7 @@ #include "distributed/metadata_utility.h" #include "lib/stringinfo.h" #include "nodes/plannodes.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" -#else -#include "nodes/relation.h" -#endif #include "utils/datum.h" diff --git a/src/backend/distributed/utils/citus_safe_lib.c b/src/backend/distributed/utils/citus_safe_lib.c index 084601440..d474a140f 100644 --- a/src/backend/distributed/utils/citus_safe_lib.c +++ b/src/backend/distributed/utils/citus_safe_lib.c @@ -23,17 +23,7 @@ #include "distributed/citus_safe_lib.h" #include "lib/stringinfo.h" -/* - * In PG 11 pg_vsnprintf is not exported and compiled in most cases, in that - * case use the copied one from pg11_snprintf.c - * NOTE: Whenever removing this section also remove pg11_snprintf.c - */ -#if PG_VERSION_NUM < PG_VERSION_12 -extern int pg11_vsnprintf(char *str, size_t count, const char *fmt, va_list args); -#define citus_vsnprintf pg11_vsnprintf -#else #define citus_vsnprintf pg_vsnprintf -#endif /* diff --git a/src/backend/distributed/utils/foreign_key_relationship.c b/src/backend/distributed/utils/foreign_key_relationship.c index 465846f81..770a7175d 100644 --- a/src/backend/distributed/utils/foreign_key_relationship.c +++ b/src/backend/distributed/utils/foreign_key_relationship.c @@ -14,14 +14,10 @@ #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/genam.h" -#endif #include "access/htup_details.h" #include "access/stratnum.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/table.h" -#endif #include "catalog/pg_constraint.h" #include "distributed/commands.h" #include "distributed/foreign_key_relationship.h" @@ -37,9 +33,6 @@ #include "common/hashfn.h" #endif #include "utils/memutils.h" -#if PG_VERSION_NUM < PG_VERSION_12 -#include "utils/rel.h" -#endif /* diff --git a/src/backend/distributed/utils/multi_partitioning_utils.c b/src/backend/distributed/utils/multi_partitioning_utils.c index 764545fca..fbe21e68c 100644 --- a/src/backend/distributed/utils/multi_partitioning_utils.c +++ b/src/backend/distributed/utils/multi_partitioning_utils.c @@ -35,9 +35,7 @@ #include "lib/stringinfo.h" #include "nodes/pg_list.h" #include "pgstat.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "partitioning/partdesc.h" -#endif #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" diff --git a/src/backend/distributed/utils/pg11_snprintf.c b/src/backend/distributed/utils/pg11_snprintf.c deleted file mode 100644 index 5d85707d7..000000000 --- a/src/backend/distributed/utils/pg11_snprintf.c +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * src/port/snprintf.c - */ - -#include "c.h" - -#include "distributed/pg_version_constants.h" - - -#include -#ifdef _MSC_VER -#include /* for _isnan */ -#endif -#include -#include -#ifndef WIN32 -#include -#endif -#include - -/* Include this file only for PG11 and only when USE_REPL_SNPRINTF is not set */ -#if PG_VERSION_NUM < PG_VERSION_12 -#ifndef USE_REPL_SNPRINTF -/* - * We used to use the platform's NL_ARGMAX here, but that's a bad idea, - * first because the point of this module is to remove platform dependencies - * not perpetuate them, and second because some platforms use ridiculously - * large values, leading to excessive stack consumption in dopr(). - */ -#define PG_NL_ARGMAX 31 - - -/* - * SNPRINTF, VSNPRINTF and friends - * - * These versions have been grabbed off the net. They have been - * cleaned up to compile properly and support for most of the C99 - * specification has been added. Remaining unimplemented features are: - * - * 1. No locale support: the radix character is always '.' and the ' - * (single quote) format flag is ignored. - * - * 2. No support for the "%n" format specification. - * - * 3. No support for wide characters ("lc" and "ls" formats). - * - * 4. No support for "long double" ("Lf" and related formats). - * - * 5. Space and '#' flags are not implemented. - * - * - * Historically the result values of sprintf/snprintf varied across platforms. - * This implementation now follows the C99 standard: - * - * 1. -1 is returned if an error is detected in the format string, or if - * a write to the target stream fails (as reported by fwrite). Note that - * overrunning snprintf's target buffer is *not* an error. - * - * 2. For successful writes to streams, the actual number of bytes written - * to the stream is returned. - * - * 3. For successful sprintf/snprintf, the number of bytes that would have - * been written to an infinite-size buffer (excluding the trailing '\0') - * is returned. snprintf will truncate its output to fit in the buffer - * (ensuring a trailing '\0' unless count == 0), but this is not reflected - * in the function result. - * - * snprintf buffer overrun can be detected by checking for function result - * greater than or equal to the supplied count. - */ - -/************************************************************** - * Original: - * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 - * A bombproof version of doprnt (dopr) included. - * Sigh. This sort of thing is always nasty do deal with. Note that - * the version here does not include floating point. (now it does ... tgl) - **************************************************************/ - -/* Prevent recursion */ -#undef vsnprintf -#undef snprintf -#undef sprintf -#undef vfprintf -#undef fprintf -#undef printf - -extern int pg11_vsnprintf(char *str, size_t count, const char *fmt, va_list args); -extern int pg11_snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4); -extern int pg11_sprintf(char *str, const char *fmt,...) pg_attribute_printf(2, 3); -extern int pg11_vfprintf(FILE *stream, const char *fmt, va_list args); -extern int pg11_fprintf(FILE *stream, const char *fmt,...) pg_attribute_printf(2, 3); -extern int pg11_printf(const char *fmt,...) pg_attribute_printf(1, 2); - -/* - * Info about where the formatted output is going. - * - * dopr and subroutines will not write at/past bufend, but snprintf - * reserves one byte, ensuring it may place the trailing '\0' there. - * - * In snprintf, we use nchars to count the number of bytes dropped on the - * floor due to buffer overrun. The correct result of snprintf is thus - * (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might - * seem: nchars is the number of emitted bytes that are not in the buffer now, - * either because we sent them to the stream or because we couldn't fit them - * into the buffer to begin with.) - */ -typedef struct -{ - char *bufptr; /* next buffer output position */ - char *bufstart; /* first buffer element */ - char *bufend; /* last+1 buffer element, or NULL */ - /* bufend == NULL is for sprintf, where we assume buf is big enough */ - FILE *stream; /* eventual output destination, or NULL */ - int nchars; /* # chars sent to stream, or dropped */ - bool failed; /* call is a failure; errno is set */ -} PrintfTarget; - -/* - * Info about the type and value of a formatting parameter. Note that we - * don't currently support "long double", "wint_t", or "wchar_t *" data, - * nor the '%n' formatting code; else we'd need more types. Also, at this - * level we need not worry about signed vs unsigned values. - */ -typedef enum -{ - ATYPE_NONE = 0, - ATYPE_INT, - ATYPE_LONG, - ATYPE_LONGLONG, - ATYPE_DOUBLE, - ATYPE_CHARPTR -} PrintfArgType; - -typedef union -{ - int i; - long l; - int64 ll; - double d; - char *cptr; -} PrintfArgValue; - - -static void flushbuffer(PrintfTarget *target); -static void dopr(PrintfTarget *target, const char *format, va_list args); - - -int -pg11_vsnprintf(char *str, size_t count, const char *fmt, va_list args) -{ - PrintfTarget target; - char onebyte[1]; - - /* - * C99 allows the case str == NULL when count == 0. Rather than - * special-casing this situation further down, we substitute a one-byte - * local buffer. Callers cannot tell, since the function result doesn't - * depend on count. - */ - if (count == 0) - { - str = onebyte; - count = 1; - } - target.bufstart = target.bufptr = str; - target.bufend = str + count - 1; - target.stream = NULL; - target.nchars = 0; - target.failed = false; - dopr(&target, fmt, args); - *(target.bufptr) = '\0'; - return target.failed ? -1 : (target.bufptr - target.bufstart - + target.nchars); -} - -int -pg11_snprintf(char *str, size_t count, const char *fmt,...) -{ - int len; - va_list args; - - va_start(args, fmt); - len = pg11_vsnprintf(str, count, fmt, args); - va_end(args); - return len; -} - -static int -pg11_vsprintf(char *str, const char *fmt, va_list args) -{ - PrintfTarget target; - - target.bufstart = target.bufptr = str; - target.bufend = NULL; - target.stream = NULL; - target.nchars = 0; /* not really used in this case */ - target.failed = false; - dopr(&target, fmt, args); - *(target.bufptr) = '\0'; - return target.failed ? -1 : (target.bufptr - target.bufstart - + target.nchars); -} - -int -pg11_sprintf(char *str, const char *fmt,...) -{ - int len; - va_list args; - - va_start(args, fmt); - len = pg11_vsprintf(str, fmt, args); - va_end(args); - return len; -} - -int -pg11_vfprintf(FILE *stream, const char *fmt, va_list args) -{ - PrintfTarget target; - char buffer[1024]; /* size is arbitrary */ - - if (stream == NULL) - { - errno = EINVAL; - return -1; - } - target.bufstart = target.bufptr = buffer; - target.bufend = buffer + sizeof(buffer); /* use the whole buffer */ - target.stream = stream; - target.nchars = 0; - target.failed = false; - dopr(&target, fmt, args); - /* dump any remaining buffer contents */ - flushbuffer(&target); - return target.failed ? -1 : target.nchars; -} - -int -pg11_fprintf(FILE *stream, const char *fmt,...) -{ - int len; - va_list args; - - va_start(args, fmt); - len = pg11_vfprintf(stream, fmt, args); - va_end(args); - return len; -} - -int -pg11_printf(const char *fmt,...) -{ - int len; - va_list args; - - va_start(args, fmt); - len = pg11_vfprintf(stdout, fmt, args); - va_end(args); - return len; -} - -/* - * Attempt to write the entire buffer to target->stream; discard the entire - * buffer in any case. Call this only when target->stream is defined. - */ -static void -flushbuffer(PrintfTarget *target) -{ - size_t nc = target->bufptr - target->bufstart; - - /* - * Don't write anything if we already failed; this is to ensure we - * preserve the original failure's errno. - */ - if (!target->failed && nc > 0) - { - size_t written; - - written = fwrite(target->bufstart, 1, nc, target->stream); - target->nchars += written; - if (written != nc) - target->failed = true; - } - target->bufptr = target->bufstart; -} - - -static void fmtstr(char *value, int leftjust, int minlen, int maxwidth, - int pointflag, PrintfTarget *target); -static void fmtptr(void *value, PrintfTarget *target); -static void fmtint(int64 value, char type, int forcesign, - int leftjust, int minlen, int zpad, int precision, int pointflag, - PrintfTarget *target); -static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target); -static void fmtfloat(double value, char type, int forcesign, - int leftjust, int minlen, int zpad, int precision, int pointflag, - PrintfTarget *target); -static void dostr(const char *str, int slen, PrintfTarget *target); -static void dopr_outch(int c, PrintfTarget *target); -static int adjust_sign(int is_negative, int forcesign, int *signvalue); -static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen); -static void leading_pad(int zpad, int *signvalue, int *padlen, - PrintfTarget *target); -static void trailing_pad(int *padlen, PrintfTarget *target); - - -/* - * dopr(): poor man's version of doprintf - */ -static void -dopr(PrintfTarget *target, const char *format, va_list args) -{ - const char *format_start = format; - int ch; - bool have_dollar; - bool have_non_dollar; - bool have_star; - bool afterstar; - int accum; - int longlongflag; - int longflag; - int pointflag; - int leftjust; - int fieldwidth; - int precision; - int zpad; - int forcesign; - int last_dollar; - int fmtpos; - int cvalue; - int64 numvalue; - double fvalue; - char *strvalue; - int i; - PrintfArgType argtypes[PG_NL_ARGMAX + 1]; - PrintfArgValue argvalues[PG_NL_ARGMAX + 1]; - - /* - * Parse the format string to determine whether there are %n$ format - * specs, and identify the types and order of the format parameters. - */ - have_dollar = have_non_dollar = false; - last_dollar = 0; - MemSet(argtypes, 0, sizeof(argtypes)); - - while ((ch = *format++) != '\0') - { - if (ch != '%') - continue; - longflag = longlongflag = pointflag = 0; - fmtpos = accum = 0; - afterstar = false; -nextch1: - ch = *format++; - if (ch == '\0') - break; /* illegal, but we don't complain */ - switch (ch) - { - case '-': - case '+': - goto nextch1; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - accum = accum * 10 + (ch - '0'); - goto nextch1; - case '.': - pointflag = 1; - accum = 0; - goto nextch1; - case '*': - if (afterstar) - have_non_dollar = true; /* multiple stars */ - afterstar = true; - accum = 0; - goto nextch1; - case '$': - have_dollar = true; - if (accum <= 0 || accum > PG_NL_ARGMAX) - goto bad_format; - if (afterstar) - { - if (argtypes[accum] && - argtypes[accum] != ATYPE_INT) - goto bad_format; - argtypes[accum] = ATYPE_INT; - last_dollar = Max(last_dollar, accum); - afterstar = false; - } - else - fmtpos = accum; - accum = 0; - goto nextch1; - case 'l': - if (longflag) - longlongflag = 1; - else - longflag = 1; - goto nextch1; - case 'z': -#if SIZEOF_SIZE_T == 8 -#ifdef HAVE_LONG_INT_64 - longflag = 1; -#elif defined(HAVE_LONG_LONG_INT_64) - longlongflag = 1; -#else -#error "Don't know how to print 64bit integers" -#endif -#else - /* assume size_t is same size as int */ -#endif - goto nextch1; - case 'h': - case '\'': - /* ignore these */ - goto nextch1; - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - if (fmtpos) - { - PrintfArgType atype; - - if (longlongflag) - atype = ATYPE_LONGLONG; - else if (longflag) - atype = ATYPE_LONG; - else - atype = ATYPE_INT; - if (argtypes[fmtpos] && - argtypes[fmtpos] != atype) - goto bad_format; - argtypes[fmtpos] = atype; - last_dollar = Max(last_dollar, fmtpos); - } - else - have_non_dollar = true; - break; - case 'c': - if (fmtpos) - { - if (argtypes[fmtpos] && - argtypes[fmtpos] != ATYPE_INT) - goto bad_format; - argtypes[fmtpos] = ATYPE_INT; - last_dollar = Max(last_dollar, fmtpos); - } - else - have_non_dollar = true; - break; - case 's': - case 'p': - if (fmtpos) - { - if (argtypes[fmtpos] && - argtypes[fmtpos] != ATYPE_CHARPTR) - goto bad_format; - argtypes[fmtpos] = ATYPE_CHARPTR; - last_dollar = Max(last_dollar, fmtpos); - } - else - have_non_dollar = true; - break; - case 'e': - case 'E': - case 'f': - case 'g': - case 'G': - if (fmtpos) - { - if (argtypes[fmtpos] && - argtypes[fmtpos] != ATYPE_DOUBLE) - goto bad_format; - argtypes[fmtpos] = ATYPE_DOUBLE; - last_dollar = Max(last_dollar, fmtpos); - } - else - have_non_dollar = true; - break; - case '%': - break; - } - - /* - * If we finish the spec with afterstar still set, there's a - * non-dollar star in there. - */ - if (afterstar) - have_non_dollar = true; - } - - /* Per spec, you use either all dollar or all not. */ - if (have_dollar && have_non_dollar) - goto bad_format; - - /* - * In dollar mode, collect the arguments in physical order. - */ - for (i = 1; i <= last_dollar; i++) - { - switch (argtypes[i]) - { - case ATYPE_NONE: - goto bad_format; - case ATYPE_INT: - argvalues[i].i = va_arg(args, int); - break; - case ATYPE_LONG: - argvalues[i].l = va_arg(args, long); - break; - case ATYPE_LONGLONG: - argvalues[i].ll = va_arg(args, int64); - break; - case ATYPE_DOUBLE: - argvalues[i].d = va_arg(args, double); - break; - case ATYPE_CHARPTR: - argvalues[i].cptr = va_arg(args, char *); - break; - } - } - - /* - * At last we can parse the format for real. - */ - format = format_start; - while ((ch = *format++) != '\0') - { - if (target->failed) - break; - - if (ch != '%') - { - dopr_outch(ch, target); - continue; - } - fieldwidth = precision = zpad = leftjust = forcesign = 0; - longflag = longlongflag = pointflag = 0; - fmtpos = accum = 0; - have_star = afterstar = false; -nextch2: - ch = *format++; - if (ch == '\0') - break; /* illegal, but we don't complain */ - switch (ch) - { - case '-': - leftjust = 1; - goto nextch2; - case '+': - forcesign = 1; - goto nextch2; - case '0': - /* set zero padding if no nonzero digits yet */ - if (accum == 0 && !pointflag) - zpad = '0'; - /* FALL THRU */ - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - accum = accum * 10 + (ch - '0'); - goto nextch2; - case '.': - if (have_star) - have_star = false; - else - fieldwidth = accum; - pointflag = 1; - accum = 0; - goto nextch2; - case '*': - if (have_dollar) - { - /* process value after reading n$ */ - afterstar = true; - } - else - { - /* fetch and process value now */ - int starval = va_arg(args, int); - - if (pointflag) - { - precision = starval; - if (precision < 0) - { - precision = 0; - pointflag = 0; - } - } - else - { - fieldwidth = starval; - if (fieldwidth < 0) - { - leftjust = 1; - fieldwidth = -fieldwidth; - } - } - } - have_star = true; - accum = 0; - goto nextch2; - case '$': - if (afterstar) - { - /* fetch and process star value */ - int starval = argvalues[accum].i; - - if (pointflag) - { - precision = starval; - if (precision < 0) - { - precision = 0; - pointflag = 0; - } - } - else - { - fieldwidth = starval; - if (fieldwidth < 0) - { - leftjust = 1; - fieldwidth = -fieldwidth; - } - } - afterstar = false; - } - else - fmtpos = accum; - accum = 0; - goto nextch2; - case 'l': - if (longflag) - longlongflag = 1; - else - longflag = 1; - goto nextch2; - case 'z': -#if SIZEOF_SIZE_T == 8 -#ifdef HAVE_LONG_INT_64 - longflag = 1; -#elif defined(HAVE_LONG_LONG_INT_64) - longlongflag = 1; -#else -#error "Don't know how to print 64bit integers" -#endif -#else - /* assume size_t is same size as int */ -#endif - goto nextch2; - case 'h': - case '\'': - /* ignore these */ - goto nextch2; - case 'd': - case 'i': - if (!have_star) - { - if (pointflag) - precision = accum; - else - fieldwidth = accum; - } - if (have_dollar) - { - if (longlongflag) - numvalue = argvalues[fmtpos].ll; - else if (longflag) - numvalue = argvalues[fmtpos].l; - else - numvalue = argvalues[fmtpos].i; - } - else - { - if (longlongflag) - numvalue = va_arg(args, int64); - else if (longflag) - numvalue = va_arg(args, long); - else - numvalue = va_arg(args, int); - } - fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, - precision, pointflag, target); - break; - case 'o': - case 'u': - case 'x': - case 'X': - if (!have_star) - { - if (pointflag) - precision = accum; - else - fieldwidth = accum; - } - if (have_dollar) - { - if (longlongflag) - numvalue = (uint64) argvalues[fmtpos].ll; - else if (longflag) - numvalue = (unsigned long) argvalues[fmtpos].l; - else - numvalue = (unsigned int) argvalues[fmtpos].i; - } - else - { - if (longlongflag) - numvalue = (uint64) va_arg(args, int64); - else if (longflag) - numvalue = (unsigned long) va_arg(args, long); - else - numvalue = (unsigned int) va_arg(args, int); - } - fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, - precision, pointflag, target); - break; - case 'c': - if (!have_star) - { - if (pointflag) - precision = accum; - else - fieldwidth = accum; - } - if (have_dollar) - cvalue = (unsigned char) argvalues[fmtpos].i; - else - cvalue = (unsigned char) va_arg(args, int); - fmtchar(cvalue, leftjust, fieldwidth, target); - break; - case 's': - if (!have_star) - { - if (pointflag) - precision = accum; - else - fieldwidth = accum; - } - if (have_dollar) - strvalue = argvalues[fmtpos].cptr; - else - strvalue = va_arg(args, char *); - /* Whine if someone tries to print a NULL string */ - Assert(strvalue != NULL); - fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag, - target); - break; - case 'p': - /* fieldwidth/leftjust are ignored ... */ - if (have_dollar) - strvalue = argvalues[fmtpos].cptr; - else - strvalue = va_arg(args, char *); - fmtptr((void *) strvalue, target); - break; - case 'e': - case 'E': - case 'f': - case 'g': - case 'G': - if (!have_star) - { - if (pointflag) - precision = accum; - else - fieldwidth = accum; - } - if (have_dollar) - fvalue = argvalues[fmtpos].d; - else - fvalue = va_arg(args, double); - fmtfloat(fvalue, ch, forcesign, leftjust, - fieldwidth, zpad, - precision, pointflag, - target); - break; - case '%': - dopr_outch('%', target); - break; - } - } - - return; - -bad_format: - errno = EINVAL; - target->failed = true; -} - -static void -fmtstr(char *value, int leftjust, int minlen, int maxwidth, - int pointflag, PrintfTarget *target) -{ - int padlen, - vallen; /* amount to pad */ - - /* - * If a maxwidth (precision) is specified, we must not fetch more bytes - * than that. - */ - if (pointflag) - vallen = strnlen(value, maxwidth); - else - vallen = strlen(value); - - adjust_padlen(minlen, vallen, leftjust, &padlen); - - while (padlen > 0) - { - dopr_outch(' ', target); - --padlen; - } - - dostr(value, vallen, target); - - trailing_pad(&padlen, target); -} - -static void -fmtptr(void *value, PrintfTarget *target) -{ - int vallen; - char convert[64]; - - /* we rely on regular C library's sprintf to do the basic conversion */ - vallen = sprintf(convert, "%p", value); - if (vallen < 0) - target->failed = true; - else - dostr(convert, vallen, target); -} - -static void -fmtint(int64 value, char type, int forcesign, int leftjust, - int minlen, int zpad, int precision, int pointflag, - PrintfTarget *target) -{ - uint64 base; - int dosign; - const char *cvt = "0123456789abcdef"; - int signvalue = 0; - char convert[64]; - int vallen = 0; - int padlen = 0; /* amount to pad */ - int zeropad; /* extra leading zeroes */ - - switch (type) - { - case 'd': - case 'i': - base = 10; - dosign = 1; - break; - case 'o': - base = 8; - dosign = 0; - break; - case 'u': - base = 10; - dosign = 0; - break; - case 'x': - base = 16; - dosign = 0; - break; - case 'X': - cvt = "0123456789ABCDEF"; - base = 16; - dosign = 0; - break; - default: - return; /* keep compiler quiet */ - } - - /* Handle +/- */ - if (dosign && adjust_sign((value < 0), forcesign, &signvalue)) - value = -value; - - /* - * SUS: the result of converting 0 with an explicit precision of 0 is no - * characters - */ - if (value == 0 && pointflag && precision == 0) - vallen = 0; - else - { - /* make integer string */ - uint64 uvalue = (uint64) value; - - do - { - convert[vallen++] = cvt[uvalue % base]; - uvalue = uvalue / base; - } while (uvalue); - } - - zeropad = Max(0, precision - vallen); - - adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen); - - leading_pad(zpad, &signvalue, &padlen, target); - - while (zeropad-- > 0) - dopr_outch('0', target); - - while (vallen > 0) - dopr_outch(convert[--vallen], target); - - trailing_pad(&padlen, target); -} - -static void -fmtchar(int value, int leftjust, int minlen, PrintfTarget *target) -{ - int padlen = 0; /* amount to pad */ - - adjust_padlen(minlen, 1, leftjust, &padlen); - - while (padlen > 0) - { - dopr_outch(' ', target); - --padlen; - } - - dopr_outch(value, target); - - trailing_pad(&padlen, target); -} - -static void -fmtfloat(double value, char type, int forcesign, int leftjust, - int minlen, int zpad, int precision, int pointflag, - PrintfTarget *target) -{ - int signvalue = 0; - int prec; - int vallen; - char fmt[32]; - char convert[1024]; - int zeropadlen = 0; /* amount to pad with zeroes */ - int padlen = 0; /* amount to pad with spaces */ - - /* - * We rely on the regular C library's sprintf to do the basic conversion, - * then handle padding considerations here. - * - * The dynamic range of "double" is about 1E+-308 for IEEE math, and not - * too wildly more than that with other hardware. In "f" format, sprintf - * could therefore generate at most 308 characters to the left of the - * decimal point; while we need to allow the precision to get as high as - * 308+17 to ensure that we don't truncate significant digits from very - * small values. To handle both these extremes, we use a buffer of 1024 - * bytes and limit requested precision to 350 digits; this should prevent - * buffer overrun even with non-IEEE math. If the original precision - * request was more than 350, separately pad with zeroes. - */ - if (precision < 0) /* cover possible overflow of "accum" */ - precision = 0; - prec = Min(precision, 350); - - if (pointflag) - { - if (sprintf(fmt, "%%.%d%c", prec, type) < 0) - goto fail; - zeropadlen = precision - prec; - } - else if (sprintf(fmt, "%%%c", type) < 0) - goto fail; - - if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue)) - value = -value; - - vallen = sprintf(convert, fmt, value); - if (vallen < 0) - goto fail; - - /* If it's infinity or NaN, forget about doing any zero-padding */ - if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1])) - zeropadlen = 0; - - adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen); - - leading_pad(zpad, &signvalue, &padlen, target); - - if (zeropadlen > 0) - { - /* If 'e' or 'E' format, inject zeroes before the exponent */ - char *epos = strrchr(convert, 'e'); - - if (!epos) - epos = strrchr(convert, 'E'); - if (epos) - { - /* pad after exponent */ - dostr(convert, epos - convert, target); - while (zeropadlen-- > 0) - dopr_outch('0', target); - dostr(epos, vallen - (epos - convert), target); - } - else - { - /* no exponent, pad after the digits */ - dostr(convert, vallen, target); - while (zeropadlen-- > 0) - dopr_outch('0', target); - } - } - else - { - /* no zero padding, just emit the number as-is */ - dostr(convert, vallen, target); - } - - trailing_pad(&padlen, target); - return; - -fail: - target->failed = true; -} - -static void -dostr(const char *str, int slen, PrintfTarget *target) -{ - while (slen > 0) - { - int avail; - - if (target->bufend != NULL) - avail = target->bufend - target->bufptr; - else - avail = slen; - if (avail <= 0) - { - /* buffer full, can we dump to stream? */ - if (target->stream == NULL) - { - target->nchars += slen; /* no, lose the data */ - return; - } - flushbuffer(target); - continue; - } - avail = Min(avail, slen); - memmove(target->bufptr, str, avail); - target->bufptr += avail; - str += avail; - slen -= avail; - } -} - -static void -dopr_outch(int c, PrintfTarget *target) -{ - if (target->bufend != NULL && target->bufptr >= target->bufend) - { - /* buffer full, can we dump to stream? */ - if (target->stream == NULL) - { - target->nchars++; /* no, lose the data */ - return; - } - flushbuffer(target); - } - *(target->bufptr++) = c; -} - - -static int -adjust_sign(int is_negative, int forcesign, int *signvalue) -{ - if (is_negative) - { - *signvalue = '-'; - return true; - } - else if (forcesign) - *signvalue = '+'; - return false; -} - - -static void -adjust_padlen(int minlen, int vallen, int leftjust, int *padlen) -{ - *padlen = minlen - vallen; - if (*padlen < 0) - *padlen = 0; - if (leftjust) - *padlen = -(*padlen); -} - - -static void -leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target) -{ - if (*padlen > 0 && zpad) - { - if (*signvalue) - { - dopr_outch(*signvalue, target); - --(*padlen); - *signvalue = 0; - } - while (*padlen > 0) - { - dopr_outch(zpad, target); - --(*padlen); - } - } - while (*padlen > (*signvalue != 0)) - { - dopr_outch(' ', target); - --(*padlen); - } - if (*signvalue) - { - dopr_outch(*signvalue, target); - if (*padlen > 0) - --(*padlen); - else if (*padlen < 0) - ++(*padlen); - } -} - - -static void -trailing_pad(int *padlen, PrintfTarget *target) -{ - while (*padlen < 0) - { - dopr_outch(' ', target); - ++(*padlen); - } -} -#endif /* USE_REPL_SNPRINTF */ -#endif /* PG_VERSION_NUM < PG_VERSION_12 */ diff --git a/src/backend/distributed/worker/worker_data_fetch_protocol.c b/src/backend/distributed/worker/worker_data_fetch_protocol.c index 964790061..823a5035f 100644 --- a/src/backend/distributed/worker/worker_data_fetch_protocol.c +++ b/src/backend/distributed/worker/worker_data_fetch_protocol.c @@ -81,7 +81,6 @@ PG_FUNCTION_INFO_V1(worker_append_table_to_shard); * Following UDFs are stub functions, you can check their comments for more * detail. */ -PG_FUNCTION_INFO_V1(worker_fetch_query_results_file); PG_FUNCTION_INFO_V1(worker_fetch_regular_table); PG_FUNCTION_INFO_V1(worker_fetch_foreign_file); PG_FUNCTION_INFO_V1(master_expire_table_cache); @@ -814,20 +813,6 @@ SetDefElemArg(AlterSeqStmt *statement, const char *name, Node *arg) } -/* - * worker_fetch_query_results_file is a stub UDF to allow the function object - * to be re-created during upgrades. We should keep this around until we drop - * support for Postgres 11, since Postgres 11 is the highest version for which - * this object may have been created. - */ -Datum -worker_fetch_query_results_file(PG_FUNCTION_ARGS) -{ - ereport(DEBUG2, (errmsg("this function is deprecated and no longer is used"))); - PG_RETURN_VOID(); -} - - /* * worker_fetch_regular_table UDF is a stub UDF to install Citus flawlessly. * Otherwise we need to delete them from our sql files, which is confusing diff --git a/src/backend/distributed/worker/worker_merge_protocol.c b/src/backend/distributed/worker/worker_merge_protocol.c index ce8747837..8b1f07870 100644 --- a/src/backend/distributed/worker/worker_merge_protocol.c +++ b/src/backend/distributed/worker/worker_merge_protocol.c @@ -20,10 +20,8 @@ #include "funcapi.h" #include "miscadmin.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "access/genam.h" #include "access/table.h" -#endif #include "access/htup_details.h" #include "access/xact.h" #include "catalog/dependency.h" @@ -266,26 +264,16 @@ worker_merge_files_and_run_query(PG_FUNCTION_ARGS) Datum worker_cleanup_job_schema_cache(PG_FUNCTION_ARGS) { - Relation pgNamespace = NULL; -#if PG_VERSION_NUM >= PG_VERSION_12 - TableScanDesc scanDescriptor = NULL; -#else - HeapScanDesc scanDescriptor = NULL; -#endif ScanKey scanKey = NULL; int scanKeyCount = 0; - HeapTuple heapTuple = NULL; CheckCitusVersion(ERROR); - pgNamespace = table_open(NamespaceRelationId, AccessExclusiveLock); -#if PG_VERSION_NUM >= PG_VERSION_12 - scanDescriptor = table_beginscan_catalog(pgNamespace, scanKeyCount, scanKey); -#else - scanDescriptor = heap_beginscan_catalog(pgNamespace, scanKeyCount, scanKey); -#endif + Relation pgNamespace = table_open(NamespaceRelationId, AccessExclusiveLock); + TableScanDesc scanDescriptor = table_beginscan_catalog(pgNamespace, scanKeyCount, + scanKey); - heapTuple = heap_getnext(scanDescriptor, ForwardScanDirection); + HeapTuple heapTuple = heap_getnext(scanDescriptor, ForwardScanDirection); while (HeapTupleIsValid(heapTuple)) { Form_pg_namespace schemaForm = (Form_pg_namespace) GETSTRUCT(heapTuple); diff --git a/src/include/columnar/columnar_version_compat.h b/src/include/columnar/columnar_version_compat.h index 3a4e21542..48bd2203e 100644 --- a/src/include/columnar/columnar_version_compat.h +++ b/src/include/columnar/columnar_version_compat.h @@ -17,17 +17,6 @@ #define ExplainPropertyLong(qlabel, value, es) \ ExplainPropertyInteger(qlabel, NULL, value, es) -#if PG_VERSION_NUM < 120000 -#define TTS_EMPTY(slot) ((slot)->tts_isempty) -#define ExecForceStoreHeapTuple(tuple, slot, shouldFree) \ - ExecStoreTuple(newTuple, tupleSlot, InvalidBuffer, shouldFree); -#define table_open(r, l) heap_open(r, l) -#define table_close(r, l) heap_close(r, l) -#define TableScanDesc HeapScanDesc -#define table_beginscan heap_beginscan -#define table_endscan heap_endscan -#endif - #if PG_VERSION_NUM < 130000 #define detoast_attr(X) heap_tuple_untoast_attr(X) #endif diff --git a/src/include/distributed/combine_query_planner.h b/src/include/distributed/combine_query_planner.h index d91442f6b..c4702b781 100644 --- a/src/include/distributed/combine_query_planner.h +++ b/src/include/distributed/combine_query_planner.h @@ -16,11 +16,7 @@ #include "nodes/parsenodes.h" #include "nodes/plannodes.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" -#else -#include "nodes/relation.h" -#endif /* Function declarations for building local plans on the coordinator node */ diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 1249cac90..2c0939d75 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -408,7 +408,6 @@ extern List * PreprocessCreateEnumStmt(Node *stmt, const char *queryString, extern List * PostprocessCreateEnumStmt(Node *stmt, const char *queryString); extern List * PreprocessAlterEnumStmt(Node *stmt, const char *queryString, ProcessUtilityContext processUtilityContext); -extern List * PostprocessAlterEnumStmt(Node *stmt, const char *queryString); extern List * PreprocessDropTypeStmt(Node *stmt, const char *queryString, ProcessUtilityContext processUtilityContext); extern List * PreprocessRenameTypeStmt(Node *stmt, const char *queryString, diff --git a/src/include/distributed/distributed_planner.h b/src/include/distributed/distributed_planner.h index bc289ef0c..c91951c67 100644 --- a/src/include/distributed/distributed_planner.h +++ b/src/include/distributed/distributed_planner.h @@ -16,11 +16,7 @@ #include "nodes/plannodes.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" -#else -#include "nodes/relation.h" -#endif #include "distributed/citus_nodes.h" #include "distributed/errormessage.h" diff --git a/src/include/distributed/hash_helpers.h b/src/include/distributed/hash_helpers.h index 5d329e423..4abe18e11 100644 --- a/src/include/distributed/hash_helpers.h +++ b/src/include/distributed/hash_helpers.h @@ -15,26 +15,6 @@ #include "utils/hsearch.h" -/* pg12 includes this exact implementation of hash_combine */ -#if PG_VERSION_NUM < PG_VERSION_12 - -/* - * Combine two hash values, resulting in another hash value, with decent bit - * mixing. - * - * Similar to boost's hash_combine(). - */ -static inline uint32 -hash_combine(uint32 a, uint32 b) -{ - a ^= b + 0x9e3779b9 + (a << 6) + (a >> 2); - return a; -} - - -#endif - - extern void hash_delete_all(HTAB *htab); /* diff --git a/src/include/distributed/multi_physical_planner.h b/src/include/distributed/multi_physical_planner.h index 8acc4b3a4..1fc75f8d2 100644 --- a/src/include/distributed/multi_physical_planner.h +++ b/src/include/distributed/multi_physical_planner.h @@ -58,9 +58,7 @@ typedef enum CitusRTEKind CITUS_RTE_VALUES = RTE_VALUES, /* VALUES (), (), ... */ CITUS_RTE_CTE = RTE_CTE, /* common table expr (WITH list element) */ CITUS_RTE_NAMEDTUPLESTORE = RTE_NAMEDTUPLESTORE, /* tuplestore, e.g. for triggers */ -#if (PG_VERSION_NUM >= PG_VERSION_12) CITUS_RTE_RESULT = RTE_RESULT, /* RTE represents an empty FROM clause */ -#endif CITUS_RTE_SHARD, CITUS_RTE_REMOTE_QUERY } CitusRTEKind; diff --git a/src/include/distributed/pg_version_constants.h b/src/include/distributed/pg_version_constants.h index 68b8ffb72..046beea09 100644 --- a/src/include/distributed/pg_version_constants.h +++ b/src/include/distributed/pg_version_constants.h @@ -11,7 +11,6 @@ #ifndef PG_VERSION_CONSTANTS #define PG_VERSION_CONSTANTS -#define PG_VERSION_11 110000 #define PG_VERSION_12 120000 #define PG_VERSION_13 130000 #define PG_VERSION_14 140000 diff --git a/src/include/distributed/recursive_planning.h b/src/include/distributed/recursive_planning.h index 98d230cb2..e849d7158 100644 --- a/src/include/distributed/recursive_planning.h +++ b/src/include/distributed/recursive_planning.h @@ -16,11 +16,7 @@ #include "distributed/relation_restriction_equivalence.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" -#if PG_VERSION_NUM >= PG_VERSION_12 #include "nodes/pathnodes.h" -#else -#include "nodes/relation.h" -#endif typedef struct RecursivePlanningContextInternal RecursivePlanningContext; diff --git a/src/include/distributed/version_compat.h b/src/include/distributed/version_compat.h index e59734501..01b2e66cb 100644 --- a/src/include/distributed/version_compat.h +++ b/src/include/distributed/version_compat.h @@ -24,9 +24,7 @@ #include "executor/tuptable.h" #include "nodes/parsenodes.h" #include "parser/parse_func.h" -#if (PG_VERSION_NUM >= PG_VERSION_12) #include "optimizer/optimizer.h" -#endif #if (PG_VERSION_NUM >= PG_VERSION_13) #include "tcop/tcopprot.h" @@ -125,87 +123,6 @@ FileCompatFromFileStart(File fileDesc) } -#else /* pre PG12 */ -#define CreateTableSlotForRel(rel) MakeSingleTupleTableSlot(RelationGetDescr(rel)) -#define table_open(r, l) heap_open(r, l) -#define table_openrv(r, l) heap_openrv(r, l) -#define table_openrv_extended(r, l, m) heap_openrv_extended(r, l, m) -#define table_close(r, l) heap_close(r, l) -#define QTW_EXAMINE_RTES_BEFORE QTW_EXAMINE_RTES -#define MakeSingleTupleTableSlotCompat(tupleDesc, tts_opts) \ - MakeSingleTupleTableSlot(tupleDesc) -#define NextCopyFromCompat(cstate, econtext, values, nulls) \ - NextCopyFrom(cstate, econtext, values, nulls, NULL) - -/* - * In PG12 GetSysCacheOid requires an oid column, - * whereas beforehand the oid column was implicit with WITH OIDS - */ -#define GetSysCacheOid1Compat(cacheId, oidcol, key1) \ - GetSysCacheOid1(cacheId, key1) -#define GetSysCacheOid2Compat(cacheId, oidcol, key1, key2) \ - GetSysCacheOid2(cacheId, key1, key2) -#define GetSysCacheOid3Compat(cacheId, oidcol, key1, key2, key3) \ - GetSysCacheOid3(cacheId, key1, key2, key3) -#define GetSysCacheOid4Compat(cacheId, oidcol, key1, key2, key3, key4) \ - GetSysCacheOid4(cacheId, key1, key2, key3, key4) - -#define LOCAL_FCINFO(name, nargs) \ - FunctionCallInfoData name ## data; \ - FunctionCallInfoData *name = &name ## data - -#define fcGetArgValue(fc, n) ((fc)->arg[n]) -#define fcGetArgNull(fc, n) ((fc)->argnull[n]) -#define fcSetArgExt(fc, n, val, is_null) \ - (((fc)->argnull[n] = (is_null)), ((fc)->arg[n] = (val))) - -typedef struct -{ - File fd; -} FileCompat; - -static inline int -FileWriteCompat(FileCompat *file, char *buffer, int amount, uint32 wait_event_info) -{ - return FileWrite(file->fd, buffer, amount, wait_event_info); -} - - -static inline int -FileReadCompat(FileCompat *file, char *buffer, int amount, uint32 wait_event_info) -{ - return FileRead(file->fd, buffer, amount, wait_event_info); -} - - -static inline FileCompat -FileCompatFromFileStart(File fileDesc) -{ - FileCompat fc = { - .fd = fileDesc, - }; - - return fc; -} - - -/* - * postgres 11 equivalent for a function with the same name in postgres 12+. - */ -static inline bool -table_scan_getnextslot(HeapScanDesc scan, ScanDirection dir, TupleTableSlot *slot) -{ - HeapTuple tuple = heap_getnext(scan, ForwardScanDirection); - if (tuple == NULL) - { - return false; - } - - ExecStoreTuple(tuple, slot, InvalidBuffer, false); - return true; -} - - #endif /* PG12 */ #define fcSetArg(fc, n, value) fcSetArgExt(fc, n, value, false) diff --git a/src/include/distributed/worker_protocol.h b/src/include/distributed/worker_protocol.h index 192701fe2..77d2a25cc 100644 --- a/src/include/distributed/worker_protocol.h +++ b/src/include/distributed/worker_protocol.h @@ -144,7 +144,6 @@ extern Node * ParseTreeRawStmt(const char *ddlCommand); /* Function declarations for applying distributed execution primitives */ extern Datum worker_fetch_partition_file(PG_FUNCTION_ARGS); -extern Datum worker_fetch_query_results_file(PG_FUNCTION_ARGS); extern Datum worker_apply_shard_ddl_command(PG_FUNCTION_ARGS); extern Datum worker_range_partition_table(PG_FUNCTION_ARGS); extern Datum worker_hash_partition_table(PG_FUNCTION_ARGS); diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index fb8b9e1fa..77a4ca11a 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -43,12 +43,6 @@ s/"citus_local_table_([0-9]+)_[0-9]+"/"citus_local_table_\1_xxxxxxx"/g # normalize relation oid suffix for the truncate triggers created by citus s/truncate_trigger_[0-9]+/truncate_trigger_xxxxxxx/g -# (citus_table_triggers.sql) -# postgres generates create trigger commands for triggers with: -# "EXECUTE FUNCTION" in pg12 -# "EXECUTE PROCEDURE" in pg11 -s/FOR EACH (ROW|STATEMENT)(.*)EXECUTE PROCEDURE/FOR EACH \1\2EXECUTE FUNCTION/g - # In foreign_key_restriction_enforcement, normalize shard names s/"(on_update_fkey_table_|fkey_)[0-9]+"/"\1xxxxxxx"/g @@ -116,9 +110,6 @@ s/partition ".*" would be violated by some row/partition would be violated by so /.*Peak Memory Usage:.*$/d s/of relation ".*" contains null values/contains null values/g s/of relation "t1" is violated by some row/is violated by some row/g -# can be removed when we remove PG_VERSION_NUM >= 120000 -s/(.*)Output:.*$/\1Output: xxxxxx/g - # intermediate_results s/(ERROR.*)pgsql_job_cache\/([0-9]+_[0-9]+_[0-9]+)\/(.*).data/\1pgsql_job_cache\/xx_x_xxx\/\3.data/g @@ -145,10 +136,6 @@ s/repartitioned_results_[0-9]+/repartitioned_results_xxxxx/g # ignore job id in worker_hash_partition_table s/worker_hash_partition_table \([0-9]+/worker_hash_partition_table \(xxxxxxx/g -# ignore first parameter for citus_extradata_container due to differences between pg11 and pg12 -# can be removed when we remove PG_VERSION_NUM >= 120000 -s/pg_catalog.citus_extradata_container\([0-9]+/pg_catalog.citus_extradata_container\(XXX/g - # ignore referene table replication messages /replicating reference table.*$/d diff --git a/src/test/regress/expected/alter_distributed_table_0.out b/src/test/regress/expected/alter_distributed_table_0.out deleted file mode 100644 index b48c89433..000000000 --- a/src/test/regress/expected/alter_distributed_table_0.out +++ /dev/null @@ -1,900 +0,0 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 11 AS server_version_above_eleven; - server_version_above_eleven ---------------------------------------------------------------------- - f -(1 row) - -\gset -CREATE SCHEMA alter_distributed_table; -SET search_path TO alter_distributed_table; -SET citus.shard_count TO 4; -SET citus.shard_replication_factor TO 1; -CREATE TABLE dist_table (a INT, b INT); -SELECT create_distributed_table ('dist_table', 'a', colocate_with := 'none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO dist_table VALUES (1, 1), (2, 2), (3, 3); -CREATE TABLE colocation_table (a INT, b INT); -SELECT create_distributed_table ('colocation_table', 'a', colocate_with := 'none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -CREATE TABLE colocation_table_2 (a INT, b INT); -SELECT create_distributed_table ('colocation_table_2', 'a', colocate_with := 'none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT table_name, citus_table_type, distribution_column, shard_count FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2'); - table_name | citus_table_type | distribution_column | shard_count ---------------------------------------------------------------------- - colocation_table | distributed | a | 4 - colocation_table_2 | distributed | a | 4 - dist_table | distributed | a | 4 -(3 rows) - -SELECT STRING_AGG(table_name::text, ', ' ORDER BY 1) AS "Colocation Groups" FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2') GROUP BY colocation_id ORDER BY 1; - Colocation Groups ---------------------------------------------------------------------- - colocation_table - colocation_table_2 - dist_table -(3 rows) - --- test altering distribution column -SELECT alter_distributed_table('dist_table', distribution_column := 'b'); -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT table_name, citus_table_type, distribution_column, shard_count FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2'); - table_name | citus_table_type | distribution_column | shard_count ---------------------------------------------------------------------- - colocation_table | distributed | a | 4 - colocation_table_2 | distributed | a | 4 - dist_table | distributed | b | 4 -(3 rows) - -SELECT STRING_AGG(table_name::text, ', ' ORDER BY 1) AS "Colocation Groups" FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2') GROUP BY colocation_id ORDER BY 1; - Colocation Groups ---------------------------------------------------------------------- - colocation_table - colocation_table_2 - dist_table -(3 rows) - --- test altering shard count -SELECT alter_distributed_table('dist_table', shard_count := 6); -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT table_name, citus_table_type, distribution_column, shard_count FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2'); - table_name | citus_table_type | distribution_column | shard_count ---------------------------------------------------------------------- - colocation_table | distributed | a | 4 - colocation_table_2 | distributed | a | 4 - dist_table | distributed | b | 6 -(3 rows) - -SELECT STRING_AGG(table_name::text, ', ' ORDER BY 1) AS "Colocation Groups" FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2') GROUP BY colocation_id ORDER BY 1; - Colocation Groups ---------------------------------------------------------------------- - colocation_table - colocation_table_2 - dist_table -(3 rows) - --- test altering colocation, note that shard count will also change -SELECT alter_distributed_table('dist_table', colocate_with := 'alter_distributed_table.colocation_table'); -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT table_name, citus_table_type, distribution_column, shard_count FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2'); - table_name | citus_table_type | distribution_column | shard_count ---------------------------------------------------------------------- - colocation_table | distributed | a | 4 - colocation_table_2 | distributed | a | 4 - dist_table | distributed | b | 4 -(3 rows) - -SELECT STRING_AGG(table_name::text, ', ' ORDER BY 1) AS "Colocation Groups" FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2') GROUP BY colocation_id ORDER BY 1; - Colocation Groups ---------------------------------------------------------------------- - colocation_table, dist_table - colocation_table_2 -(2 rows) - --- test altering shard count with cascading, note that the colocation will be kept -SELECT alter_distributed_table('dist_table', shard_count := 8, cascade_to_colocated := true); -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table -NOTICE: creating a new table for alter_distributed_table.colocation_table -NOTICE: moving the data of alter_distributed_table.colocation_table -NOTICE: dropping the old alter_distributed_table.colocation_table -NOTICE: renaming the new table to alter_distributed_table.colocation_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT table_name, citus_table_type, distribution_column, shard_count FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2'); - table_name | citus_table_type | distribution_column | shard_count ---------------------------------------------------------------------- - colocation_table | distributed | a | 8 - colocation_table_2 | distributed | a | 4 - dist_table | distributed | b | 8 -(3 rows) - -SELECT STRING_AGG(table_name::text, ', ' ORDER BY 1) AS "Colocation Groups" FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2') GROUP BY colocation_id ORDER BY 1; - Colocation Groups ---------------------------------------------------------------------- - colocation_table, dist_table - colocation_table_2 -(2 rows) - --- test altering shard count without cascading, note that the colocation will be broken -SELECT alter_distributed_table('dist_table', shard_count := 10, cascade_to_colocated := false); -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT table_name, citus_table_type, distribution_column, shard_count FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2'); - table_name | citus_table_type | distribution_column | shard_count ---------------------------------------------------------------------- - colocation_table | distributed | a | 8 - colocation_table_2 | distributed | a | 4 - dist_table | distributed | b | 10 -(3 rows) - -SELECT STRING_AGG(table_name::text, ', ' ORDER BY 1) AS "Colocation Groups" FROM public.citus_tables - WHERE table_name IN ('dist_table', 'colocation_table', 'colocation_table_2') GROUP BY colocation_id ORDER BY 1; - Colocation Groups ---------------------------------------------------------------------- - colocation_table - colocation_table_2 - dist_table -(3 rows) - --- test partitions -CREATE TABLE partitioned_table (id INT, a INT) PARTITION BY RANGE (id); -SELECT create_distributed_table('partitioned_table', 'id', colocate_with := 'none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -CREATE TABLE partitioned_table_1_5 PARTITION OF partitioned_table FOR VALUES FROM (1) TO (5); -CREATE TABLE partitioned_table_6_10 PARTITION OF partitioned_table FOR VALUES FROM (6) TO (10); -INSERT INTO partitioned_table VALUES (2, 12), (7, 2); -SELECT logicalrelid::text FROM pg_dist_partition WHERE logicalrelid::regclass::text LIKE 'partitioned\_table%' ORDER BY 1; - logicalrelid ---------------------------------------------------------------------- - partitioned_table - partitioned_table_1_5 - partitioned_table_6_10 -(3 rows) - -SELECT run_command_on_workers($$SELECT COUNT(*) FROM pg_catalog.pg_class WHERE relname LIKE 'partitioned\_table%'$$); - run_command_on_workers ---------------------------------------------------------------------- - (localhost,57637,t,6) - (localhost,57638,t,6) -(2 rows) - -SELECT inhrelid::regclass::text FROM pg_catalog.pg_inherits WHERE inhparent = 'partitioned_table'::regclass ORDER BY 1; - inhrelid ---------------------------------------------------------------------- - partitioned_table_1_5 - partitioned_table_6_10 -(2 rows) - -SELECT table_name::text, distribution_column, shard_count FROM public.citus_tables WHERE table_name::text LIKE 'partitioned\_table%' ORDER BY 1; - table_name | distribution_column | shard_count ---------------------------------------------------------------------- - partitioned_table | id | 4 - partitioned_table_1_5 | id | 4 - partitioned_table_6_10 | id | 4 -(3 rows) - -SELECT * FROM partitioned_table ORDER BY 1, 2; - id | a ---------------------------------------------------------------------- - 2 | 12 - 7 | 2 -(2 rows) - -SELECT * FROM partitioned_table_1_5 ORDER BY 1, 2; - id | a ---------------------------------------------------------------------- - 2 | 12 -(1 row) - -SELECT * FROM partitioned_table_6_10 ORDER BY 1, 2; - id | a ---------------------------------------------------------------------- - 7 | 2 -(1 row) - --- test altering the parent table -SELECT alter_distributed_table('partitioned_table', shard_count := 10, distribution_column := 'a'); -NOTICE: converting the partitions of alter_distributed_table.partitioned_table -NOTICE: creating a new table for alter_distributed_table.partitioned_table_1_5 -NOTICE: moving the data of alter_distributed_table.partitioned_table_1_5 -NOTICE: dropping the old alter_distributed_table.partitioned_table_1_5 -NOTICE: renaming the new table to alter_distributed_table.partitioned_table_1_5 -NOTICE: creating a new table for alter_distributed_table.partitioned_table_6_10 -NOTICE: moving the data of alter_distributed_table.partitioned_table_6_10 -NOTICE: dropping the old alter_distributed_table.partitioned_table_6_10 -NOTICE: renaming the new table to alter_distributed_table.partitioned_table_6_10 -NOTICE: creating a new table for alter_distributed_table.partitioned_table -NOTICE: dropping the old alter_distributed_table.partitioned_table -NOTICE: renaming the new table to alter_distributed_table.partitioned_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- test altering the partition -SELECT alter_distributed_table('partitioned_table_1_5', shard_count := 10, distribution_column := 'a'); -ERROR: cannot complete operation because table is a partition -HINT: the parent table is "partitioned_table" -SELECT logicalrelid::text FROM pg_dist_partition WHERE logicalrelid::regclass::text LIKE 'partitioned\_table%' ORDER BY 1; - logicalrelid ---------------------------------------------------------------------- - partitioned_table - partitioned_table_1_5 - partitioned_table_6_10 -(3 rows) - -SELECT run_command_on_workers($$SELECT COUNT(*) FROM pg_catalog.pg_class WHERE relname LIKE 'partitioned\_table%'$$); - run_command_on_workers ---------------------------------------------------------------------- - (localhost,57637,t,15) - (localhost,57638,t,15) -(2 rows) - -SELECT inhrelid::regclass::text FROM pg_catalog.pg_inherits WHERE inhparent = 'partitioned_table'::regclass ORDER BY 1; - inhrelid ---------------------------------------------------------------------- - partitioned_table_1_5 - partitioned_table_6_10 -(2 rows) - -SELECT table_name::text, distribution_column, shard_count FROM public.citus_tables WHERE table_name::text LIKE 'partitioned\_table%' ORDER BY 1; - table_name | distribution_column | shard_count ---------------------------------------------------------------------- - partitioned_table | a | 10 - partitioned_table_1_5 | a | 10 - partitioned_table_6_10 | a | 10 -(3 rows) - -SELECT * FROM partitioned_table ORDER BY 1, 2; - id | a ---------------------------------------------------------------------- - 2 | 12 - 7 | 2 -(2 rows) - -SELECT * FROM partitioned_table_1_5 ORDER BY 1, 2; - id | a ---------------------------------------------------------------------- - 2 | 12 -(1 row) - -SELECT * FROM partitioned_table_6_10 ORDER BY 1, 2; - id | a ---------------------------------------------------------------------- - 7 | 2 -(1 row) - --- test references -CREATE TABLE referenced_dist_table (a INT UNIQUE); -CREATE TABLE referenced_ref_table (a INT UNIQUE); -CREATE TABLE table_with_references (a1 INT UNIQUE REFERENCES referenced_dist_table(a), a2 INT REFERENCES referenced_ref_table(a)); -CREATE TABLE referencing_dist_table (a INT REFERENCES table_with_references(a1)); -SELECT create_distributed_table('referenced_dist_table', 'a', colocate_with:='none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_reference_table('referenced_ref_table'); - create_reference_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('table_with_references', 'a1', colocate_with:='referenced_dist_table'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('referencing_dist_table', 'a', colocate_with:='referenced_dist_table'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SET client_min_messages TO WARNING; -SELECT conrelid::regclass::text AS "Referencing Table", pg_get_constraintdef(oid, true) AS "Definition" FROM pg_constraint - WHERE (conrelid::regclass::text = 'table_with_references' OR confrelid::regclass::text = 'table_with_references') AND contype = 'f' ORDER BY 1; - Referencing Table | Definition ---------------------------------------------------------------------- - referencing_dist_table | FOREIGN KEY (a) REFERENCES table_with_references(a1) - table_with_references | FOREIGN KEY (a1) REFERENCES referenced_dist_table(a) - table_with_references | FOREIGN KEY (a2) REFERENCES referenced_ref_table(a) -(3 rows) - -SELECT alter_distributed_table('table_with_references', shard_count := 12, cascade_to_colocated := true); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT conrelid::regclass::text AS "Referencing Table", pg_get_constraintdef(oid, true) AS "Definition" FROM pg_constraint - WHERE (conrelid::regclass::text = 'table_with_references' OR confrelid::regclass::text = 'table_with_references') AND contype = 'f' ORDER BY 1; - Referencing Table | Definition ---------------------------------------------------------------------- - referencing_dist_table | FOREIGN KEY (a) REFERENCES table_with_references(a1) - table_with_references | FOREIGN KEY (a2) REFERENCES referenced_ref_table(a) - table_with_references | FOREIGN KEY (a1) REFERENCES referenced_dist_table(a) -(3 rows) - -SELECT alter_distributed_table('table_with_references', shard_count := 10, cascade_to_colocated := false); -WARNING: foreign key table_with_references_a1_fkey will be dropped -WARNING: foreign key referencing_dist_table_a_fkey will be dropped - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT conrelid::regclass::text AS "Referencing Table", pg_get_constraintdef(oid, true) AS "Definition" FROM pg_constraint - WHERE (conrelid::regclass::text = 'table_with_references' OR confrelid::regclass::text = 'table_with_references') AND contype = 'f' ORDER BY 1; - Referencing Table | Definition ---------------------------------------------------------------------- - table_with_references | FOREIGN KEY (a2) REFERENCES referenced_ref_table(a) -(1 row) - --- check when multi shard modify mode is set to sequential -SELECT alter_distributed_table('referenced_dist_table', colocate_with:='none'); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -CREATE TABLE ref_to_dist_table(a INT REFERENCES referenced_dist_table(a)); -CREATE TABLE ref_to_ref_table(a INT REFERENCES referenced_ref_table(a)); -SELECT create_distributed_table('ref_to_dist_table', 'a', colocate_with:='referenced_dist_table'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('ref_to_ref_table', 'a', colocate_with:='none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- alter a table referencing a reference table -SELECT alter_distributed_table('ref_to_ref_table', shard_count:=6); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- let's create a table that is not colocated with a table that references a reference table -CREATE TABLE col_with_ref_to_dist (a INT); -SELECT create_distributed_table('col_with_ref_to_dist', 'a', colocate_with:='ref_to_dist_table'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- and create a table colocated with a table that references a reference table -CREATE TABLE col_with_ref_to_ref (a INT); -SELECT alter_distributed_table('ref_to_ref_table', colocate_with:='none'); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('col_with_ref_to_ref', 'a', colocate_with:='ref_to_ref_table'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- alter a table colocated with a table referencing a reference table with cascading -SELECT alter_distributed_table('col_with_ref_to_ref', shard_count:=8, cascade_to_colocated:=true); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- alter a table colocated with a table referencing a reference table without cascading -SELECT alter_distributed_table('col_with_ref_to_ref', shard_count:=10, cascade_to_colocated:=false); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- alter a table not colocated with a table referencing a reference table with cascading -SELECT alter_distributed_table('col_with_ref_to_dist', shard_count:=6, cascade_to_colocated:=true); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -\if :server_version_above_eleven --- test altering columnar table -CREATE TABLE columnar_table (a INT) USING columnar; -SELECT create_distributed_table('columnar_table', 'a', colocate_with:='none'); -SELECT table_name::text, shard_count, access_method FROM public.citus_tables WHERE table_name::text = 'columnar_table'; -SELECT alter_distributed_table('columnar_table', shard_count:=6); -SELECT table_name::text, shard_count, access_method FROM public.citus_tables WHERE table_name::text = 'columnar_table'; -\endif --- test with metadata sync -SET citus.replication_model TO 'streaming'; -SELECT start_metadata_sync_to_node('localhost', :worker_1_port); - start_metadata_sync_to_node ---------------------------------------------------------------------- - -(1 row) - -CREATE TABLE metadata_sync_table (a BIGSERIAL); -SELECT create_distributed_table('metadata_sync_table', 'a', colocate_with:='none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('metadata_sync_table', shard_count:=6); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('metadata_sync_table', shard_count:=8); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT table_name, shard_count FROM public.citus_tables WHERE table_name::text = 'metadata_sync_table'; - table_name | shard_count ---------------------------------------------------------------------- - metadata_sync_table | 8 -(1 row) - -SET citus.replication_model TO DEFAULT; -SELECT stop_metadata_sync_to_node('localhost', :worker_1_port); - stop_metadata_sync_to_node ---------------------------------------------------------------------- - -(1 row) - --- test complex cascade operations -CREATE TABLE cas_1 (a INT UNIQUE); -CREATE TABLE cas_2 (a INT UNIQUE); -CREATE TABLE cas_3 (a INT UNIQUE); -CREATE TABLE cas_4 (a INT UNIQUE); -CREATE TABLE cas_par (a INT UNIQUE) PARTITION BY RANGE(a); -CREATE TABLE cas_par_1 PARTITION OF cas_par FOR VALUES FROM (1) TO (4); -CREATE TABLE cas_par_2 PARTITION OF cas_par FOR VALUES FROM (5) TO (8); -CREATE TABLE cas_col (a INT UNIQUE); --- add foreign keys from and to partitions -ALTER TABLE cas_par_1 ADD CONSTRAINT fkey_from_par_1 FOREIGN KEY (a) REFERENCES cas_1(a); -ALTER TABLE cas_2 ADD CONSTRAINT fkey_to_par_1 FOREIGN KEY (a) REFERENCES cas_par_1(a); -ALTER TABLE cas_par ADD CONSTRAINT fkey_from_par FOREIGN KEY (a) REFERENCES cas_3(a); -ALTER TABLE cas_4 ADD CONSTRAINT fkey_to_par FOREIGN KEY (a) REFERENCES cas_par(a); -ERROR: cannot reference partitioned table "cas_par" --- distribute all the tables -SELECT create_distributed_table('cas_1', 'a', colocate_with:='none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('cas_3', 'a', colocate_with:='cas_1'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('cas_par', 'a', colocate_with:='cas_1'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('cas_2', 'a', colocate_with:='cas_1'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('cas_4', 'a', colocate_with:='cas_1'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('cas_col', 'a', colocate_with:='cas_1'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT conrelid::regclass::text AS "Referencing Table", pg_get_constraintdef(oid, true) AS "Definition" FROM pg_constraint - WHERE (conrelid::regclass::text = 'cas_par_1' OR confrelid::regclass::text = 'cas_par_1') ORDER BY 1, 2; - Referencing Table | Definition ---------------------------------------------------------------------- - cas_2 | FOREIGN KEY (a) REFERENCES cas_par_1(a) - cas_par_1 | FOREIGN KEY (a) REFERENCES cas_1(a) - cas_par_1 | FOREIGN KEY (a) REFERENCES cas_3(a) - cas_par_1 | UNIQUE (a) -(4 rows) - -SELECT inhrelid::regclass::text FROM pg_catalog.pg_inherits WHERE inhparent = 'cas_par'::regclass ORDER BY 1; - inhrelid ---------------------------------------------------------------------- - cas_par_1 - cas_par_2 -(2 rows) - --- alter the cas_col and cascade the change -SELECT alter_distributed_table('cas_col', shard_count:=6, cascade_to_colocated:=true); - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT conrelid::regclass::text AS "Referencing Table", pg_get_constraintdef(oid, true) AS "Definition" FROM pg_constraint - WHERE (conrelid::regclass::text = 'cas_par_1' OR confrelid::regclass::text = 'cas_par_1') ORDER BY 1, 2; - Referencing Table | Definition ---------------------------------------------------------------------- - cas_2 | FOREIGN KEY (a) REFERENCES cas_par_1(a) - cas_par_1 | FOREIGN KEY (a) REFERENCES cas_1(a) - cas_par_1 | FOREIGN KEY (a) REFERENCES cas_3(a) - cas_par_1 | UNIQUE (a) -(4 rows) - -SELECT inhrelid::regclass::text FROM pg_catalog.pg_inherits WHERE inhparent = 'cas_par'::regclass ORDER BY 1; - inhrelid ---------------------------------------------------------------------- - cas_par_1 - cas_par_2 -(2 rows) - -SET client_min_messages TO DEFAULT; --- test changing dist column and colocating partitioned table without changing shard count -CREATE TABLE col_table (a INT); -SELECT create_distributed_table('col_table', 'a', colocate_with:='none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -CREATE TABLE par_table (a BIGINT, b INT) PARTITION BY RANGE (a); -SELECT create_distributed_table('par_table', 'a', colocate_with:='none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -CREATE TABLE par_table_1 (a BIGINT, b INT); -SELECT create_distributed_table('par_table_1', 'a', colocate_with:='par_table'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -ALTER TABLE par_table ATTACH PARTITION par_table_1 FOR VALUES FROM (1) TO (5); -SELECT alter_distributed_table('par_table', distribution_column:='b', colocate_with:='col_table'); -NOTICE: converting the partitions of alter_distributed_table.par_table -NOTICE: creating a new table for alter_distributed_table.par_table_1 -NOTICE: moving the data of alter_distributed_table.par_table_1 -NOTICE: dropping the old alter_distributed_table.par_table_1 -NOTICE: renaming the new table to alter_distributed_table.par_table_1 -NOTICE: creating a new table for alter_distributed_table.par_table -NOTICE: dropping the old alter_distributed_table.par_table -NOTICE: renaming the new table to alter_distributed_table.par_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- test messages --- test nothing to change -SELECT alter_distributed_table('dist_table'); -ERROR: you have to specify at least one of the distribution_column, shard_count or colocate_with parameters -SELECT alter_distributed_table('dist_table', cascade_to_colocated := false); -ERROR: you have to specify at least one of the distribution_column, shard_count or colocate_with parameters --- no operation UDF calls -SELECT alter_distributed_table('dist_table', distribution_column := 'b'); -ERROR: this call doesn't change any properties of the table -HINT: check citus_tables view to see current properties of the table -SELECT alter_distributed_table('dist_table', shard_count := 10); -ERROR: this call doesn't change any properties of the table -HINT: check citus_tables view to see current properties of the table --- first colocate the tables, then try to re-colococate -SELECT alter_distributed_table('dist_table', colocate_with := 'colocation_table'); -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('dist_table', colocate_with := 'colocation_table'); -ERROR: this call doesn't change any properties of the table -HINT: check citus_tables view to see current properties of the table --- test some changes while keeping others same --- shouldn't error but should have notices about no-change parameters -SELECT alter_distributed_table('dist_table', distribution_column:='b', shard_count:=4, cascade_to_colocated:=false); -NOTICE: table is already distributed by b -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('dist_table', shard_count:=4, colocate_with:='colocation_table_2'); -NOTICE: shard count of the table is already 4 -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('dist_table', colocate_with:='colocation_table_2', distribution_column:='a'); -NOTICE: table is already colocated with colocation_table_2 -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- test cascading distribution column, should error -SELECT alter_distributed_table('dist_table', distribution_column := 'b', cascade_to_colocated := true); -ERROR: distribution_column cannot be cascaded to colocated tables -SELECT alter_distributed_table('dist_table', distribution_column := 'b', shard_count:=12, colocate_with:='colocation_table_2', cascade_to_colocated := true); -ERROR: distribution_column cannot be cascaded to colocated tables --- test nothing to cascade -SELECT alter_distributed_table('dist_table', cascade_to_colocated := true); -ERROR: shard_count or colocate_with is necessary for cascading to colocated tables --- test cascading colocate_with := 'none' -SELECT alter_distributed_table('dist_table', colocate_with := 'none', cascade_to_colocated := true); -ERROR: colocate_with := 'none' cannot be cascaded to colocated tables --- test changing shard count of a colocated table without cascade_to_colocated, should error -SELECT alter_distributed_table('dist_table', shard_count := 14); -ERROR: cascade_to_colocated parameter is necessary -DETAIL: this table is colocated with some other tables -HINT: cascade_to_colocated := false will break the current colocation, cascade_to_colocated := true will change the shard count of colocated tables too. --- test changing shard count of a non-colocated table without cascade_to_colocated, shouldn't error -SELECT alter_distributed_table('dist_table', colocate_with := 'none'); -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('dist_table', shard_count := 14); -NOTICE: creating a new table for alter_distributed_table.dist_table -NOTICE: moving the data of alter_distributed_table.dist_table -NOTICE: dropping the old alter_distributed_table.dist_table -NOTICE: renaming the new table to alter_distributed_table.dist_table - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- test altering a table into colocating with a table but giving a different shard count -SELECT alter_distributed_table('dist_table', colocate_with := 'colocation_table', shard_count := 16); -ERROR: shard_count cannot be different than the shard count of the table in colocate_with -HINT: if no shard_count is specified shard count will be same with colocate_with table's --- test colocation with distribution columns with different data types -CREATE TABLE different_type_table (a TEXT); -SELECT create_distributed_table('different_type_table', 'a'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('dist_table', colocate_with := 'different_type_table'); -ERROR: cannot colocate with different_type_table because data type of its distribution column is different than dist_table -SELECT alter_distributed_table('dist_table', distribution_column := 'a', colocate_with := 'different_type_table'); -ERROR: cannot colocate with different_type_table and change distribution column to a because data type of column a is different then the distribution column of the different_type_table --- test shard_count := 0 -SELECT alter_distributed_table('dist_table', shard_count := 0); -ERROR: shard_count cannot be 0 -HINT: if you no longer want this to be a distributed table you can try undistribute_table() function --- test colocating with non-distributed table -CREATE TABLE reference_table (a INT); -SELECT create_reference_table('reference_table'); - create_reference_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('dist_table', colocate_with:='reference_table'); -ERROR: cannot colocate with reference_table because it is not a distributed table --- test append table -CREATE TABLE append_table (a INT); -SELECT create_distributed_table('append_table', 'a', 'append'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('append_table', shard_count:=6); -ERROR: relation append_table should be a hash distributed table --- test keeping dependent materialized views -CREATE TABLE mat_view_test (a int, b int); -SELECT create_distributed_table('mat_view_test', 'a'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO mat_view_test VALUES (1,1), (2,2); -CREATE MATERIALIZED VIEW mat_view AS SELECT * FROM mat_view_test; -SELECT alter_distributed_table('mat_view_test', shard_count := 5, cascade_to_colocated := false); -NOTICE: creating a new table for alter_distributed_table.mat_view_test -NOTICE: moving the data of alter_distributed_table.mat_view_test -NOTICE: dropping the old alter_distributed_table.mat_view_test -NOTICE: drop cascades to materialized view mat_view -CONTEXT: SQL statement "DROP TABLE alter_distributed_table.mat_view_test CASCADE" -NOTICE: renaming the new table to alter_distributed_table.mat_view_test - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT * FROM mat_view ORDER BY a; - a | b ---------------------------------------------------------------------- - 1 | 1 - 2 | 2 -(2 rows) - --- test long table names -SET client_min_messages TO DEBUG1; -CREATE TABLE abcde_0123456789012345678901234567890123456789012345678901234567890123456789 (x int, y int); -NOTICE: identifier "abcde_0123456789012345678901234567890123456789012345678901234567890123456789" will be truncated to "abcde_012345678901234567890123456789012345678901234567890123456" -SELECT create_distributed_table('abcde_0123456789012345678901234567890123456789012345678901234567890123456789', 'x'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT alter_distributed_table('abcde_0123456789012345678901234567890123456789012345678901234567890123456789', distribution_column := 'y'); -DEBUG: the name of the shard (abcde_01234567890123456789012345678901234567890_f7ff6612_xxxxxx) for relation (abcde_012345678901234567890123456789012345678901234567890123456) is too long, switching to sequential and local execution mode to prevent self deadlocks -NOTICE: creating a new table for alter_distributed_table.abcde_012345678901234567890123456789012345678901234567890123456 -NOTICE: moving the data of alter_distributed_table.abcde_012345678901234567890123456789012345678901234567890123456 -DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match -DETAIL: The target table's partition column should correspond to a partition column in the subquery. -CONTEXT: SQL statement "INSERT INTO alter_distributed_table.abcde_0123456789012345678901234567890123456_f7ff6612_4160710162 (x,y) SELECT x,y FROM alter_distributed_table.abcde_012345678901234567890123456789012345678901234567890123456" -DEBUG: performing repartitioned INSERT ... SELECT -CONTEXT: SQL statement "INSERT INTO alter_distributed_table.abcde_0123456789012345678901234567890123456_f7ff6612_4160710162 (x,y) SELECT x,y FROM alter_distributed_table.abcde_012345678901234567890123456789012345678901234567890123456" -NOTICE: dropping the old alter_distributed_table.abcde_012345678901234567890123456789012345678901234567890123456 -CONTEXT: SQL statement "DROP TABLE alter_distributed_table.abcde_012345678901234567890123456789012345678901234567890123456 CASCADE" -NOTICE: renaming the new table to alter_distributed_table.abcde_012345678901234567890123456789012345678901234567890123456 -DEBUG: the name of the shard (abcde_01234567890123456789012345678901234567890_f7ff6612_xxxxxx) for relation (abcde_012345678901234567890123456789012345678901234567890123456) is too long, switching to sequential and local execution mode to prevent self deadlocks -CONTEXT: SQL statement "ALTER TABLE alter_distributed_table.abcde_0123456789012345678901234567890123456_f7ff6612_4160710162 RENAME TO abcde_012345678901234567890123456789012345678901234567890123456" - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -RESET client_min_messages; --- test long partitioned table names -CREATE TABLE partition_lengths -( - tenant_id integer NOT NULL, - timeperiod timestamp without time zone NOT NULL, - inserted_utc timestamp without time zone NOT NULL DEFAULT now() -) PARTITION BY RANGE (timeperiod); -SELECT create_distributed_table('partition_lengths', 'tenant_id'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -CREATE TABLE partition_lengths_p2020_09_28_12345678901234567890123456789012345678901234567890 PARTITION OF partition_lengths FOR VALUES FROM ('2020-09-28 00:00:00') TO ('2020-09-29 00:00:00'); -NOTICE: identifier "partition_lengths_p2020_09_28_12345678901234567890123456789012345678901234567890" will be truncated to "partition_lengths_p2020_09_28_123456789012345678901234567890123" --- verify alter_distributed_table works with long partition names -SELECT alter_distributed_table('partition_lengths', shard_count := 29, cascade_to_colocated := false); -NOTICE: converting the partitions of alter_distributed_table.partition_lengths -NOTICE: creating a new table for alter_distributed_table.partition_lengths_p2020_09_28_123456789012345678901234567890123 -NOTICE: moving the data of alter_distributed_table.partition_lengths_p2020_09_28_123456789012345678901234567890123 -NOTICE: dropping the old alter_distributed_table.partition_lengths_p2020_09_28_123456789012345678901234567890123 -NOTICE: renaming the new table to alter_distributed_table.partition_lengths_p2020_09_28_123456789012345678901234567890123 -NOTICE: creating a new table for alter_distributed_table.partition_lengths -NOTICE: dropping the old alter_distributed_table.partition_lengths -NOTICE: renaming the new table to alter_distributed_table.partition_lengths - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - --- test long partition table names -ALTER TABLE partition_lengths_p2020_09_28_12345678901234567890123456789012345678901234567890 RENAME TO partition_lengths_p2020_09_28; -NOTICE: identifier "partition_lengths_p2020_09_28_12345678901234567890123456789012345678901234567890" will be truncated to "partition_lengths_p2020_09_28_123456789012345678901234567890123" -ALTER TABLE partition_lengths RENAME TO partition_lengths_12345678901234567890123456789012345678901234567890; -NOTICE: identifier "partition_lengths_12345678901234567890123456789012345678901234567890" will be truncated to "partition_lengths_123456789012345678901234567890123456789012345" --- verify alter_distributed_table works with long partitioned table names -SELECT alter_distributed_table('partition_lengths_12345678901234567890123456789012345678901234567890', shard_count := 17, cascade_to_colocated := false); -NOTICE: converting the partitions of alter_distributed_table.partition_lengths_123456789012345678901234567890123456789012345 -NOTICE: creating a new table for alter_distributed_table.partition_lengths_p2020_09_28 -NOTICE: moving the data of alter_distributed_table.partition_lengths_p2020_09_28 -NOTICE: dropping the old alter_distributed_table.partition_lengths_p2020_09_28 -NOTICE: renaming the new table to alter_distributed_table.partition_lengths_p2020_09_28 -NOTICE: creating a new table for alter_distributed_table.partition_lengths_123456789012345678901234567890123456789012345 -NOTICE: dropping the old alter_distributed_table.partition_lengths_123456789012345678901234567890123456789012345 -NOTICE: renaming the new table to alter_distributed_table.partition_lengths_123456789012345678901234567890123456789012345 - alter_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SET client_min_messages TO WARNING; -DROP SCHEMA alter_distributed_table CASCADE; diff --git a/src/test/regress/expected/alter_table_set_access_method_0.out b/src/test/regress/expected/alter_table_set_access_method_0.out deleted file mode 100644 index 032e79aa7..000000000 --- a/src/test/regress/expected/alter_table_set_access_method_0.out +++ /dev/null @@ -1,12 +0,0 @@ --- test for Postgres version --- should error before PG12 -CREATE TABLE alter_am_pg_version_table (a INT); -SELECT alter_table_set_access_method('alter_am_pg_version_table', 'columnar'); -ERROR: table access methods are not supported for Postgres versions earlier than 12 -DROP TABLE alter_am_pg_version_table; -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 11 AS server_version_above_eleven -\gset -\if :server_version_above_eleven -\else -\q diff --git a/src/test/regress/expected/coordinator_shouldhaveshards.out b/src/test/regress/expected/coordinator_shouldhaveshards.out index 6a25138c6..dcae09dbe 100644 --- a/src/test/regress/expected/coordinator_shouldhaveshards.out +++ b/src/test/regress/expected/coordinator_shouldhaveshards.out @@ -335,7 +335,6 @@ NOTICE: executing the command locally: SELECT ref.a, ref.b, local.x, local.y FR (1 row) ROLLBACK; -set citus.enable_cte_inlining to off; BEGIN; SELECT count(*) FROM test; NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503000 test WHERE true @@ -348,11 +347,11 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinato -- we wont see the modifying cte in this query because we will use local execution and -- in postgres we wouldn't see this modifying cte, so it is consistent with postgres. WITH a AS (SELECT count(*) FROM test), b AS (INSERT INTO local VALUES (3,2) RETURNING *), c AS (INSERT INTO ref VALUES (3,2) RETURNING *), d AS (SELECT count(*) FROM ref JOIN local ON (a = x)) SELECT * FROM a, b, c, d ORDER BY x,y,a,b; +NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 (a, b) VALUES (3, 2) RETURNING a, b NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503000 test WHERE true NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503003 test WHERE true -NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 (a, b) VALUES (3, 2) RETURNING a, b -NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN coordinator_shouldhaveshards.local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) -NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b +NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN (SELECT local_1.x, NULL::integer AS y FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) local_1) local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) +NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_5'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b count | x | y | a | b | count --------------------------------------------------------------------- 100 | 3 | 2 | 3 | 2 | 0 @@ -369,11 +368,11 @@ NOTICE: executing the command locally: SELECT ref.a, ref.b, local.x, local.y FR -- we wont see the modifying cte in this query because we will use local execution and -- in postgres we wouldn't see this modifying cte, so it is consistent with postgres. WITH a AS (SELECT count(*) FROM test), b AS (INSERT INTO local VALUES (3,2) RETURNING *), c AS (INSERT INTO ref VALUES (3,2) RETURNING *), d AS (SELECT count(*) FROM ref JOIN local ON (a = x)) SELECT * FROM a, b, c, d ORDER BY x,y,a,b; +NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 (a, b) VALUES (3, 2) RETURNING a, b NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503000 test WHERE true NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503003 test WHERE true -NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 (a, b) VALUES (3, 2) RETURNING a, b -NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN coordinator_shouldhaveshards.local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) -NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b +NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN (SELECT local_1.x, NULL::integer AS y FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) local_1) local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) +NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_5'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b count | x | y | a | b | count --------------------------------------------------------------------- 100 | 3 | 2 | 3 | 2 | 0 @@ -384,11 +383,11 @@ BEGIN; -- we wont see the modifying cte in this query because we will use local execution and -- in postgres we wouldn't see this modifying cte, so it is consistent with postgres. WITH a AS (SELECT count(*) FROM test), b AS (INSERT INTO local VALUES (3,2) RETURNING *), c AS (INSERT INTO ref VALUES (3,2) RETURNING *), d AS (SELECT count(*) FROM ref JOIN local ON (a = x)) SELECT * FROM a, b, c, d ORDER BY x,y,a,b; +NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 (a, b) VALUES (3, 2) RETURNING a, b NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503000 test WHERE true NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503003 test WHERE true -NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 (a, b) VALUES (3, 2) RETURNING a, b -NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN coordinator_shouldhaveshards.local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) -NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b +NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN (SELECT local_1.x, NULL::integer AS y FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) local_1) local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) +NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_5'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b count | x | y | a | b | count --------------------------------------------------------------------- 100 | 3 | 2 | 3 | 2 | 0 @@ -399,12 +398,12 @@ BEGIN; -- we wont see the modifying cte in this query because we will use local execution and -- in postgres we wouldn't see this modifying cte, so it is consistent with postgres. WITH a AS (SELECT count(*) FROM test), b AS (INSERT INTO local VALUES (3,2) RETURNING *), c AS (INSERT INTO ref SELECT *,* FROM generate_series(1,10) RETURNING *), d AS (SELECT count(*) FROM ref JOIN local ON (a = x)) SELECT * FROM a, b, c, d ORDER BY x,y,a,b; -NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503000 test WHERE true -NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503003 test WHERE true NOTICE: executing the copy locally for colocated file with shard xxxxx NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 AS citus_table_alias (a, b) SELECT a, b FROM read_intermediate_result('insert_select_XXX_1503020'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer) RETURNING citus_table_alias.a, citus_table_alias.b -NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN coordinator_shouldhaveshards.local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) -NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b +NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503000 test WHERE true +NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503003 test WHERE true +NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN (SELECT local_1.x, NULL::integer AS y FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) local_1) local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) +NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_5'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b count | x | y | a | b | count --------------------------------------------------------------------- 100 | 3 | 2 | 1 | 1 | 0 @@ -434,11 +433,11 @@ NOTICE: executing the command locally: SELECT ref.a, ref.b, local.x, local.y FR -- we wont see the modifying cte in this query because we will use local execution and -- in postgres we wouldn't see this modifying cte, so it is consistent with postgres. WITH a AS (SELECT count(*) FROM test), b AS (INSERT INTO local VALUES (3,2) RETURNING *), c AS (INSERT INTO ref VALUES (3,2) RETURNING *), d AS (SELECT count(*) FROM ref JOIN local ON (a = x)) SELECT * FROM a, b, c, d ORDER BY x,y,a,b; +NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 (a, b) VALUES (3, 2) RETURNING a, b NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503000 test WHERE true NOTICE: executing the command locally: SELECT count(*) AS count FROM coordinator_shouldhaveshards.test_1503003 test WHERE true -NOTICE: executing the command locally: INSERT INTO coordinator_shouldhaveshards.ref_1503020 (a, b) VALUES (3, 2) RETURNING a, b -NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN coordinator_shouldhaveshards.local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) -NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b +NOTICE: executing the command locally: SELECT count(*) AS count FROM (coordinator_shouldhaveshards.ref_1503020 ref JOIN (SELECT local_1.x, NULL::integer AS y FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) local_1) local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) +NOTICE: executing the command locally: SELECT a.count, b.x, b.y, c.a, c.b, d.count FROM (SELECT intermediate_result.count FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) a, (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) b, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) c, (SELECT intermediate_result.count FROM read_intermediate_result('XXX_5'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) d ORDER BY b.x, b.y, c.a, c.b count | x | y | a | b | count --------------------------------------------------------------------- 100 | 3 | 2 | 3 | 2 | 1 @@ -489,7 +488,7 @@ NOTICE: executing the command locally: SELECT ref.a, ref.b, local.x, local.y, c -- full router query with CTE and local WITH cte_1 AS (SELECT * FROM ref LIMIT 1) SELECT * FROM ref JOIN local ON (a = x) JOIN cte_1 ON (local.x = cte_1.a); -NOTICE: executing the command locally: WITH cte_1 AS (SELECT ref_1.a, ref_1.b FROM coordinator_shouldhaveshards.ref_1503020 ref_1 LIMIT 1) SELECT ref.a, ref.b, local.x, local.y, cte_1.a, cte_1.b FROM ((coordinator_shouldhaveshards.ref_1503020 ref JOIN coordinator_shouldhaveshards.local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) JOIN cte_1 ON ((local.x OPERATOR(pg_catalog.=) cte_1.a))) +NOTICE: executing the command locally: SELECT ref.a, ref.b, local.x, local.y, cte_1.a, cte_1.b FROM ((coordinator_shouldhaveshards.ref_1503020 ref JOIN coordinator_shouldhaveshards.local ON ((ref.a OPERATOR(pg_catalog.=) local.x))) JOIN (SELECT ref_1.a, ref_1.b FROM coordinator_shouldhaveshards.ref_1503020 ref_1 LIMIT 1) cte_1 ON ((local.x OPERATOR(pg_catalog.=) cte_1.a))) a | b | x | y | a | b --------------------------------------------------------------------- 1 | 2 | 1 | 2 | 1 | 2 @@ -531,7 +530,6 @@ NOTICE: executing the command locally: SELECT worker_apply_shard_ddl_command (1 (1 row) ROLLBACK; -RESET citus.enable_cte_inlining; CREATE table ref_table(x int PRIMARY KEY, y int); -- this will be replicated to the coordinator because of add_coordinator test SELECT create_reference_table('ref_table'); diff --git a/src/test/regress/expected/create_ref_dist_from_citus_local.out b/src/test/regress/expected/create_ref_dist_from_citus_local.out index 023e23b7a..e24fe3e20 100644 --- a/src/test/regress/expected/create_ref_dist_from_citus_local.out +++ b/src/test/regress/expected/create_ref_dist_from_citus_local.out @@ -342,7 +342,7 @@ BEGIN; $update_value$ LANGUAGE plpgsql; CREATE TRIGGER update_value_dist AFTER INSERT ON citus_local_table_6 - FOR EACH ROW EXECUTE FUNCTION update_value(); + FOR EACH ROW EXECUTE PROCEDURE update_value(); -- show that we error out as we don't supprt triggers on distributed tables SELECT create_distributed_table('citus_local_table_6', 'col_1'); ERROR: cannot distribute relation "citus_local_table_6" because it has triggers diff --git a/src/test/regress/expected/cte_inline_0.out b/src/test/regress/expected/cte_inline_0.out deleted file mode 100644 index 35653633f..000000000 --- a/src/test/regress/expected/cte_inline_0.out +++ /dev/null @@ -1,1396 +0,0 @@ -CREATE SCHEMA cte_inline; -SET search_path TO cte_inline; -SET citus.next_shard_id TO 1960000; -CREATE TABLE test_table (key int, value text, other_value jsonb); -SELECT create_distributed_table ('test_table', 'key'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO test_table SELECT i % 10, 'test' || i, row_to_json(row(i, i*18, 'test' || i)) FROM generate_series (0, 100) i; --- server version because CTE inlining might produce --- different debug messages in PG 11 vs PG 12 -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 12; - ?column? ---------------------------------------------------------------------- - f -(1 row) - -SET client_min_messages TO DEBUG; --- Citus should not inline this CTE because otherwise it cannot --- plan the query -WITH cte_1 AS (SELECT * FROM test_table) -SELECT - * -FROM - test_table LEFT JOIN cte_1 USING (value) -ORDER BY 1 DESC LIMIT 3; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.value, test_table.key, test_table.other_value, cte_1.key, cte_1.other_value FROM (cte_inline.test_table LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1 USING (value)) ORDER BY test_table.value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | {"f1": 99, "f2": 1782, "f3": "test99"} | 9 | {"f1": 99, "f2": 1782, "f3": "test99"} - test98 | 8 | {"f1": 98, "f2": 1764, "f3": "test98"} | 8 | {"f1": 98, "f2": 1764, "f3": "test98"} - test97 | 7 | {"f1": 97, "f2": 1746, "f3": "test97"} | 7 | {"f1": 97, "f2": 1746, "f3": "test97"} -(3 rows) - --- Should still not be inlined even if NOT MATERIALIZED is passed -WITH cte_1 AS NOT MATERIALIZED (SELECT * FROM test_table) -SELECT - * -FROM - test_table LEFT JOIN cte_1 USING (value) -ORDER BY 2 DESC LIMIT 1; -ERROR: syntax error at or near "NOT" --- the cte can be inlined because the unsupported --- part of the query (subquery in WHERE clause) --- doesn't access the cte -WITH cte_1 AS (SELECT * FROM test_table) -SELECT - count(*) -FROM - cte_1 -WHERE - key IN ( - SELECT - (SELECT 1) - FROM - test_table WHERE key = 1 - ); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Creating router plan -DEBUG: query has a single distribution column value: 1 -DEBUG: generating subplan XXX_1 for subquery SELECT (SELECT 1) FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.=) 1) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1 WHERE (key OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result."?column?" FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result("?column?" integer))) -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 10 -(1 row) - --- a similar query as the above, and this time the planning --- fails, but it fails because the subquery in WHERE clause --- cannot be planned by Citus -WITH cte_1 AS (SELECT * FROM test_table) -SELECT - count(*) -FROM - cte_1 -WHERE - key IN ( - SELECT - key - FROM - test_table - FOR UPDATE - ); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: SELECT FOR UPDATE with table replication factor > 1 not supported for non-reference tables. -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: SELECT FOR UPDATE with table replication factor > 1 not supported for non-reference tables. -ERROR: could not run distributed query with FOR UPDATE/SHARE commands -HINT: Consider using an equality filter on the distributed table's partition column. --- Citus does the inlining, the planning fails --- and retries without inlining, which works --- fine later via recursive planning -WITH cte_1 AS - (SELECT * - FROM test_table) -SELECT *, (SELECT 1) -FROM - (SELECT * - FROM cte_1) AS foo -ORDER BY 2 DESC LIMIT 1; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 1 - key | value | other_value | ?column? ---------------------------------------------------------------------- - 9 | test99 | {"f1": 99, "f2": 1782, "f3": "test99"} | 1 -(1 row) - --- a little more complicated query tree --- Citus does the inlining, the planning fails --- and retries without inlining, which works -WITH top_cte AS - (SELECT * - FROM test_table) -SELECT count(*) -FROM top_cte, - (WITH cte_1 AS - (SELECT * - FROM test_table) SELECT *, (SELECT 1) - FROM - (SELECT * - FROM cte_1) AS foo) AS bar; -DEBUG: CTE top_cte is going to be inlined via distributed planning -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value, (SELECT 1) FROM (SELECT cte_1.key, cte_1.value, cte_1.other_value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1) foo -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) top_cte, (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value, intermediate_result."?column?" FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb, "?column?" integer)) bar(key, value, other_value, "?column?") -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 10201 -(1 row) - --- CTE is used inside a subquery in WHERE clause --- the query wouldn't work by inlining, so Citus --- retries again via recursive planning, which --- works fine -WITH cte_1 AS - (SELECT * - FROM test_table) -SELECT count(*) -FROM test_table -WHERE KEY IN - (SELECT (SELECT 1) - FROM - (SELECT *, - random() - FROM - (SELECT * - FROM cte_1) AS foo) AS bar); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT (SELECT 1) FROM (SELECT foo.key, foo.value, foo.other_value, random() AS random FROM (SELECT cte_1.key, cte_1.value, cte_1.other_value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1) foo) bar -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result."?column?" FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result("?column?" integer))) -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 10 -(1 row) - --- cte_1 is used inside another CTE, but still --- doesn't work when inlined because it is finally --- used in an unsupported query --- but still works fine because recursive planning --- kicks in -WITH cte_1 AS - (SELECT * - FROM test_table) -SELECT (SELECT 1) AS KEY FROM ( - WITH cte_2 AS (SELECT *, random() - FROM (SELECT *,random() FROM cte_1) as foo) -SELECT *, random() FROM cte_2) as bar ORDER BY 1 DESC LIMIT 3; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_2: SELECT key, value, other_value, random, random() AS random FROM (SELECT cte_1.key, cte_1.value, cte_1.other_value, random() AS random FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1) foo -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT (SELECT 1) AS key FROM (SELECT cte_2.key, cte_2.value, cte_2.other_value, cte_2.random, cte_2.random_1 AS random, random() AS random FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value, intermediate_result.random, intermediate_result.random_1 AS random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb, random double precision, random_1 double precision)) cte_2(key, value, other_value, random, random_1)) bar(key, value, other_value, random, random_1, random_2) ORDER BY (SELECT 1) DESC LIMIT 3 -DEBUG: Creating router plan - key ---------------------------------------------------------------------- - 1 - 1 - 1 -(3 rows) - --- in this example, cte_2 can be inlined, because it is not used --- on any query that Citus cannot plan. However, cte_1 should not be --- inlined, because it is used with a subquery in target list -WITH cte_1 AS (SELECT * FROM test_table), - cte_2 AS (select * from test_table) -SELECT - count(*) -FROM - (SELECT *, (SELECT 1) FROM cte_1) as foo - JOIN - cte_2 - ON (true); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT cte_1.key, cte_1.value, cte_1.other_value, (SELECT 1) FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1) foo(key, value, other_value, "?column?") JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_2 ON (true)) -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 10201 -(1 row) - --- unreferenced CTEs are just ignored --- by Citus/Postgres -WITH a AS (SELECT * FROM test_table) -SELECT - *, row_number() OVER () -FROM - test_table -WHERE - key = 1 -ORDER BY 3 DESC -LIMIT 5; -DEBUG: Creating router plan -DEBUG: query has a single distribution column value: 1 - key | value | other_value | row_number ---------------------------------------------------------------------- - 1 | test91 | {"f1": 91, "f2": 1638, "f3": "test91"} | 10 - 1 | test81 | {"f1": 81, "f2": 1458, "f3": "test81"} | 9 - 1 | test71 | {"f1": 71, "f2": 1278, "f3": "test71"} | 8 - 1 | test61 | {"f1": 61, "f2": 1098, "f3": "test61"} | 7 - 1 | test51 | {"f1": 51, "f2": 918, "f3": "test51"} | 6 -(5 rows) - --- router queries are affected by the distributed --- cte inlining -WITH a AS (SELECT * FROM test_table WHERE key = 1) -SELECT - *, (SELECT 1) -FROM - a -WHERE - key = 1 -ORDER BY 1 DESC -LIMIT 5; -DEBUG: CTE a is going to be inlined via distributed planning -DEBUG: Creating router plan -DEBUG: query has a single distribution column value: 1 - key | value | other_value | ?column? ---------------------------------------------------------------------- - 1 | test1 | {"f1": 1, "f2": 18, "f3": "test1"} | 1 - 1 | test11 | {"f1": 11, "f2": 198, "f3": "test11"} | 1 - 1 | test21 | {"f1": 21, "f2": 378, "f3": "test21"} | 1 - 1 | test31 | {"f1": 31, "f2": 558, "f3": "test31"} | 1 - 1 | test41 | {"f1": 41, "f2": 738, "f3": "test41"} | 1 -(5 rows) - --- non router queries are affected by the distributed --- cte inlining as well -WITH a AS (SELECT * FROM test_table) -SELECT - count(*) -FROM - a -WHERE - key = 1; -DEBUG: CTE a is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 10 -(1 row) - --- explicitely using NOT MATERIALIZED should result in the same -WITH a AS NOT MATERIALIZED (SELECT * FROM test_table) -SELECT - count(*) -FROM - a -WHERE - key = 1; -ERROR: syntax error at or near "NOT" --- using MATERIALIZED should cause inlining not to happen -WITH a AS MATERIALIZED (SELECT * FROM test_table) -SELECT - count(*) -FROM - a -WHERE - key = 1; -ERROR: syntax error at or near "MATERIALIZED" --- EXPLAIN should show the difference between materialized an not materialized -EXPLAIN (COSTS OFF) WITH a AS (SELECT * FROM test_table) -SELECT - count(*) -FROM - a -WHERE - key = 1; -DEBUG: CTE a is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - QUERY PLAN ---------------------------------------------------------------------- - Aggregate - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> Aggregate - -> Seq Scan on test_table_1960000 test_table - Filter: (key = 1) -(9 rows) - -EXPLAIN (COSTS OFF) WITH a AS MATERIALIZED (SELECT * FROM test_table) -SELECT - count(*) -FROM - a -WHERE - key = 1; -ERROR: syntax error at or near "MATERIALIZED" --- citus should not inline the CTE because it is used multiple times -WITH cte_1 AS (SELECT * FROM test_table) -SELECT - count(*) -FROM - cte_1 as first_entry - JOIN - cte_1 as second_entry - USING (key); -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) first_entry JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) second_entry USING (key)) -DEBUG: Creating router plan - count ---------------------------------------------------------------------- - 1021 -(1 row) - --- NOT MATERIALIZED should cause the query to be inlined twice -WITH cte_1 AS NOT MATERIALIZED (SELECT * FROM test_table) -SELECT - count(*) -FROM - cte_1 as first_entry - JOIN - cte_1 as second_entry - USING (key); -ERROR: syntax error at or near "NOT" --- EXPLAIN should show the differences between MATERIALIZED and NOT MATERIALIZED -EXPLAIN (COSTS OFF) WITH cte_1 AS (SELECT * FROM test_table) -SELECT - count(*) -FROM - cte_1 as first_entry - JOIN - cte_1 as second_entry - USING (key); -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) first_entry JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) second_entry USING (key)) -DEBUG: Creating router plan - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - -> Distributed Subplan XXX_1 - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> Seq Scan on test_table_1960000 test_table - Task Count: 1 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> Aggregate - -> Merge Join - Merge Cond: (intermediate_result.key = intermediate_result_1.key) - -> Sort - Sort Key: intermediate_result.key - -> Function Scan on read_intermediate_result intermediate_result - -> Sort - Sort Key: intermediate_result_1.key - -> Function Scan on read_intermediate_result intermediate_result_1 -(21 rows) - -EXPLAIN (COSTS OFF) WITH cte_1 AS NOT MATERIALIZED (SELECT * FROM test_table) -SELECT - count(*) -FROM - cte_1 as first_entry - JOIN - cte_1 as second_entry - USING (key); -ERROR: syntax error at or near "NOT" --- ctes with volatile functions are not --- inlined -WITH cte_1 AS (SELECT *, random() FROM test_table) -SELECT - key, value -FROM - cte_1 -ORDER BY 2 DESC LIMIT 1; -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value, other_value, random() AS random FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb, random double precision)) cte_1 ORDER BY value DESC LIMIT 1 -DEBUG: Creating router plan - key | value ---------------------------------------------------------------------- - 9 | test99 -(1 row) - --- even with NOT MATERIALIZED volatile functions should not be inlined -WITH cte_1 AS NOT MATERIALIZED (SELECT *, random() FROM test_table) -SELECT - count(*) -FROM - cte_1; -ERROR: syntax error at or near "NOT" --- cte_1 should be able to inlined even if --- it is used one level below -WITH cte_1 AS (SELECT * FROM test_table) -SELECT - count(*) -FROM -( - WITH ct2 AS (SELECT * FROM cte_1) - SELECT * FROM ct2 -) as foo; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE ct2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 101 -(1 row) - --- a similar query, but there is also --- one more cte, which relies on the previous --- CTE -WITH cte_1 AS (SELECT * FROM test_table) -SELECT - count(DISTINCT key) -FROM -( - WITH cte_2 AS (SELECT * FROM cte_1), - cte_3 AS (SELECT * FROM cte_2) - SELECT * FROM cte_3 -) as foo; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: CTE cte_3 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 10 -(1 row) - --- inlined CTE contains a reference to outer query --- should be fine (because we pushdown the whole query) -SELECT count(*) - FROM - (SELECT * - FROM test_table) AS test_table_cte - JOIN LATERAL - (WITH bar AS (SELECT * - FROM test_table - WHERE key = test_table_cte.key) - SELECT * - FROM - bar - LEFT JOIN test_table u2 ON u2.key = bar.key) AS foo ON TRUE; -DEBUG: CTE bar is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: skipping recursive planning for the subquery since it contains references to outer queries -DEBUG: skipping recursive planning for the subquery since it contains references to outer queries -DEBUG: skipping recursive planning for the subquery since it contains references to outer queries -DEBUG: Router planner cannot handle multi-shard select queries -ERROR: CTEs that refer to other subqueries are not supported in multi-shard queries --- inlined CTE contains a reference to outer query --- should be fine (even if the recursive planning fails --- to recursively plan the query) -SELECT count(*) - FROM - (SELECT * - FROM test_table) AS test_table_cte - JOIN LATERAL - (WITH bar AS (SELECT * - FROM test_table - WHERE key = test_table_cte.key) - SELECT * - FROM - bar - LEFT JOIN test_table u2 ON u2.key = bar.value::int) AS foo ON TRUE; -DEBUG: CTE bar is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: skipping recursive planning for the subquery since it contains references to outer queries -DEBUG: skipping recursive planning for the subquery since it contains references to outer queries -DEBUG: skipping recursive planning for the subquery since it contains references to outer queries -DEBUG: Router planner cannot handle multi-shard select queries -ERROR: CTEs that refer to other subqueries are not supported in multi-shard queries --- inlined CTE can recursively planned later, that's the decision --- recursive planning makes --- LIMIT 5 in cte2 triggers recusrive planning, after cte inlining -WITH cte_1 AS (SELECT * FROM test_table) -SELECT - * -FROM -( - WITH ct2 AS (SELECT * FROM cte_1 ORDER BY 1, 2, 3 LIMIT 5) - SELECT * FROM ct2 -) as foo ORDER BY 1 DESC, 2 DESC, 3 DESC LIMIT 5; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE ct2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 5 -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1 ORDER BY key, value, other_value LIMIT 5 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT key, value, other_value FROM (SELECT ct2.key, ct2.value, ct2.other_value FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) ct2) foo ORDER BY key DESC, value DESC, other_value DESC LIMIT 5 -DEBUG: Creating router plan - key | value | other_value ---------------------------------------------------------------------- - 0 | test30 | {"f1": 30, "f2": 540, "f3": "test30"} - 0 | test20 | {"f1": 20, "f2": 360, "f3": "test20"} - 0 | test100 | {"f1": 100, "f2": 1800, "f3": "test100"} - 0 | test10 | {"f1": 10, "f2": 180, "f3": "test10"} - 0 | test0 | {"f1": 0, "f2": 0, "f3": "test0"} -(5 rows) - --- all nested CTEs can be inlinied -WITH cte_1 AS ( - WITH cte_1 AS ( - WITH cte_1 AS ( - WITH cte_1 AS ( - WITH cte_1 AS ( - WITH cte_1 AS ( - WITH cte_1 AS (SELECT count(*), key FROM test_table GROUP BY key) - SELECT * FROM cte_1) - SELECT * FROM cte_1 WHERE key = 1) - SELECT * FROM cte_1 WHERE key = 2) - SELECT * FROM cte_1 WHERE key = 3) - SELECT * FROM cte_1 WHERE key = 4) - SELECT * FROM cte_1 WHERE key = 5) -SELECT * FROM cte_1 WHERE key = 6; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - count | key ---------------------------------------------------------------------- -(0 rows) - --- ctes can be inlined even if they are used --- in set operations -WITH cte_1 AS (SELECT * FROM test_table), - cte_2 AS (SELECT * FROM test_table) -SELECT count(*) FROM ( -(SELECT * FROM cte_1 EXCEPT SELECT * FROM test_table) -UNION -(SELECT * FROM cte_2)) as foo; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_2 for subquery SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_3 for subquery SELECT key, value, other_value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_2 -DEBUG: Creating router plan -DEBUG: generating subplan XXX_4 for subquery (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb) EXCEPT SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) UNION SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) foo -DEBUG: Creating router plan - count ---------------------------------------------------------------------- - 101 -(1 row) - --- cte_1 is going to be inlined even inside another set operation -WITH cte_1 AS (SELECT * FROM test_table), - cte_2 AS (SELECT * FROM test_table ORDER BY 1 DESC LIMIT 3) -(SELECT *, (SELECT 1) FROM cte_1 EXCEPT SELECT *, 1 FROM test_table) -UNION -(SELECT *, 1 FROM cte_2) -ORDER BY 1,2; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table ORDER BY key DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_2 for subquery SELECT key, value, other_value, (SELECT 1) FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_3 for subquery SELECT key, value, other_value, 1 FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value, intermediate_result."?column?" FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb, "?column?" integer) EXCEPT SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value, intermediate_result."?column?" FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb, "?column?" integer)) UNION SELECT cte_2.key, cte_2.value, cte_2.other_value, 1 FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_2 ORDER BY 1, 2 -DEBUG: Creating router plan - key | value | other_value | ?column? ---------------------------------------------------------------------- - 9 | test19 | {"f1": 19, "f2": 342, "f3": "test19"} | 1 - 9 | test29 | {"f1": 29, "f2": 522, "f3": "test29"} | 1 - 9 | test9 | {"f1": 9, "f2": 162, "f3": "test9"} | 1 -(3 rows) - --- cte_1 is safe to inline, even if because after inlining --- it'd be in a query tree where there is a query that is --- not supported by Citus unless recursively planned --- cte_2 is on another queryTree, should be fine -WITH cte_1 AS (SELECT * FROM test_table), - cte_2 AS (SELECT * FROM test_table) -(SELECT *, (SELECT key FROM cte_1) FROM test_table) -UNION -(SELECT *, 1 FROM cte_2); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_3 for subquery SELECT key, value, other_value, (SELECT cte_1.key FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1) AS key FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value, intermediate_result.key_1 AS key FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb, key_1 integer) UNION SELECT cte_2.key, cte_2.value, cte_2.other_value, 1 FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_2 -DEBUG: Creating router plan -ERROR: more than one row returned by a subquery used as an expression -CONTEXT: while executing command on localhost:xxxxx --- after inlining CTEs, the query becomes --- subquery pushdown with set operations -WITH cte_1 AS (SELECT * FROM test_table), - cte_2 AS (SELECT * FROM test_table) -SELECT max(key) FROM -( - SELECT * FROM cte_1 - UNION - SELECT * FROM cte_2 -) as bar; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - max ---------------------------------------------------------------------- - 9 -(1 row) - --- cte LEFT JOIN subquery should only work --- when CTE is inlined, as Citus currently --- doesn't know how to handle intermediate --- results in the outer parts of outer --- queries -WITH cte AS (SELECT * FROM test_table) -SELECT - count(*) -FROM - cte LEFT JOIN test_table USING (key); -DEBUG: CTE cte is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte LEFT JOIN cte_inline.test_table USING (key)) -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte LEFT JOIN cte_inline.test_table USING (key)) -DEBUG: Router planner cannot handle multi-shard select queries -ERROR: cannot pushdown the subquery -DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join --- the CTEs are very simple, so postgres --- can pull-up the subqueries after inlining --- the CTEs, and the query that we send to workers --- becomes a join between two tables -WITH cte_1 AS (SELECT key FROM test_table), - cte_2 AS (SELECT key FROM test_table) -SELECT - count(*) -FROM - cte_1 JOIN cte_2 USING (key); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT test_table.key FROM cte_inline.test_table) cte_1 JOIN (SELECT intermediate_result.key FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_2 USING (key)) -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 1021 -(1 row) - --- the following query is kind of interesting --- During INSERT .. SELECT via coordinator, --- Citus moves the CTEs into SELECT part, and plans/execute --- the SELECT separately. Thus, fist_table_cte can be inlined --- by Citus -- but not by Postgres -WITH fist_table_cte AS - (SELECT * FROM test_table) -INSERT INTO test_table - (key, value) - SELECT - key, value - FROM - fist_table_cte; -DEBUG: distributed INSERT ... SELECT can only select from distributed tables -DEBUG: CTE fist_table_cte is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: performing repartitioned INSERT ... SELECT -DEBUG: partitioning SELECT query by column index 0 with name 'key' -DEBUG: distributed statement: INSERT INTO cte_inline.test_table_1960000 AS citus_table_alias (key, value) SELECT key, value FROM read_intermediate_results('{repartitioned_results_xxxxx_from_1960000_to_0}'::text[], 'binary'::citus_copy_format) intermediate_result(key integer, value text) -DEBUG: distributed statement: INSERT INTO cte_inline.test_table_1960001 AS citus_table_alias (key, value) SELECT key, value FROM read_intermediate_results('{repartitioned_results_xxxxx_from_1960001_to_1}'::text[], 'binary'::citus_copy_format) intermediate_result(key integer, value text) -DEBUG: distributed statement: INSERT INTO cte_inline.test_table_1960002 AS citus_table_alias (key, value) SELECT key, value FROM read_intermediate_results('{repartitioned_results_xxxxx_from_1960002_to_2}'::text[], 'binary'::citus_copy_format) intermediate_result(key integer, value text) -DEBUG: distributed statement: INSERT INTO cte_inline.test_table_1960003 AS citus_table_alias (key, value) SELECT key, value FROM read_intermediate_results('{repartitioned_results_xxxxx_from_1960003_to_3}'::text[], 'binary'::citus_copy_format) intermediate_result(key integer, value text) --- the following INSERT..SELECT is even more interesting --- the CTE becomes pushdownable -INSERT INTO test_table -WITH fist_table_cte AS - (SELECT * FROM test_table) - SELECT - key, value - FROM - fist_table_cte; -DEBUG: CTE fist_table_cte is going to be inlined via distributed planning -DEBUG: distributed statement: INSERT INTO cte_inline.test_table_1960000 AS citus_table_alias (key, value) SELECT key, value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table_1960000 test_table) fist_table_cte WHERE (key IS NOT NULL) -DEBUG: distributed statement: INSERT INTO cte_inline.test_table_1960001 AS citus_table_alias (key, value) SELECT key, value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table_1960001 test_table) fist_table_cte WHERE (key IS NOT NULL) -DEBUG: distributed statement: INSERT INTO cte_inline.test_table_1960002 AS citus_table_alias (key, value) SELECT key, value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table_1960002 test_table) fist_table_cte WHERE (key IS NOT NULL) -DEBUG: distributed statement: INSERT INTO cte_inline.test_table_1960003 AS citus_table_alias (key, value) SELECT key, value FROM (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table_1960003 test_table) fist_table_cte WHERE (key IS NOT NULL) --- update/delete/modifying ctes --- we don't support any cte inlining in modifications --- queries and modifying CTEs -WITH cte_1 AS (SELECT * FROM test_table) - DELETE FROM test_table WHERE key NOT IN (SELECT key FROM cte_1); -DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: DELETE FROM cte_inline.test_table WHERE (NOT (key OPERATOR(pg_catalog.=) ANY (SELECT cte_1.key FROM (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1))) -DEBUG: Creating router plan --- NOT MATERIALIZED should not CTEs that are used in a modifying query, because --- we de still don't support it -WITH cte_1 AS NOT MATERIALIZED (SELECT * FROM test_table) - DELETE FROM test_table WHERE key NOT IN (SELECT key FROM cte_1); -ERROR: syntax error at or near "NOT" --- we don't inline CTEs if they are modifying CTEs -WITH cte_1 AS (DELETE FROM test_table WHERE key % 3 = 1 RETURNING key) -SELECT * FROM cte_1 ORDER BY 1 DESC LIMIT 3; -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_1: DELETE FROM cte_inline.test_table WHERE ((key OPERATOR(pg_catalog.%) 3) OPERATOR(pg_catalog.=) 1) RETURNING key -DEBUG: Creating router plan -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT key FROM (SELECT intermediate_result.key FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_1 ORDER BY key DESC LIMIT 3 -DEBUG: Creating router plan - key ---------------------------------------------------------------------- - 7 - 7 - 7 -(3 rows) - --- NOT MATERIALIZED should not affect modifying CTEs -WITH cte_1 AS NOT MATERIALIZED (DELETE FROM test_table WHERE key % 3 = 0 RETURNING key) -SELECT count(*) FROM cte_1; -ERROR: syntax error at or near "NOT" --- cte with column aliases -SELECT * FROM test_table, -(WITH cte_1 (x,y) AS (SELECT * FROM test_table), - cte_2 (z,y) AS (SELECT value, other_value, key FROM test_table), - cte_3 (t,m) AS (SELECT z, y, key as cte_2_key FROM cte_2) - SELECT * FROM cte_2, cte_3) as bar -ORDER BY value, other_value, z, y, t, m, cte_2_key -LIMIT 5; -DEBUG: CTE cte_3 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_2: SELECT value, other_value, key FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.key, test_table.value, test_table.other_value, bar.z, bar.y, bar.key, bar.t, bar.m, bar.cte_2_key FROM cte_inline.test_table, (SELECT cte_2.z, cte_2.y, cte_2.key, cte_3.t, cte_3.m, cte_3.cte_2_key FROM (SELECT intermediate_result.value AS z, intermediate_result.other_value AS y, intermediate_result.key FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text, other_value jsonb, key integer)) cte_2, (SELECT cte_2_1.z AS t, cte_2_1.y AS m, cte_2_1.key AS cte_2_key FROM (SELECT intermediate_result.value AS z, intermediate_result.other_value AS y, intermediate_result.key FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text, other_value jsonb, key integer)) cte_2_1) cte_3) bar ORDER BY test_table.value, test_table.other_value, bar.z, bar.y, bar.t, bar.m, bar.cte_2_key LIMIT 5 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 5 - key | value | other_value | z | y | key | t | m | cte_2_key ---------------------------------------------------------------------- - 0 | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | 0 | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | 0 - 0 | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | 0 | test0 | | 0 - 0 | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | 0 | test0 | | 0 - 0 | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | 0 | test0 | | 0 - 0 | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | test0 | {"f1": 0, "f2": 0, "f3": "test0"} | 0 | test10 | {"f1": 10, "f2": 180, "f3": "test10"} | 0 -(5 rows) - --- cte used in HAVING subquery just works fine --- even if it is inlined -WITH cte_1 AS (SELECT max(key) as max FROM test_table) -SELECT - key, count(*) -FROM - test_table -GROUP BY - key -HAVING - (count(*) > (SELECT max FROM cte_1)) -ORDER BY 2 DESC, 1 DESC -LIMIT 5; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT max(key) AS max FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT key, count(*) AS count FROM cte_inline.test_table GROUP BY key HAVING (count(*) OPERATOR(pg_catalog.>) (SELECT cte_1.max FROM (SELECT intermediate_result.max FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(max integer)) cte_1)) ORDER BY (count(*)) DESC, key DESC LIMIT 5 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 5 - key | count ---------------------------------------------------------------------- - 0 | 44 - 9 | 40 - 8 | 40 - 6 | 40 - 5 | 40 -(5 rows) - --- cte used in ORDER BY just works fine --- even if it is inlined -WITH cte_1 AS (SELECT max(key) as max FROM test_table) -SELECT - key -FROM - test_table JOIN cte_1 ON (key = max) -ORDER BY - cte_1.max -LIMIT 3; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT max(key) AS max FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.key FROM (cte_inline.test_table JOIN (SELECT intermediate_result.max FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(max integer)) cte_1 ON ((test_table.key OPERATOR(pg_catalog.=) cte_1.max))) ORDER BY cte_1.max LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - key ---------------------------------------------------------------------- - 9 - 9 - 9 -(3 rows) - -PREPARE inlined_cte_without_params AS - WITH cte_1 AS (SELECT count(*) FROM test_table GROUP BY key) - SELECT * FROM cte_1 ORDER BY 1 DESC LIMIT 3; -PREPARE non_inlined_cte_without_params AS - WITH cte_1 AS (SELECT * FROM test_table) - SELECT - * - FROM - test_table LEFT JOIN cte_1 USING (value) ORDER BY 1 DESC, 2 DESC, 3 DESC LIMIT 3; -PREPARE inlined_cte_has_parameter_on_non_dist_key(text) AS - WITH cte_1 AS (SELECT count(*) FROM test_table WHERE value = $1 GROUP BY key) - SELECT * FROM cte_1 ORDER BY 1 DESC LIMIT 3; -PREPARE inlined_cte_has_parameter_on_dist_key(int) AS - WITH cte_1 AS (SELECT count(*) FROM test_table WHERE key > $1 GROUP BY key) - SELECT * FROM cte_1 ORDER BY 1 DESC LIMIT 3; -PREPARE non_inlined_cte_has_parameter_on_dist_key(int) AS - WITH cte_1 AS (SELECT * FROM test_table where key > $1) - SELECT - * - FROM - test_table LEFT JOIN cte_1 USING (value) ORDER BY 1 DESC, 2 DESC, 3 DESC LIMIT 3; -PREPARE retry_planning(int) AS - WITH cte_1 AS (SELECT * FROM test_table WHERE key > $1) - SELECT json_object_agg(DISTINCT key, value) FROM cte_1 ORDER BY max(key), min(value) DESC LIMIT 3; -EXECUTE inlined_cte_without_params; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 44 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_without_params; - count ---------------------------------------------------------------------- - 44 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_without_params; - count ---------------------------------------------------------------------- - 44 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_without_params; - count ---------------------------------------------------------------------- - 44 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_without_params; - count ---------------------------------------------------------------------- - 44 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_without_params; - count ---------------------------------------------------------------------- - 44 - 40 - 40 -(3 rows) - -EXECUTE non_inlined_cte_without_params; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.value, test_table.key, test_table.other_value, cte_1.key, cte_1.other_value FROM (cte_inline.test_table LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1 USING (value)) ORDER BY test_table.value DESC, test_table.key DESC, test_table.other_value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_without_params; - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_without_params; - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_without_params; - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_without_params; - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_without_params; - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE inlined_cte_has_parameter_on_non_dist_key('test1'); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- -(0 rows) - -EXECUTE inlined_cte_has_parameter_on_non_dist_key('test2'); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 4 -(1 row) - -EXECUTE inlined_cte_has_parameter_on_non_dist_key('test3'); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 4 -(1 row) - -EXECUTE inlined_cte_has_parameter_on_non_dist_key('test4'); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- -(0 rows) - -EXECUTE inlined_cte_has_parameter_on_non_dist_key('test5'); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 4 -(1 row) - -EXECUTE inlined_cte_has_parameter_on_non_dist_key('test6'); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 4 -(1 row) - -EXECUTE inlined_cte_has_parameter_on_dist_key(1); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 40 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_has_parameter_on_dist_key(2); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 40 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_has_parameter_on_dist_key(3); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 40 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_has_parameter_on_dist_key(4); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 40 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_has_parameter_on_dist_key(5); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 40 - 40 - 40 -(3 rows) - -EXECUTE inlined_cte_has_parameter_on_dist_key(6); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - count ---------------------------------------------------------------------- - 40 - 40 -(2 rows) - -EXECUTE non_inlined_cte_has_parameter_on_dist_key(1); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 1) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.value, test_table.key, test_table.other_value, cte_1.key, cte_1.other_value FROM (cte_inline.test_table LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1 USING (value)) ORDER BY test_table.value DESC, test_table.key DESC, test_table.other_value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_has_parameter_on_dist_key(2); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 2) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.value, test_table.key, test_table.other_value, cte_1.key, cte_1.other_value FROM (cte_inline.test_table LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1 USING (value)) ORDER BY test_table.value DESC, test_table.key DESC, test_table.other_value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_has_parameter_on_dist_key(3); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 3) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.value, test_table.key, test_table.other_value, cte_1.key, cte_1.other_value FROM (cte_inline.test_table LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1 USING (value)) ORDER BY test_table.value DESC, test_table.key DESC, test_table.other_value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_has_parameter_on_dist_key(4); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 4) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.value, test_table.key, test_table.other_value, cte_1.key, cte_1.other_value FROM (cte_inline.test_table LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1 USING (value)) ORDER BY test_table.value DESC, test_table.key DESC, test_table.other_value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_has_parameter_on_dist_key(5); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 5) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.value, test_table.key, test_table.other_value, cte_1.key, cte_1.other_value FROM (cte_inline.test_table LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1 USING (value)) ORDER BY test_table.value DESC, test_table.key DESC, test_table.other_value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE non_inlined_cte_has_parameter_on_dist_key(6); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 6) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT test_table.value, test_table.key, test_table.other_value, cte_1.key, cte_1.other_value FROM (cte_inline.test_table LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_1 USING (value)) ORDER BY test_table.value DESC, test_table.key DESC, test_table.other_value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - value | key | other_value | key | other_value ---------------------------------------------------------------------- - test99 | 9 | | 9 | - test99 | 9 | | 9 | - test99 | 9 | | 9 | -(3 rows) - -EXECUTE retry_planning(1); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - json_object_agg ---------------------------------------------------------------------- - { "2" : "test12", "2" : "test2", "2" : "test22", "2" : "test32", "2" : "test42", "2" : "test52", "2" : "test62", "2" : "test72", "2" : "test82", "2" : "test92", "3" : "test13", "3" : "test23", "3" : "test3", "3" : "test33", "3" : "test43", "3" : "test53", "3" : "test63", "3" : "test73", "3" : "test83", "3" : "test93", "5" : "test15", "5" : "test25", "5" : "test35", "5" : "test45", "5" : "test5", "5" : "test55", "5" : "test65", "5" : "test75", "5" : "test85", "5" : "test95", "6" : "test16", "6" : "test26", "6" : "test36", "6" : "test46", "6" : "test56", "6" : "test6", "6" : "test66", "6" : "test76", "6" : "test86", "6" : "test96", "8" : "test18", "8" : "test28", "8" : "test38", "8" : "test48", "8" : "test58", "8" : "test68", "8" : "test78", "8" : "test8", "8" : "test88", "8" : "test98", "9" : "test19", "9" : "test29", "9" : "test39", "9" : "test49", "9" : "test59", "9" : "test69", "9" : "test79", "9" : "test89", "9" : "test9", "9" : "test99" } -(1 row) - -EXECUTE retry_planning(2); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - json_object_agg ---------------------------------------------------------------------- - { "3" : "test13", "3" : "test23", "3" : "test3", "3" : "test33", "3" : "test43", "3" : "test53", "3" : "test63", "3" : "test73", "3" : "test83", "3" : "test93", "5" : "test15", "5" : "test25", "5" : "test35", "5" : "test45", "5" : "test5", "5" : "test55", "5" : "test65", "5" : "test75", "5" : "test85", "5" : "test95", "6" : "test16", "6" : "test26", "6" : "test36", "6" : "test46", "6" : "test56", "6" : "test6", "6" : "test66", "6" : "test76", "6" : "test86", "6" : "test96", "8" : "test18", "8" : "test28", "8" : "test38", "8" : "test48", "8" : "test58", "8" : "test68", "8" : "test78", "8" : "test8", "8" : "test88", "8" : "test98", "9" : "test19", "9" : "test29", "9" : "test39", "9" : "test49", "9" : "test59", "9" : "test69", "9" : "test79", "9" : "test89", "9" : "test9", "9" : "test99" } -(1 row) - -EXECUTE retry_planning(3); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - json_object_agg ---------------------------------------------------------------------- - { "5" : "test15", "5" : "test25", "5" : "test35", "5" : "test45", "5" : "test5", "5" : "test55", "5" : "test65", "5" : "test75", "5" : "test85", "5" : "test95", "6" : "test16", "6" : "test26", "6" : "test36", "6" : "test46", "6" : "test56", "6" : "test6", "6" : "test66", "6" : "test76", "6" : "test86", "6" : "test96", "8" : "test18", "8" : "test28", "8" : "test38", "8" : "test48", "8" : "test58", "8" : "test68", "8" : "test78", "8" : "test8", "8" : "test88", "8" : "test98", "9" : "test19", "9" : "test29", "9" : "test39", "9" : "test49", "9" : "test59", "9" : "test69", "9" : "test79", "9" : "test89", "9" : "test9", "9" : "test99" } -(1 row) - -EXECUTE retry_planning(4); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - json_object_agg ---------------------------------------------------------------------- - { "5" : "test15", "5" : "test25", "5" : "test35", "5" : "test45", "5" : "test5", "5" : "test55", "5" : "test65", "5" : "test75", "5" : "test85", "5" : "test95", "6" : "test16", "6" : "test26", "6" : "test36", "6" : "test46", "6" : "test56", "6" : "test6", "6" : "test66", "6" : "test76", "6" : "test86", "6" : "test96", "8" : "test18", "8" : "test28", "8" : "test38", "8" : "test48", "8" : "test58", "8" : "test68", "8" : "test78", "8" : "test8", "8" : "test88", "8" : "test98", "9" : "test19", "9" : "test29", "9" : "test39", "9" : "test49", "9" : "test59", "9" : "test69", "9" : "test79", "9" : "test89", "9" : "test9", "9" : "test99" } -(1 row) - -EXECUTE retry_planning(5); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - json_object_agg ---------------------------------------------------------------------- - { "6" : "test16", "6" : "test26", "6" : "test36", "6" : "test46", "6" : "test56", "6" : "test6", "6" : "test66", "6" : "test76", "6" : "test86", "6" : "test96", "8" : "test18", "8" : "test28", "8" : "test38", "8" : "test48", "8" : "test58", "8" : "test68", "8" : "test78", "8" : "test8", "8" : "test88", "8" : "test98", "9" : "test19", "9" : "test29", "9" : "test39", "9" : "test49", "9" : "test59", "9" : "test69", "9" : "test79", "9" : "test89", "9" : "test9", "9" : "test99" } -(1 row) - -EXECUTE retry_planning(6); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - json_object_agg ---------------------------------------------------------------------- - { "8" : "test18", "8" : "test28", "8" : "test38", "8" : "test48", "8" : "test58", "8" : "test68", "8" : "test78", "8" : "test8", "8" : "test88", "8" : "test98", "9" : "test19", "9" : "test29", "9" : "test39", "9" : "test49", "9" : "test59", "9" : "test69", "9" : "test79", "9" : "test89", "9" : "test9", "9" : "test99" } -(1 row) - --- this test can only work if the CTE is recursively --- planned -WITH b AS (SELECT * FROM test_table) -SELECT count(*) FROM (SELECT key as x FROM test_table OFFSET 0) as ref LEFT JOIN b ON (ref.x = b.key); -DEBUG: CTE b is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key AS x FROM cte_inline.test_table OFFSET 0 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.x FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) ref LEFT JOIN (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) b ON ((ref.x OPERATOR(pg_catalog.=) b.key))) -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE b: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_2 for subquery SELECT key AS x FROM cte_inline.test_table OFFSET 0 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.x FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) ref LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) b ON ((ref.x OPERATOR(pg_catalog.=) b.key))) -DEBUG: Creating router plan - count ---------------------------------------------------------------------- - 11536 -(1 row) - --- this becomes a non-colocated subquery join --- because after the CTEs are inlined the joins --- become a non-colocated subquery join -WITH a AS (SELECT * FROM test_table), -b AS (SELECT * FROM test_table) -SELECT count(*) FROM a LEFT JOIN b ON (a.value = b.value); -DEBUG: CTE a is going to be inlined via distributed planning -DEBUG: CTE b is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) a LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) b ON ((a.value OPERATOR(pg_catalog.=) b.value))) -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 1136 -(1 row) - --- cte a has to be recursively planned because of OFFSET 0 --- after that, cte b also requires recursive planning -WITH a AS (SELECT * FROM test_table OFFSET 0), -b AS (SELECT * FROM test_table) -SELECT min(a.key) FROM a LEFT JOIN b ON (a.value = b.value); -DEBUG: CTE a is going to be inlined via distributed planning -DEBUG: CTE b is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table OFFSET 0 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT min(a.key) AS min FROM ((SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) a LEFT JOIN (SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) b ON ((a.value OPERATOR(pg_catalog.=) b.value))) -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value, other_value FROM cte_inline.test_table OFFSET 0 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_2 for CTE b: SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT min(a.key) AS min FROM ((SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) a LEFT JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) b ON ((a.value OPERATOR(pg_catalog.=) b.value))) -DEBUG: Creating router plan - min ---------------------------------------------------------------------- - 0 -(1 row) - --- after both CTEs are inlined, this becomes non-colocated subquery join -WITH cte_1 AS (SELECT * FROM test_table), -cte_2 AS (SELECT * FROM test_table) -SELECT * FROM cte_1 JOIN cte_2 ON (cte_1.value > cte_2.value) ORDER BY 1,2,3,4,5,6 DESC LIMIT 3;; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT key, value, other_value FROM cte_inline.test_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_1.key, cte_1.value, cte_1.other_value, cte_2.key, cte_2.value, cte_2.other_value FROM ((SELECT test_table.key, test_table.value, test_table.other_value FROM cte_inline.test_table) cte_1 JOIN (SELECT intermediate_result.key, intermediate_result.value, intermediate_result.other_value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text, other_value jsonb)) cte_2 ON ((cte_1.value OPERATOR(pg_catalog.>) cte_2.value))) ORDER BY cte_1.key, cte_1.value, cte_1.other_value, cte_2.key, cte_2.value, cte_2.other_value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: push down of limit count: 3 - key | value | other_value | key | value | other_value ---------------------------------------------------------------------- - 0 | test10 | {"f1": 10, "f2": 180, "f3": "test10"} | 0 | test0 | - 0 | test10 | {"f1": 10, "f2": 180, "f3": "test10"} | 0 | test0 | - 0 | test10 | {"f1": 10, "f2": 180, "f3": "test10"} | 0 | test0 | -(3 rows) - --- full join is only supported when both sides are --- recursively planned -WITH cte_1 AS (SELECT value FROM test_table WHERE key > 1), - cte_2 AS (SELECT value FROM test_table WHERE key > 3) -SELECT * FROM cte_1 FULL JOIN cte_2 USING (value) ORDER BY 1 DESC LIMIT 3;; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 3) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT value FROM ((SELECT test_table.value FROM cte_inline.test_table WHERE (test_table.key OPERATOR(pg_catalog.>) 1)) cte_1 FULL JOIN (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) cte_2 USING (value)) ORDER BY value DESC LIMIT 3 -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 1) -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 3) -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT value FROM ((SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) cte_1 FULL JOIN (SELECT intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(value text)) cte_2 USING (value)) ORDER BY value DESC LIMIT 3 -DEBUG: Creating router plan - value ---------------------------------------------------------------------- - test99 - test99 - test99 -(3 rows) - --- an unsupported agg. for multi-shard queries --- so CTE has to be recursively planned -WITH cte_1 AS (SELECT * FROM test_table WHERE key > 1) -SELECT json_object_agg(DISTINCT key, value) FROM cte_1; -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries - json_object_agg ---------------------------------------------------------------------- - { "2" : "test12", "2" : "test2", "2" : "test22", "2" : "test32", "2" : "test42", "2" : "test52", "2" : "test62", "2" : "test72", "2" : "test82", "2" : "test92", "3" : "test13", "3" : "test23", "3" : "test3", "3" : "test33", "3" : "test43", "3" : "test53", "3" : "test63", "3" : "test73", "3" : "test83", "3" : "test93", "5" : "test15", "5" : "test25", "5" : "test35", "5" : "test45", "5" : "test5", "5" : "test55", "5" : "test65", "5" : "test75", "5" : "test85", "5" : "test95", "6" : "test16", "6" : "test26", "6" : "test36", "6" : "test46", "6" : "test56", "6" : "test6", "6" : "test66", "6" : "test76", "6" : "test86", "6" : "test96", "8" : "test18", "8" : "test28", "8" : "test38", "8" : "test48", "8" : "test58", "8" : "test68", "8" : "test78", "8" : "test8", "8" : "test88", "8" : "test98", "9" : "test19", "9" : "test29", "9" : "test39", "9" : "test49", "9" : "test59", "9" : "test69", "9" : "test79", "9" : "test89", "9" : "test9", "9" : "test99" } -(1 row) - --- both cte_1 and cte_2 are going to be inlined. --- later, cte_2 is recursively planned since it doesn't have --- GROUP BY but aggragate in a subquery. --- this is an important example of being able to recursively plan --- "some" of the CTEs -WITH cte_1 AS (SELECT value FROM test_table WHERE key > 1), - cte_2 AS (SELECT max(value) as value FROM test_table WHERE key > 3) -SELECT count(*) FROM cte_1 JOIN cte_2 USING (value); -DEBUG: CTE cte_1 is going to be inlined via distributed planning -DEBUG: CTE cte_2 is going to be inlined via distributed planning -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: generating subplan XXX_1 for subquery SELECT max(value) AS value FROM cte_inline.test_table WHERE (key OPERATOR(pg_catalog.>) 3) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT test_table.value FROM cte_inline.test_table WHERE (test_table.key OPERATOR(pg_catalog.>) 1)) cte_1 JOIN (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) cte_2 USING (value)) -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 4 -(1 row) - --- prevent DROP CASCADE to give notices -SET client_min_messages TO ERROR; -DROP SCHEMA cte_inline CASCADE; diff --git a/src/test/regress/expected/custom_aggregate_support_1.out b/src/test/regress/expected/custom_aggregate_support_1.out deleted file mode 100644 index 5a27b9d82..000000000 --- a/src/test/regress/expected/custom_aggregate_support_1.out +++ /dev/null @@ -1,646 +0,0 @@ --- --- CUSTOM_AGGREGATE_SUPPORT --- --- Create HLL extension if present, print false result otherwise -SELECT CASE WHEN COUNT(*) > 0 THEN - 'CREATE EXTENSION HLL' -ELSE 'SELECT false AS hll_present' END -AS create_cmd FROM pg_available_extensions() -WHERE name = 'hll' -\gset -:create_cmd; -SET citus.shard_count TO 4; -set citus.coordinator_aggregation_strategy to 'disabled'; -CREATE TABLE raw_table (day date, user_id int); -CREATE TABLE daily_uniques(day date, unique_users hll); -SELECT create_distributed_table('raw_table', 'user_id'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('daily_uniques', 'day'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO raw_table - SELECT day, user_id % 19 - FROM generate_series('2018-05-24'::timestamp, '2018-06-24'::timestamp, '1 day'::interval) as f(day), - generate_series(1,100) as g(user_id); -INSERT INTO raw_table - SELECT day, user_id % 13 - FROM generate_series('2018-06-10'::timestamp, '2018-07-10'::timestamp, '1 day'::interval) as f(day), - generate_series(1,100) as g(user_id); --- Run hll on raw data -SELECT hll_cardinality(hll_union_agg(agg)) -FROM ( - SELECT hll_add_agg(hll_hash_integer(user_id)) AS agg - FROM raw_table)a; - hll_cardinality ---------------------------------------------------------------------- - 19 -(1 row) - --- Aggregate the data into daily_uniques -INSERT INTO daily_uniques - SELECT day, hll_add_agg(hll_hash_integer(user_id)) - FROM raw_table - GROUP BY 1; --- Basic hll_cardinality check on aggregated data -SELECT day, hll_cardinality(unique_users) -FROM daily_uniques -WHERE day >= '2018-06-20' and day <= '2018-06-30' -ORDER BY 2 DESC,1 -LIMIT 10; - day | hll_cardinality ---------------------------------------------------------------------- - 06-20-2018 | 19 - 06-21-2018 | 19 - 06-22-2018 | 19 - 06-23-2018 | 19 - 06-24-2018 | 19 - 06-25-2018 | 13 - 06-26-2018 | 13 - 06-27-2018 | 13 - 06-28-2018 | 13 - 06-29-2018 | 13 -(10 rows) - --- Union aggregated data for one week -SELECT hll_cardinality(hll_union_agg(unique_users)) -FROM daily_uniques -WHERE day >= '2018-05-24'::date AND day <= '2018-05-31'::date; - hll_cardinality ---------------------------------------------------------------------- - 19 -(1 row) - -SELECT EXTRACT(MONTH FROM day) AS month, hll_cardinality(hll_union_agg(unique_users)) -FROM daily_uniques -WHERE day >= '2018-06-23' AND day <= '2018-07-01' -GROUP BY 1 -ORDER BY 1; - month | hll_cardinality ---------------------------------------------------------------------- - 6 | 19 - 7 | 13 -(2 rows) - -SELECT day, hll_cardinality(hll_union_agg(unique_users) OVER seven_days) -FROM daily_uniques -WINDOW seven_days AS (ORDER BY day ASC ROWS 6 PRECEDING) -ORDER BY 1; - day | hll_cardinality ---------------------------------------------------------------------- - 05-24-2018 | 19 - 05-25-2018 | 19 - 05-26-2018 | 19 - 05-27-2018 | 19 - 05-28-2018 | 19 - 05-29-2018 | 19 - 05-30-2018 | 19 - 05-31-2018 | 19 - 06-01-2018 | 19 - 06-02-2018 | 19 - 06-03-2018 | 19 - 06-04-2018 | 19 - 06-05-2018 | 19 - 06-06-2018 | 19 - 06-07-2018 | 19 - 06-08-2018 | 19 - 06-09-2018 | 19 - 06-10-2018 | 19 - 06-11-2018 | 19 - 06-12-2018 | 19 - 06-13-2018 | 19 - 06-14-2018 | 19 - 06-15-2018 | 19 - 06-16-2018 | 19 - 06-17-2018 | 19 - 06-18-2018 | 19 - 06-19-2018 | 19 - 06-20-2018 | 19 - 06-21-2018 | 19 - 06-22-2018 | 19 - 06-23-2018 | 19 - 06-24-2018 | 19 - 06-25-2018 | 19 - 06-26-2018 | 19 - 06-27-2018 | 19 - 06-28-2018 | 19 - 06-29-2018 | 19 - 06-30-2018 | 19 - 07-01-2018 | 13 - 07-02-2018 | 13 - 07-03-2018 | 13 - 07-04-2018 | 13 - 07-05-2018 | 13 - 07-06-2018 | 13 - 07-07-2018 | 13 - 07-08-2018 | 13 - 07-09-2018 | 13 - 07-10-2018 | 13 -(48 rows) - -SELECT day, (hll_cardinality(hll_union_agg(unique_users) OVER two_days)) - hll_cardinality(unique_users) AS lost_uniques -FROM daily_uniques -WINDOW two_days AS (ORDER BY day ASC ROWS 1 PRECEDING) -ORDER BY 1; - day | lost_uniques ---------------------------------------------------------------------- - 05-24-2018 | 0 - 05-25-2018 | 0 - 05-26-2018 | 0 - 05-27-2018 | 0 - 05-28-2018 | 0 - 05-29-2018 | 0 - 05-30-2018 | 0 - 05-31-2018 | 0 - 06-01-2018 | 0 - 06-02-2018 | 0 - 06-03-2018 | 0 - 06-04-2018 | 0 - 06-05-2018 | 0 - 06-06-2018 | 0 - 06-07-2018 | 0 - 06-08-2018 | 0 - 06-09-2018 | 0 - 06-10-2018 | 0 - 06-11-2018 | 0 - 06-12-2018 | 0 - 06-13-2018 | 0 - 06-14-2018 | 0 - 06-15-2018 | 0 - 06-16-2018 | 0 - 06-17-2018 | 0 - 06-18-2018 | 0 - 06-19-2018 | 0 - 06-20-2018 | 0 - 06-21-2018 | 0 - 06-22-2018 | 0 - 06-23-2018 | 0 - 06-24-2018 | 0 - 06-25-2018 | 6 - 06-26-2018 | 0 - 06-27-2018 | 0 - 06-28-2018 | 0 - 06-29-2018 | 0 - 06-30-2018 | 0 - 07-01-2018 | 0 - 07-02-2018 | 0 - 07-03-2018 | 0 - 07-04-2018 | 0 - 07-05-2018 | 0 - 07-06-2018 | 0 - 07-07-2018 | 0 - 07-08-2018 | 0 - 07-09-2018 | 0 - 07-10-2018 | 0 -(48 rows) - --- Test disabling hash_agg on coordinator query -SET citus.explain_all_tasks to true; -SET hll.force_groupagg to OFF; -EXPLAIN(COSTS OFF) -SELECT - day, hll_union_agg(unique_users) -FROM - daily_uniques -GROUP BY(1); - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(23 rows) - -SET hll.force_groupagg to ON; -EXPLAIN(COSTS OFF) -SELECT - day, hll_union_agg(unique_users) -FROM - daily_uniques -GROUP BY(1); - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(23 rows) - --- Test disabling hash_agg with operator on coordinator query -SET hll.force_groupagg to OFF; -EXPLAIN(COSTS OFF) -SELECT - day, hll_union_agg(unique_users) || hll_union_agg(unique_users) -FROM - daily_uniques -GROUP BY(1); - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(23 rows) - -SET hll.force_groupagg to ON; -EXPLAIN(COSTS OFF) -SELECT - day, hll_union_agg(unique_users) || hll_union_agg(unique_users) -FROM - daily_uniques -GROUP BY(1); - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(23 rows) - --- Test disabling hash_agg with expression on coordinator query -SET hll.force_groupagg to OFF; -EXPLAIN(COSTS OFF) -SELECT - day, hll_cardinality(hll_union_agg(unique_users)) -FROM - daily_uniques -GROUP BY(1); - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(23 rows) - -SET hll.force_groupagg to ON; -EXPLAIN(COSTS OFF) -SELECT - day, hll_cardinality(hll_union_agg(unique_users)) -FROM - daily_uniques -GROUP BY(1); - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(23 rows) - --- Test disabling hash_agg with having -SET hll.force_groupagg to OFF; -EXPLAIN(COSTS OFF) -SELECT - day, hll_cardinality(hll_union_agg(unique_users)) -FROM - daily_uniques -GROUP BY(1); - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(23 rows) - -SET hll.force_groupagg to ON; -EXPLAIN(COSTS OFF) -SELECT - day, hll_cardinality(hll_union_agg(unique_users)) -FROM - daily_uniques -GROUP BY(1) -HAVING hll_cardinality(hll_union_agg(unique_users)) > 1; - QUERY PLAN ---------------------------------------------------------------------- - Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: All - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - Filter: (hll_cardinality(hll_union_agg(unique_users)) > '1'::double precision) - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - Filter: (hll_cardinality(hll_union_agg(unique_users)) > '1'::double precision) - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - Filter: (hll_cardinality(hll_union_agg(unique_users)) > '1'::double precision) - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: day - Filter: (hll_cardinality(hll_union_agg(unique_users)) > '1'::double precision) - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(27 rows) - -DROP TABLE raw_table; -DROP TABLE daily_uniques; --- Check if TopN aggregates work as expected --- Create TopN extension if present, print false result otherwise -SELECT CASE WHEN COUNT(*) > 0 THEN - 'CREATE EXTENSION TOPN' -ELSE 'SELECT false AS topn_present' END -AS create_topn FROM pg_available_extensions() -WHERE name = 'topn' -\gset -:create_topn; -CREATE TABLE customer_reviews (day date, user_id int, review int); -CREATE TABLE popular_reviewer(day date, reviewers jsonb); -SELECT create_distributed_table('customer_reviews', 'user_id'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT create_distributed_table('popular_reviewer', 'day'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO customer_reviews - SELECT day, user_id % 7, review % 5 - FROM generate_series('2018-05-24'::timestamp, '2018-06-24'::timestamp, '1 day'::interval) as f(day), - generate_series(1,30) as g(user_id), generate_series(0,30) AS r(review); -INSERT INTO customer_reviews - SELECT day, user_id % 13, review % 3 - FROM generate_series('2018-06-10'::timestamp, '2018-07-10'::timestamp, '1 day'::interval) as f(day), - generate_series(1,30) as g(user_id), generate_series(0,30) AS r(review); --- Run topn on raw data -SELECT (topn(agg, 10)).* -FROM ( - SELECT topn_add_agg(user_id::text) AS agg - FROM customer_reviews - )a -ORDER BY 2 DESC, 1; - item | frequency ---------------------------------------------------------------------- - 1 | 7843 - 2 | 7843 - 3 | 6851 - 4 | 6851 - 0 | 5890 - 5 | 5890 - 6 | 5890 - 7 | 1922 - 8 | 1922 - 9 | 1922 -(10 rows) - --- Aggregate the data into popular_reviewer -INSERT INTO popular_reviewer - SELECT day, topn_add_agg(user_id::text) - FROM customer_reviews - GROUP BY 1; --- Basic topn check on aggregated data -SELECT day, (topn(reviewers, 10)).* -FROM popular_reviewer -WHERE day >= '2018-06-20' and day <= '2018-06-30' -ORDER BY 3 DESC, 1, 2 -LIMIT 10; - day | item | frequency ---------------------------------------------------------------------- - 06-20-2018 | 1 | 248 - 06-20-2018 | 2 | 248 - 06-21-2018 | 1 | 248 - 06-21-2018 | 2 | 248 - 06-22-2018 | 1 | 248 - 06-22-2018 | 2 | 248 - 06-23-2018 | 1 | 248 - 06-23-2018 | 2 | 248 - 06-24-2018 | 1 | 248 - 06-24-2018 | 2 | 248 -(10 rows) - --- Union aggregated data for one week -SELECT (topn(agg, 10)).* -FROM ( - SELECT topn_union_agg(reviewers) AS agg - FROM popular_reviewer - WHERE day >= '2018-05-24'::date AND day <= '2018-05-31'::date - )a -ORDER BY 2 DESC, 1; - item | frequency ---------------------------------------------------------------------- - 1 | 1240 - 2 | 1240 - 0 | 992 - 3 | 992 - 4 | 992 - 5 | 992 - 6 | 992 -(7 rows) - -SELECT month, (topn(agg, 5)).* -FROM ( - SELECT EXTRACT(MONTH FROM day) AS month, topn_union_agg(reviewers) AS agg - FROM popular_reviewer - WHERE day >= '2018-06-23' AND day <= '2018-07-01' - GROUP BY 1 - ORDER BY 1 - )a -ORDER BY 1, 3 DESC, 2; - month | item | frequency ---------------------------------------------------------------------- - 6 | 1 | 1054 - 6 | 2 | 1054 - 6 | 3 | 992 - 6 | 4 | 992 - 6 | 0 | 744 - 7 | 1 | 93 - 7 | 2 | 93 - 7 | 3 | 93 - 7 | 4 | 93 - 7 | 8 | 62 -(10 rows) - --- TODO the following queries will be supported after we fix #2265 --- They work for PG9.6 but not for PG10 -SELECT (topn(topn_union_agg(reviewers), 10)).* -FROM popular_reviewer -WHERE day >= '2018-05-24'::date AND day <= '2018-05-31'::date -ORDER BY 2 DESC, 1; -ERROR: set-valued function called in context that cannot accept a set -SELECT (topn(topn_add_agg(user_id::text), 10)).* -FROM customer_reviews -ORDER BY 2 DESC, 1; -ERROR: set-valued function called in context that cannot accept a set -SELECT day, (topn(agg, 10)).* -FROM ( - SELECT day, topn_union_agg(reviewers) OVER seven_days AS agg - FROM popular_reviewer - WINDOW seven_days AS (ORDER BY day ASC ROWS 6 PRECEDING) - )a -ORDER BY 3 DESC, 1, 2 -LIMIT 10; - day | item | frequency ---------------------------------------------------------------------- - 06-16-2018 | 1 | 1736 - 06-16-2018 | 2 | 1736 - 06-17-2018 | 1 | 1736 - 06-17-2018 | 2 | 1736 - 06-18-2018 | 1 | 1736 - 06-18-2018 | 2 | 1736 - 06-19-2018 | 1 | 1736 - 06-19-2018 | 2 | 1736 - 06-20-2018 | 1 | 1736 - 06-20-2018 | 2 | 1736 -(10 rows) - -SELECT day, (topn(topn_add_agg(user_id::text) OVER seven_days, 10)).* -FROM customer_reviews -WINDOW seven_days AS (ORDER BY day ASC ROWS 6 PRECEDING) -ORDER BY 3 DESC, 1, 2 -LIMIT 10; -ERROR: set-valued function called in context that cannot accept a set -DROP TABLE customer_reviews; -DROP TABLE popular_reviewer; diff --git a/src/test/regress/expected/distributed_types_xact_add_enum_value_0.out b/src/test/regress/expected/distributed_types_xact_add_enum_value_0.out deleted file mode 100644 index 398c616c5..000000000 --- a/src/test/regress/expected/distributed_types_xact_add_enum_value_0.out +++ /dev/null @@ -1,75 +0,0 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 11 AS version_above_eleven; - version_above_eleven ---------------------------------------------------------------------- - f -(1 row) - -SET citus.next_shard_id TO 20040000; -CREATE SCHEMA xact_enum_type; -SET search_path TO xact_enum_type; -SET citus.shard_count TO 4; --- transaction block with simple type -BEGIN; -CREATE TYPE xact_enum_edit AS ENUM ('yes', 'no'); -CREATE TABLE t1 (a int PRIMARY KEY, b xact_enum_edit); -SELECT create_distributed_table('t1','a'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO t1 VALUES (1, 'yes'); -SELECT * FROM t1; - a | b ---------------------------------------------------------------------- - 1 | yes -(1 row) - -COMMIT; -BEGIN; -ALTER TYPE xact_enum_edit ADD VALUE 'maybe'; -ERROR: ALTER TYPE ... ADD cannot run inside a transaction block -ABORT; --- maybe should not be on the workers -SELECT string_agg(enumlabel, ',' ORDER BY enumsortorder ASC) FROM pg_enum WHERE enumtypid = 'xact_enum_type.xact_enum_edit'::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 = 'xact_enum_type.xact_enum_edit'::regtype;$$); - run_command_on_workers ---------------------------------------------------------------------- - (localhost,57637,t,"yes,no") - (localhost,57638,t,"yes,no") -(2 rows) - -BEGIN; -ALTER TYPE xact_enum_edit ADD VALUE 'maybe'; -ERROR: ALTER TYPE ... ADD cannot run inside a transaction block -COMMIT; --- maybe should be on the workers (pg12 and above) -SELECT string_agg(enumlabel, ',' ORDER BY enumsortorder ASC) FROM pg_enum WHERE enumtypid = 'xact_enum_type.xact_enum_edit'::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 = 'xact_enum_type.xact_enum_edit'::regtype;$$); - run_command_on_workers ---------------------------------------------------------------------- - (localhost,57637,t,"yes,no") - (localhost,57638,t,"yes,no") -(2 rows) - --- clear objects -SET client_min_messages TO error; -- suppress cascading objects dropping -DROP SCHEMA xact_enum_type CASCADE; -SELECT run_command_on_workers($$DROP SCHEMA xact_enum_type CASCADE;$$); - run_command_on_workers ---------------------------------------------------------------------- - (localhost,57637,t,"DROP SCHEMA") - (localhost,57638,t,"DROP SCHEMA") -(2 rows) - diff --git a/src/test/regress/expected/failure_cte_subquery.out b/src/test/regress/expected/failure_cte_subquery.out index 866c591ac..98b1a0b04 100644 --- a/src/test/regress/expected/failure_cte_subquery.out +++ b/src/test/regress/expected/failure_cte_subquery.out @@ -5,7 +5,7 @@ SET citus.shard_replication_factor to 1; SET citus.next_shard_id TO 16000000; -- CTE inlining should not happen because -- the tests rely on intermediate results -SET citus.enable_cte_inlining TO false; +-- That's why we use MATERIALIZED CTEs in the test file -- prevent using locally executing the intermediate results SET citus.task_assignment_policy TO "round-robin"; SELECT pg_backend_pid() as pid \gset @@ -31,11 +31,11 @@ SELECT citus.mitmproxy('conn.onQuery(query="^COPY").kill()'); (1 row) -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -65,11 +65,11 @@ SELECT citus.mitmproxy('conn.onQuery(query="SELECT user_id FROM cte_failure.even (1 row) -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -98,11 +98,11 @@ SELECT citus.mitmproxy('conn.onQuery(query="SELECT DISTINCT users_table.user").k (1 row) -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -131,11 +131,11 @@ SELECT citus.mitmproxy('conn.onQuery(query="^COPY").cancel(' || :pid || ')'); (1 row) -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -162,11 +162,11 @@ SELECT citus.mitmproxy('conn.onQuery(query="SELECT user_id FROM").cancel(' || :p (1 row) -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -193,11 +193,11 @@ SELECT citus.mitmproxy('conn.onQuery(query="SELECT DISTINCT users_table.user").c (1 row) -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -238,7 +238,7 @@ SELECT * FROM users_table ORDER BY 1, 2; (5 rows) -- following will delete and insert the same rows -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; -- verify contents are the same SELECT * FROM users_table ORDER BY 1, 2; @@ -258,7 +258,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="^DELETE FROM").kill()'); (1 row) -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; ERROR: connection to the remote node localhost:xxxxx failed with the following error: server closed the connection unexpectedly This probably means the server terminated abnormally @@ -287,7 +287,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="^COPY").kill()'); (1 row) -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; ERROR: server closed the connection unexpectedly This probably means the server terminated abnormally @@ -317,7 +317,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="^DELETE FROM").cancel(' || :pid || ' (1 row) -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; ERROR: canceling statement due to user request -- verify contents are the same @@ -344,7 +344,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="^COPY").cancel(' || :pid || ')'); (1 row) -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; ERROR: canceling statement due to user request -- verify contents are the same @@ -373,7 +373,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="^DELETE FROM").kill()'); BEGIN; SET LOCAL citus.multi_shard_modify_mode = 'sequential'; -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; ERROR: connection to the remote node localhost:xxxxx failed with the following error: server closed the connection unexpectedly This probably means the server terminated abnormally diff --git a/src/test/regress/expected/intermediate_result_pruning.out b/src/test/regress/expected/intermediate_result_pruning.out index 6977a3caf..7de87b86c 100644 --- a/src/test/regress/expected/intermediate_result_pruning.out +++ b/src/test/regress/expected/intermediate_result_pruning.out @@ -32,8 +32,6 @@ SELECT create_reference_table('ref_table'); (1 row) --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -- load some data INSERT INTO table_1 VALUES (1, '1'), (2, '2'), (3, '3'), (4, '4'); INSERT INTO table_2 VALUES (3, '3'), (4, '4'), (5, '5'), (6, '6'); @@ -43,7 +41,7 @@ INSERT INTO ref_table VALUES (1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), SET client_min_messages TO DEBUG1; -- a very basic case, where the intermediate result -- should go to both workers -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key FROM table_1 WHERE value IN ('3', '4')) SELECT count(*) @@ -61,7 +59,7 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx -- a very basic case, where the intermediate result -- should only go to one worker because the final query is a router -- we use random() to prevent postgres inline the CTE(s) -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')) SELECT count(*) @@ -78,7 +76,7 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx -- a similar query, but with a reference table now -- given that reference tables are replicated to all nodes -- we have to broadcast to all nodes -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')) SELECT count(*) @@ -95,9 +93,9 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx -- a similar query as above, but this time use the CTE inside -- another CTE -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1) SELECT count(*) @@ -115,9 +113,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- the second CTE does a join with a distributed table -- and the final query is a router query -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key)) SELECT count(*) @@ -137,9 +135,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- the first CTE is used both within second CTE and the final query -- the second CTE does a join with a distributed table -- and the final query is a router query -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key)) SELECT count(*) @@ -159,9 +157,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- the first CTE is used both within second CTE and the final query -- the second CTE does a join with a distributed table but a router query on a worker -- and the final query is another router query on another worker -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE table_2.key = 1) SELECT count(*) @@ -182,9 +180,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- the second CTE does a join with a distributed table but a router query on a worker -- and the final query is a router query on the same worker, so the first result is only -- broadcasted to a single node -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE table_2.key = 1) SELECT count(*) @@ -201,9 +199,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx (1 row) -- the same query with the above, but the final query is hitting all shards -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key)) SELECT count(*) @@ -224,9 +222,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- even if we add a filter on the first query and make it a router query, -- the first intermediate result still hits all workers because of the final -- join is hitting all workers -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE table_2.key = 3) SELECT count(*) @@ -247,7 +245,7 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- the reference table is joined with a distributed table and an intermediate -- result, but the distributed table hits all shards, so the intermediate -- result is sent to all nodes -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM ref_table WHERE value IN ('3', '4')) SELECT count(*) @@ -264,7 +262,7 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx -- similar query as above, but this time the whole query is a router -- query, so no intermediate results -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM ref_table WHERE value IN ('3', '4')) SELECT count(*) @@ -279,9 +277,9 @@ FROM -- so the first CTE should only be broadcasted to that node -- since the final query doesn't have a join, it should simply be broadcasted -- to one node -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE key = 1) SELECT count(*) @@ -299,10 +297,10 @@ DEBUG: Subplan XXX_2 will be written to local file -- the same query inlined inside a CTE, and the final query has a -- join with a distributed table -WITH top_cte as ( - WITH some_values_1 AS +WITH top_cte as MATERIALIZED ( + WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE key = 1) SELECT DISTINCT key @@ -313,7 +311,7 @@ SELECT count(*) FROM top_cte JOIN table_2 USING (key); -DEBUG: generating subplan XXX_1 for CTE top_cte: WITH some_values_1 AS (SELECT table_1.key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (table_1.value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text]))), some_values_2 AS (SELECT some_values_1.key, random() AS random FROM (some_values_1 JOIN intermediate_result_pruning.table_2 USING (key)) WHERE (some_values_1.key OPERATOR(pg_catalog.=) 1)) SELECT DISTINCT key FROM some_values_2 +DEBUG: generating subplan XXX_1 for CTE top_cte: WITH some_values_1 AS MATERIALIZED (SELECT table_1.key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (table_1.value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text]))), some_values_2 AS MATERIALIZED (SELECT some_values_1.key, random() AS random FROM (some_values_1 JOIN intermediate_result_pruning.table_2 USING (key)) WHERE (some_values_1.key OPERATOR(pg_catalog.=) 1)) SELECT DISTINCT key FROM some_values_2 DEBUG: generating subplan XXX_1 for CTE some_values_1: SELECT key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text])) DEBUG: generating subplan XXX_2 for CTE some_values_2: SELECT some_values_1.key, random() AS random FROM ((SELECT intermediate_result.key, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, random double precision)) some_values_1 JOIN intermediate_result_pruning.table_2 USING (key)) WHERE (some_values_1.key OPERATOR(pg_catalog.=) 1) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT key FROM (SELECT intermediate_result.key, intermediate_result.random FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, random double precision)) some_values_2 @@ -329,10 +327,10 @@ DEBUG: Subplan XXX_2 will be written to local file -- very much the same query, but this time the top query is also a router query -- on a single worker, so all intermediate results only hit a single node -WITH top_cte as ( - WITH some_values_1 AS +WITH top_cte as MATERIALIZED ( + WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE key = 1) SELECT DISTINCT key @@ -343,7 +341,7 @@ SELECT count(*) FROM top_cte JOIN table_2 USING (key) WHERE table_2.key = 2; -DEBUG: generating subplan XXX_1 for CTE top_cte: WITH some_values_1 AS (SELECT table_1.key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (table_1.value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text]))), some_values_2 AS (SELECT some_values_1.key, random() AS random FROM (some_values_1 JOIN intermediate_result_pruning.table_2 USING (key)) WHERE (some_values_1.key OPERATOR(pg_catalog.=) 1)) SELECT DISTINCT key FROM some_values_2 +DEBUG: generating subplan XXX_1 for CTE top_cte: WITH some_values_1 AS MATERIALIZED (SELECT table_1.key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (table_1.value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text]))), some_values_2 AS MATERIALIZED (SELECT some_values_1.key, random() AS random FROM (some_values_1 JOIN intermediate_result_pruning.table_2 USING (key)) WHERE (some_values_1.key OPERATOR(pg_catalog.=) 1)) SELECT DISTINCT key FROM some_values_2 DEBUG: generating subplan XXX_1 for CTE some_values_1: SELECT key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text])) DEBUG: generating subplan XXX_2 for CTE some_values_2: SELECT some_values_1.key, random() AS random FROM ((SELECT intermediate_result.key, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, random double precision)) some_values_1 JOIN intermediate_result_pruning.table_2 USING (key)) WHERE (some_values_1.key OPERATOR(pg_catalog.=) 1) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT key FROM (SELECT intermediate_result.key, intermediate_result.random FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, random double precision)) some_values_2 @@ -358,11 +356,11 @@ DEBUG: Subplan XXX_2 will be written to local file -- some_values_1 is first used by a single shard-query, and than with a multi-shard -- CTE, finally a cartesian product join -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE key = 1), - some_values_3 AS + some_values_3 AS MATERIALIZED (SELECT key FROM (some_values_2 JOIN table_2 USING (key)) JOIN some_values_1 USING (key)) SELECT * FROM some_values_3 JOIN ref_table ON (true); DEBUG: generating subplan XXX_1 for CTE some_values_1: SELECT key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text])) @@ -381,9 +379,9 @@ DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx -- join on intermediate results, so should only -- go to a single node -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM table_2 WHERE value IN ('3', '4')) SELECT count(*) FROM some_values_2 JOIN some_values_1 USING (key); DEBUG: generating subplan XXX_1 for CTE some_values_1: SELECT key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text])) @@ -398,9 +396,9 @@ DEBUG: Subplan XXX_2 will be written to local file -- same query with WHERE false make sure that we're not broken -- for such edge cases -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM table_2 WHERE value IN ('3', '4')) SELECT count(*) FROM some_values_2 JOIN some_values_1 USING (key) WHERE false; DEBUG: generating subplan XXX_1 for CTE some_values_1: SELECT key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text])) @@ -415,11 +413,11 @@ DEBUG: Subplan XXX_2 will be written to local file -- do not use some_values_2 at all, so only 2 intermediate results are -- broadcasted -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1), - some_values_3 AS + some_values_3 AS MATERIALIZED (SELECT key, random() FROM some_values_1) SELECT count(*) @@ -566,13 +564,13 @@ DEBUG: Subplan XXX_2 will be written to local file (0 rows) -- the intermediate results should just hit a single worker -WITH cte_1 AS +WITH cte_1 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 1) INTERSECT (SELECT key FROM table_1 WHERE key = 2) ), -cte_2 AS +cte_2 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 3) INTERSECT @@ -599,13 +597,13 @@ DEBUG: Subplan XXX_2 will be written to local file -- we join the results with distributed tables -- so cte_1 should hit all workers, but still the -- others should hit single worker each -WITH cte_1 AS +WITH cte_1 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 1) INTERSECT (SELECT key FROM table_1 WHERE key = 2) ), -cte_2 AS +cte_2 AS MATERIALIZED ( SELECT count(*) FROM table_1 JOIN cte_1 USING (key) ) @@ -668,10 +666,10 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx -- however, the subquery in WHERE clause of the DELETE query is broadcasted to all -- nodes BEGIN; -WITH select_data AS ( +WITH select_data AS MATERIALIZED ( SELECT * FROM table_1 ), -raw_data AS ( +raw_data AS MATERIALIZED ( DELETE FROM table_2 WHERE key >= (SELECT min(key) FROM select_data WHERE key > 1) RETURNING * ) SELECT * FROM raw_data; @@ -698,10 +696,10 @@ ROLLBACK; -- however, the subquery in WHERE clause of the DELETE query is broadcasted to all -- nodes BEGIN; -WITH select_data AS ( +WITH select_data AS MATERIALIZED ( SELECT * FROM table_1 ), -raw_data AS ( +raw_data AS MATERIALIZED ( DELETE FROM table_2 WHERE value::int >= (SELECT min(key) FROM select_data WHERE key > 1 + random()) RETURNING * ) SELECT * FROM raw_data; @@ -726,10 +724,10 @@ ROLLBACK; -- now, we need only two intermediate results as the subquery in WHERE clause is -- router plannable BEGIN; -WITH select_data AS ( +WITH select_data AS MATERIALIZED ( SELECT * FROM table_1 ), -raw_data AS ( +raw_data AS MATERIALIZED ( DELETE FROM table_2 WHERE value::int >= (SELECT min(key) FROM table_1 WHERE key > random()) AND key = 6 RETURNING * ) SELECT * FROM raw_data; @@ -766,13 +764,13 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx INSERT INTO table_1 SELECT * FROM table_2 where key = 1 AND value::int IN - (WITH cte_1 AS + (WITH cte_1 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 1) INTERSECT (SELECT key FROM table_1 WHERE key = 2) ), - cte_2 AS + cte_2 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 3) INTERSECT @@ -800,13 +798,13 @@ DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx -- hits all the shards because table_2.key != 1 INSERT INTO table_1 SELECT table_2.* FROM table_2, - (WITH cte_1 AS + (WITH cte_1 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 1) INTERSECT (SELECT key FROM table_1 WHERE key = 2) ), - cte_2 AS + cte_2 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 3) INTERSECT @@ -924,8 +922,9 @@ FROM WHERE range_column IN ('A', 'E') AND range_partitioned.data IN (SELECT data FROM some_data); -DEBUG: generating subplan XXX_1 for CTE some_data: SELECT data FROM intermediate_result_pruning.range_partitioned -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM intermediate_result_pruning.range_partitioned WHERE ((range_column OPERATOR(pg_catalog.=) ANY (ARRAY['A'::text, 'E'::text])) AND (data OPERATOR(pg_catalog.=) ANY (SELECT some_data.data FROM (SELECT intermediate_result.data FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(data integer)) some_data))) +DEBUG: CTE some_data is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT data FROM (SELECT range_partitioned.data FROM intermediate_result_pruning.range_partitioned) some_data +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM intermediate_result_pruning.range_partitioned WHERE ((range_column OPERATOR(pg_catalog.=) ANY (ARRAY['A'::text, 'E'::text])) AND (data OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.data FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(data integer)))) DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx count @@ -955,16 +954,16 @@ INSERT INTO stats (account_id, spent) VALUES ('foo', 100); SELECT * FROM ( - WITH accounts_cte AS ( + WITH accounts_cte AS MATERIALIZED ( SELECT id AS account_id FROM accounts ), - joined_stats_cte_1 AS ( + joined_stats_cte_1 AS MATERIALIZED ( SELECT spent, account_id FROM stats INNER JOIN accounts_cte USING (account_id) ), - joined_stats_cte_2 AS ( + joined_stats_cte_2 AS MATERIALIZED ( SELECT spent, account_id FROM joined_stats_cte_1 INNER JOIN accounts_cte USING (account_id) @@ -992,16 +991,16 @@ SET citus.task_assignment_policy to 'round-robin'; SELECT * FROM ( - WITH accounts_cte AS ( + WITH accounts_cte AS MATERIALIZED ( SELECT id AS account_id FROM accounts ), - joined_stats_cte_1 AS ( + joined_stats_cte_1 AS MATERIALIZED ( SELECT spent, account_id FROM stats INNER JOIN accounts_cte USING (account_id) ), - joined_stats_cte_2 AS ( + joined_stats_cte_2 AS MATERIALIZED ( SELECT spent, account_id FROM joined_stats_cte_1 INNER JOIN accounts_cte USING (account_id) @@ -1027,10 +1026,10 @@ RESET citus.task_assignment_policy; -- Insert..select is planned differently, make sure we have results everywhere. -- We put the insert..select in a CTE here to prevent the CTE from being moved -- into the select, which would follow the regular code path for select. -WITH stats AS ( +WITH stats AS MATERIALIZED ( SELECT count(key) m FROM table_3 ), -inserts AS ( +inserts AS MATERIALIZED ( INSERT INTO table_2 SELECT key, count(*) FROM table_1 diff --git a/src/test/regress/expected/limit_intermediate_size.out b/src/test/regress/expected/limit_intermediate_size.out index 66b2342fd..d4c60d78f 100644 --- a/src/test/regress/expected/limit_intermediate_size.out +++ b/src/test/regress/expected/limit_intermediate_size.out @@ -1,13 +1,11 @@ SET citus.enable_repartition_joins to ON; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; SET citus.max_intermediate_result_size TO 2; -- should fail because the copy size is ~4kB for each cte -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT * FROM users_table ), -cte2 AS ( +cte2 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT cte.user_id, cte.value_2 FROM cte,cte2 ORDER BY 1,2 LIMIT 10; @@ -15,7 +13,7 @@ ERROR: the intermediate result size exceeds citus.max_intermediate_result_size DETAIL: Citus restricts the size of intermediate results of complex subqueries and CTEs to avoid accidentally pulling large result sets into once place. HINT: To run the current query, set citus.max_intermediate_result_size to a higher value or -1 to disable. SET citus.max_intermediate_result_size TO 9; -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT users_table.user_id, users_table.value_1, users_table.value_2 @@ -26,7 +24,7 @@ WITH cte AS on (users_table.value_3=events_table.value_3) ), -cte2 AS ( +cte2 AS MATERIALIZED( SELECT * FROM events_table ) SELECT @@ -53,7 +51,7 @@ LIMIT 10; -- router queries should be able to get limitted too SET citus.max_intermediate_result_size TO 2; -- this should pass, since we fetch small portions in each subplan -with cte as (select * from users_table where user_id=1), +with cte as MATERIALIZED (select * from users_table where user_id=1), cte2 as (select * from users_table where user_id=2), cte3 as (select * from users_table where user_id=3), cte4 as (select * from users_table where user_id=4), @@ -75,18 +73,18 @@ UNION (1 row) -- if we fetch the same amount of data at once, it should fail -WITH cte AS (SELECT * FROM users_table WHERE user_id IN (1,2,3,4,5)) +WITH cte AS MATERIALIZED (SELECT * FROM users_table WHERE user_id IN (1,2,3,4,5)) SELECT * FROM cte ORDER BY 1,2,3,4,5 LIMIT 10; ERROR: the intermediate result size exceeds citus.max_intermediate_result_size (currently 2 kB) DETAIL: Citus restricts the size of intermediate results of complex subqueries and CTEs to avoid accidentally pulling large result sets into once place. HINT: To run the current query, set citus.max_intermediate_result_size to a higher value or -1 to disable. SET citus.max_intermediate_result_size TO 0; -- this should fail -WITH cte AS (SELECT * FROM users_table WHERE user_id=1), -cte2 AS (SELECT * FROM users_table WHERE user_id=2), -cte3 AS (SELECT * FROM users_table WHERE user_id=3), -cte4 AS (SELECT * FROM users_table WHERE user_id=4), -cte5 AS (SELECT * FROM users_table WHERE user_id=5) +WITH cte AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=1), +cte2 AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=2), +cte3 AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=3), +cte4 AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=4), +cte5 AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=5) SELECT * FROM ( (SELECT * FROM cte) UNION @@ -104,11 +102,11 @@ HINT: To run the current query, set citus.max_intermediate_result_size to a hig -- this fails since cte-subplan exceeds limit even if cte2 and cte3 don't -- WHERE EXISTS forces materialization in pg12 SET citus.max_intermediate_result_size TO 4; -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table ), - cte3 AS ( + cte3 AS MATERIALIZED( SELECT * FROM events_table ) SELECT * FROM cte2, cte3 WHERE cte2.user_id = cte3.user_id AND cte2.user_id = 1 @@ -123,11 +121,11 @@ SELECT count(*) FROM cte WHERE EXISTS (select * from cte); SET citus.max_intermediate_result_size TO 3; -- this should fail since the cte-subplan exceeds the limit even if the -- cte2 and cte3 does not -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (3,4,5,6) ), - cte3 AS ( + cte3 AS MATERIALIZED( SELECT * FROM events_table WHERE event_type = 1 ) SELECT * FROM cte2, cte3 WHERE cte2.value_1 IN (SELECT value_2 FROM cte3) @@ -138,16 +136,16 @@ DETAIL: Citus restricts the size of intermediate results of complex subqueries HINT: To run the current query, set citus.max_intermediate_result_size to a higher value or -1 to disable. -- this will fail in remote execution SET citus.max_intermediate_result_size TO 2; -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (1, 2) ), - cte3 AS ( + cte3 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 3 ) SELECT * FROM cte2 UNION (SELECT * FROM cte3) ), -cte4 AS ( +cte4 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT * FROM cte UNION ALL @@ -157,16 +155,16 @@ DETAIL: Citus restricts the size of intermediate results of complex subqueries HINT: To run the current query, set citus.max_intermediate_result_size to a higher value or -1 to disable. SET citus.max_intermediate_result_size TO 1; -- this will fail in router_executor -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (1, 2) ), - cte3 AS ( + cte3 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 3 ) SELECT * FROM cte2 UNION (SELECT * FROM cte3) ), -cte4 AS ( +cte4 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT * FROM cte UNION ALL @@ -174,19 +172,19 @@ SELECT * FROM cte4 ORDER BY 1,2,3,4,5 LIMIT 5; ERROR: the intermediate result size exceeds citus.max_intermediate_result_size (currently 1 kB) DETAIL: Citus restricts the size of intermediate results of complex subqueries and CTEs to avoid accidentally pulling large result sets into once place. HINT: To run the current query, set citus.max_intermediate_result_size to a higher value or -1 to disable. --- Below that, all should pass since -1 disables the limit +-- Below that, all should pAS MATERIALIZEDs since -1 disables the limit SET citus.max_intermediate_result_size TO -1; -- real_time_executor + router_executor + real_time_executor will pass -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (1, 2) ), - cte3 AS ( + cte3 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 3 ) SELECT * FROM cte2 UNION (SELECT * FROM cte3) ), -cte4 AS ( +cte4 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT * FROM cte UNION ALL @@ -201,7 +199,7 @@ SELECT * FROM cte4 ORDER BY 1,2,3,4,5 LIMIT 5; (5 rows) -- regular adaptive executor CTE, should work since -1 disables the limit -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT users_table.user_id, users_table.value_1, users_table.value_2 @@ -212,7 +210,7 @@ WITH cte AS on (users_table.value_2=events_table.value_2) ), -cte2 AS ( +cte2 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT @@ -237,11 +235,11 @@ LIMIT 10; (10 rows) -- regular real-time CTE fetches around ~4kb data in each subplan -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT * FROM users_table ), -cte2 AS ( +cte2 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT cte.user_id, cte.value_2 FROM cte,cte2 ORDER BY 1,2 LIMIT 10; @@ -260,7 +258,7 @@ SELECT cte.user_id, cte.value_2 FROM cte,cte2 ORDER BY 1,2 LIMIT 10; (10 rows) -- regular real-time query fetches ~4kB -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (1,2,3,4,5) ) @@ -280,11 +278,11 @@ SELECT * FROM cte ORDER BY 1,2,3,4,5 LIMIT 10; (10 rows) -- nested CTEs -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table ), - cte3 AS ( + cte3 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT diff --git a/src/test/regress/expected/locally_execute_intermediate_results.out b/src/test/regress/expected/locally_execute_intermediate_results.out index b148a6620..d6a52bd17 100644 --- a/src/test/regress/expected/locally_execute_intermediate_results.out +++ b/src/test/regress/expected/locally_execute_intermediate_results.out @@ -34,14 +34,12 @@ INSERT INTO table_2 VALUES (3, '3'), (4, '4'), (5, '5'), INSERT INTO ref_table VALUES (1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), (6, '6'); NOTICE: executing the command locally: INSERT INTO locally_execute_intermediate_results.ref_table_1580008 AS citus_table_alias (key, value) VALUES (1,'1'::text), (2,'2'::text), (3,'3'::text), (4,'4'::text), (5,'5'::text), (6,'6'::text) INSERT INTO local_table VALUES (3, '3'), (4, '4'), (5, '5'), (6, '6'); --- prevent PG 11 - PG 12 outputs to diverge -- and have a lot more CTEs recursively planned for the -- sake of increasing the test coverage -SET citus.enable_cte_inlining TO false; SET client_min_messages TO DEBUG1; -- the query cannot be executed locally, but still because of -- HAVING the intermediate result is written to local file as well -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -58,7 +56,7 @@ DEBUG: Subplan XXX_1 will be written to local file (2 rows) -- in this case, the HAVING Is also pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -76,8 +74,8 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx (2 rows) -- subquery in the WHERE part of the query can be executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -104,8 +102,8 @@ NOTICE: executing the command locally: SELECT key FROM (SELECT intermediate_res -- subquery in the WHERE part of the query should not be executed locally -- because it can be pushed down with the jointree -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2) SELECT count(*) FROM @@ -126,9 +124,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx (0 rows) -- now all the intermediate results are safe to be in local files -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -151,8 +149,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM (SELECT in -- multiple CTEs are joined inside HAVING, so written to file -- locally, but nothing executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -171,8 +169,8 @@ DEBUG: Subplan XXX_2 will be written to local file (2 rows) -- same as above, but HAVING pushed down to workers -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -195,9 +193,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- multiple CTEs are joined inside HAVING, so written to file -- locally, also the join tree contains only another CTE, so should be -- executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -221,7 +219,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM (SELECT in -- now, the CTE is going to be written locally, -- plus that is going to be read locally because -- of the aggragate over the cte in HAVING -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -241,7 +239,7 @@ NOTICE: executing the command locally: SELECT max(max) AS max FROM (SELECT inte (2 rows) -- same as above, but with HAVING pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -262,8 +260,8 @@ NOTICE: executing the command locally: SELECT max(max) AS max FROM (SELECT inte (2 rows) -- two ctes are going to be written locally and executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*) FROM @@ -288,7 +286,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM (SELECT in -- this time the same CTE is both joined with a distributed -- table and used in HAVING -WITH a AS (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) +WITH a AS MATERIALIZED (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -307,7 +305,7 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx -- this time the same CTE is both joined with a distributed -- table and used in HAVING -- but used in another subquery/aggregate -- so one more level of recursive planning -WITH a AS (SELECT * FROM table_1) +WITH a AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -327,13 +325,13 @@ NOTICE: executing the command locally: SELECT max(value) AS max FROM (SELECT in (1 row) -- same query as the above, without the aggragate -WITH a AS (SELECT max(key) as key, max(value) as value FROM ref_table) +WITH a AS MATERIALIZED (SELECT max(key) as key, max(value) as value FROM ref_table) SELECT count(*), key FROM a JOIN ref_table USING (key) GROUP BY key HAVING (max(ref_table.value) <= (SELECT value FROM a)); -NOTICE: executing the command locally: WITH a AS (SELECT max(ref_table_1.key) AS key, max(ref_table_1.value) AS value FROM locally_execute_intermediate_results.ref_table_1580008 ref_table_1) SELECT count(*) AS count, a.key FROM (a JOIN locally_execute_intermediate_results.ref_table_1580008 ref_table(key, value) USING (key)) GROUP BY a.key HAVING (max(ref_table.value) OPERATOR(pg_catalog.<=) (SELECT a_1.value FROM a a_1)) +NOTICE: executing the command locally: WITH a AS MATERIALIZED (SELECT max(ref_table_1.key) AS key, max(ref_table_1.value) AS value FROM locally_execute_intermediate_results.ref_table_1580008 ref_table_1) SELECT count(*) AS count, a.key FROM (a JOIN locally_execute_intermediate_results.ref_table_1580008 ref_table(key, value) USING (key)) GROUP BY a.key HAVING (max(ref_table.value) OPERATOR(pg_catalog.<=) (SELECT a_1.value FROM a a_1)) count | key --------------------------------------------------------------------- 1 | 6 @@ -341,9 +339,9 @@ NOTICE: executing the command locally: WITH a AS (SELECT max(ref_table_1.key) A -- some edge cases around CTEs used inside other CTEs -- everything can be executed locally -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) FROM cte_2) SELECT * FROM cte_3; DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 @@ -361,9 +359,9 @@ NOTICE: executing the command locally: SELECT max FROM (SELECT intermediate_res (1 row) -- the join between cte_3 and table_2 has to happen remotely -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 1; DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 @@ -379,9 +377,9 @@ NOTICE: executing the command locally: SELECT max(key) AS key FROM (SELECT inte (0 rows) -- the join between cte_3 and table_2 has to happen remotely -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN ref_table USING (key); DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 @@ -402,9 +400,9 @@ NOTICE: executing the command locally: SELECT cte_3.key, ref_table.value FROM ( -- some cases around router queries -- a router query, but the having has two cte joins -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 WHERE KEY = 3 @@ -422,9 +420,9 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- a router query, but the having has two cte joins -- and the jointree has a join with another cte -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) WHERE KEY = 3 @@ -444,9 +442,9 @@ DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx -- a router query, but the having has two cte joins -- and the jointree has a join with the same CTEs -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) JOIN cte_2 ON (key = MAX::int) JOIN cte_1 USING(MAX) WHERE KEY = 3 @@ -465,8 +463,8 @@ DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx (0 rows) -- subPlans needed remotely as the subquery is pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT key FROM table_1 GROUP BY key HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT key FROM table_2 GROUP BY key HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -484,8 +482,8 @@ DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -- the second subquery needs to be recursively planned due to non-colocated subquery join -- so cte_2 becomes part of master query of that recursive subquery planning -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -505,8 +503,8 @@ NOTICE: executing the command locally: SELECT foo.key, bar.key FROM (SELECT int (0 rows) -- similar to above, but having pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT key FROM table_1 GROUP BY key HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT key FROM table_2 GROUP BY key HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -526,8 +524,8 @@ DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx (0 rows) -- now, forcing all subqueries to be on the local node -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1) LIMIT 1) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2) LIMIT 1) as bar @@ -547,7 +545,7 @@ NOTICE: executing the command locally: SELECT foo.key, bar.key FROM (SELECT int (0 rows) -- queries in which the last step has only CTEs can use local tables -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -564,8 +562,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM locally_ex 1 (2 rows) -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -595,13 +593,9 @@ SET search_path TO locally_execute_intermediate_results; SET citus.log_intermediate_results TO TRUE; SET citus.log_local_commands TO TRUE; SET client_min_messages TO DEBUG1; --- prevent PG 11 - PG 12 outputs to diverge --- and have a lot more CTEs recursively planned for the --- sake of increasing the test coverage -SET citus.enable_cte_inlining TO false; -- the query cannot be executed locally, but still because of -- HAVING the intermediate result is written to local file as well -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -623,8 +617,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- On non-mx case the subquery in the WHERE part of the query can be executed locally -- however, on Citus MX we have this limitation where the query cannot be executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -656,8 +650,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- subquery in the WHERE part of the query should not be executed locally -- because it can be pushed down with the jointree -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2) SELECT count(*) FROM @@ -685,9 +679,9 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- although all the intermediate results are safe to be in local files -- we currently do not support it on Citus MX -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -716,8 +710,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM (SELECT in -- multiple CTEs are joined inside HAVING, so written to file -- locally, but nothing executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -744,9 +738,9 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- multiple CTEs are joined inside HAVING, so written to file -- locally, also the join tree contains only another CTE, so should be -- executed locally, but not on an Citus MX worker -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -777,7 +771,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM (SELECT in -- plus that could have been read locally on the coordinator -- because of the aggragate over the cte in HAVING -- but not on Citus MX -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -802,8 +796,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- two could have been written locally and executed locally -- on the coordinator, but not on the workers -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*) FROM @@ -832,7 +826,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM (SELECT in -- this time the same CTE is both joined with a distributed -- table and used in HAVING -WITH a AS (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) +WITH a AS MATERIALIZED (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -854,7 +848,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- this time the same CTE is both joined with a distributed -- table and used in HAVING -- but used in another subquery/aggregate -- so one more level of recursive planning -WITH a AS (SELECT * FROM table_1) +WITH a AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -877,13 +871,13 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ (1 row) -- same query as the above, without the aggragate -WITH a AS (SELECT max(key) as key, max(value) as value FROM ref_table) +WITH a AS MATERIALIZED (SELECT max(key) as key, max(value) as value FROM ref_table) SELECT count(*), key FROM a JOIN ref_table USING (key) GROUP BY key HAVING (max(ref_table.value) <= (SELECT value FROM a)); -NOTICE: executing the command locally: WITH a AS (SELECT max(ref_table_1.key) AS key, max(ref_table_1.value) AS value FROM locally_execute_intermediate_results.ref_table_1580008 ref_table_1) SELECT count(*) AS count, a.key FROM (a JOIN locally_execute_intermediate_results.ref_table_1580008 ref_table(key, value) USING (key)) GROUP BY a.key HAVING (max(ref_table.value) OPERATOR(pg_catalog.<=) (SELECT a_1.value FROM a a_1)) +NOTICE: executing the command locally: WITH a AS MATERIALIZED (SELECT max(ref_table_1.key) AS key, max(ref_table_1.value) AS value FROM locally_execute_intermediate_results.ref_table_1580008 ref_table_1) SELECT count(*) AS count, a.key FROM (a JOIN locally_execute_intermediate_results.ref_table_1580008 ref_table(key, value) USING (key)) GROUP BY a.key HAVING (max(ref_table.value) OPERATOR(pg_catalog.<=) (SELECT a_1.value FROM a a_1)) count | key --------------------------------------------------------------------- 1 | 6 @@ -892,9 +886,9 @@ NOTICE: executing the command locally: WITH a AS (SELECT max(ref_table_1.key) A -- some edge cases around CTEs used inside other CTEs -- everything could be executed locally on the coordinator, -- but not on the worker -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) FROM cte_2) SELECT * FROM cte_3; DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 @@ -916,9 +910,9 @@ NOTICE: executing the command locally: SELECT max FROM (SELECT intermediate_res -- the join between cte_3 and table_2 has to could have happened -- locally since the key = 1 resides on this node -- but because of the current implementation limitations we can't -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 1; DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 @@ -938,9 +932,9 @@ NOTICE: executing the command locally: SELECT cte_3.key, table_2.value FROM ((S -- the join between cte_3 and table_2 has to cannot happen -- locally because the key = 2 resides on a remote node -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 2; DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 @@ -959,9 +953,9 @@ NOTICE: executing the command locally: SELECT max(key) AS key FROM (SELECT inte -- the join between cte_3 and ref can could have happened locally -- but because of the current implementation limitations we can't -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN ref_table USING (key); DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 @@ -984,9 +978,9 @@ NOTICE: executing the command locally: SELECT cte_3.key, ref_table.value FROM ( -- some cases around router queries -- a router query, but the having has two cte joins -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 WHERE KEY = 3 @@ -1008,9 +1002,9 @@ NOTICE: executing the command locally: SELECT max(value) AS max FROM locally_ex -- a router query, but the having has two cte joins -- and the jointree has a join with another cte -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) WHERE KEY = 3 @@ -1036,9 +1030,9 @@ NOTICE: executing the command locally: SELECT key, value FROM locally_execute_i -- a router query, but the having has two cte joins -- and the jointree has a join with the same CTEs -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) JOIN cte_2 ON (key = MAX::int) JOIN cte_1 USING(MAX) WHERE KEY = 3 @@ -1062,8 +1056,8 @@ NOTICE: executing the command locally: SELECT key, value FROM locally_execute_i (0 rows) -- subPlans needed remotely as the subquery is pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT key FROM table_1 GROUP BY key HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT key FROM table_2 GROUP BY key HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -1087,8 +1081,8 @@ NOTICE: executing the command locally: SELECT worker_column_1 AS key, worker_co -- the second subquery needs to be recursively planned due to non-colocated subquery join -- so cte_2 becomes part of master query of that recursive subquery planning -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -1116,8 +1110,8 @@ NOTICE: executing the command locally: SELECT foo.key, bar.key FROM (SELECT int (0 rows) -- now, forcing all subqueries to be on the local node -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1) LIMIT 1) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2) LIMIT 1) as bar @@ -1148,7 +1142,7 @@ NOTICE: executing the command locally: SELECT foo.key, bar.key FROM (SELECT int set citus.task_assignment_policy TO "round-robin" ; -- the query cannot be executed locally, but still because of -- HAVING the intermediate result is written to local file as well -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -1170,8 +1164,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- On non-mx case the subquery in the WHERE part of the query can be executed locally -- however, on Citus MX we have this limitation where the query cannot be executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -1203,8 +1197,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- subquery in the WHERE part of the query should not be executed locally -- because it can be pushed down with the jointree -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2) SELECT count(*) FROM @@ -1232,9 +1226,9 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- although all the intermediate results are safe to be in local files -- we currently do not support it on Citus MX -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -1263,8 +1257,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM (SELECT in -- multiple CTEs are joined inside HAVING, so written to file -- locally, but nothing executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -1293,9 +1287,9 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- multiple CTEs are joined inside HAVING, so written to file -- locally, also the join tree contains only another CTE, so should be -- executed locally, but not on an Citus MX worker -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -1325,7 +1319,7 @@ NOTICE: executing the command locally: SELECT key, value FROM locally_execute_i -- plus that could have been read locally on the coordinator -- because of the aggragate over the cte in HAVING -- but not on Citus MX -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -1350,8 +1344,8 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- two could have been written locally and executed locally -- on the coordinator, but not on the workers -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*) FROM @@ -1379,7 +1373,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM (SELECT in -- this time the same CTE is both joined with a distributed -- table and used in HAVING -WITH a AS (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) +WITH a AS MATERIALIZED (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -1401,7 +1395,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ -- this time the same CTE is both joined with a distributed -- table and used in HAVING -- but used in another subquery/aggregate -- so one more level of recursive planning -WITH a AS (SELECT * FROM table_1) +WITH a AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -1423,13 +1417,13 @@ NOTICE: executing the command locally: SELECT count(*) AS count, worker_column_ (1 row) -- same query as the above, without the aggragate -WITH a AS (SELECT max(key) as key, max(value) as value FROM ref_table) +WITH a AS MATERIALIZED (SELECT max(key) as key, max(value) as value FROM ref_table) SELECT count(*), key FROM a JOIN ref_table USING (key) GROUP BY key HAVING (max(ref_table.value) <= (SELECT value FROM a)); -NOTICE: executing the command locally: WITH a AS (SELECT max(ref_table_1.key) AS key, max(ref_table_1.value) AS value FROM locally_execute_intermediate_results.ref_table_1580008 ref_table_1) SELECT count(*) AS count, a.key FROM (a JOIN locally_execute_intermediate_results.ref_table_1580008 ref_table(key, value) USING (key)) GROUP BY a.key HAVING (max(ref_table.value) OPERATOR(pg_catalog.<=) (SELECT a_1.value FROM a a_1)) +NOTICE: executing the command locally: WITH a AS MATERIALIZED (SELECT max(ref_table_1.key) AS key, max(ref_table_1.value) AS value FROM locally_execute_intermediate_results.ref_table_1580008 ref_table_1) SELECT count(*) AS count, a.key FROM (a JOIN locally_execute_intermediate_results.ref_table_1580008 ref_table(key, value) USING (key)) GROUP BY a.key HAVING (max(ref_table.value) OPERATOR(pg_catalog.<=) (SELECT a_1.value FROM a a_1)) count | key --------------------------------------------------------------------- 1 | 6 @@ -1439,20 +1433,18 @@ NOTICE: executing the command locally: WITH a AS (SELECT max(ref_table_1.key) A -- everything could be executed locally on the coordinator, -- but not on the worker WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) FROM cte_2) +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) FROM cte_2) SELECT * FROM cte_3; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 -DEBUG: generating subplan XXX_3 for CTE cte_3: SELECT max(key) AS max FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT max FROM (SELECT intermediate_result.max FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(max integer)) cte_3 +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for CTE cte_2: SELECT key, value FROM (SELECT table_1.key, table_1.value FROM locally_execute_intermediate_results.table_1) cte_1 +DEBUG: generating subplan XXX_2 for CTE cte_3: SELECT max(key) AS max FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT max FROM (SELECT intermediate_result.max FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(max integer)) cte_3 DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx NOTICE: executing the command locally: SELECT key, value FROM locally_execute_intermediate_results.table_1_1580000 table_1 WHERE true NOTICE: executing the command locally: SELECT key, value FROM locally_execute_intermediate_results.table_1_1580002 table_1 WHERE true DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -NOTICE: executing the command locally: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 -DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx -NOTICE: executing the command locally: SELECT max FROM (SELECT intermediate_result.max FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(max integer)) cte_3 +NOTICE: executing the command locally: SELECT max(key) AS max FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 max --------------------------------------------------------------------- 4 @@ -1462,20 +1454,19 @@ NOTICE: executing the command locally: SELECT max FROM (SELECT intermediate_res -- locally since the key = 1 resides on this node -- but because of the current implementation limitations we can't WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 1; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 -DEBUG: generating subplan XXX_3 for CTE cte_3: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_3.key, table_2.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.table_2 USING (key)) WHERE (table_2.key OPERATOR(pg_catalog.=) 1) +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for CTE cte_2: SELECT key, value FROM (SELECT table_1.key, table_1.value FROM locally_execute_intermediate_results.table_1) cte_1 +DEBUG: generating subplan XXX_2 for CTE cte_3: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_3.key, table_2.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.table_2 USING (key)) WHERE (table_2.key OPERATOR(pg_catalog.=) 1) DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx NOTICE: executing the command locally: SELECT key, value FROM locally_execute_intermediate_results.table_1_1580000 table_1 WHERE true NOTICE: executing the command locally: SELECT key, value FROM locally_execute_intermediate_results.table_1_1580002 table_1 WHERE true DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx -NOTICE: executing the command locally: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 -NOTICE: executing the command locally: SELECT cte_3.key, table_2.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.table_2_1580004 table_2(key, value) USING (key)) WHERE (table_2.key OPERATOR(pg_catalog.=) 1) +NOTICE: executing the command locally: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 +NOTICE: executing the command locally: SELECT cte_3.key, table_2.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.table_2_1580004 table_2(key, value) USING (key)) WHERE (table_2.key OPERATOR(pg_catalog.=) 1) key | value --------------------------------------------------------------------- (0 rows) @@ -1483,19 +1474,17 @@ NOTICE: executing the command locally: SELECT cte_3.key, table_2.value FROM ((S -- the join between cte_3 and table_2 has to cannot happen -- locally because the key = 2 resides on a remote node WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 2; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 -DEBUG: generating subplan XXX_3 for CTE cte_3: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_3.key, table_2.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.table_2 USING (key)) WHERE (table_2.key OPERATOR(pg_catalog.=) 2) +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for CTE cte_2: SELECT key, value FROM (SELECT table_1.key, table_1.value FROM locally_execute_intermediate_results.table_1) cte_1 +DEBUG: generating subplan XXX_2 for CTE cte_3: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_3.key, table_2.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.table_2 USING (key)) WHERE (table_2.key OPERATOR(pg_catalog.=) 2) DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx NOTICE: executing the command locally: SELECT key, value FROM locally_execute_intermediate_results.table_1_1580000 table_1 WHERE true NOTICE: executing the command locally: SELECT key, value FROM locally_execute_intermediate_results.table_1_1580002 table_1 WHERE true DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx -NOTICE: executing the command locally: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 key | value --------------------------------------------------------------------- (0 rows) @@ -1503,21 +1492,20 @@ NOTICE: executing the command locally: SELECT max(key) AS key FROM (SELECT inte -- the join between cte_3 and ref can could have happened locally -- but because of the current implementation limitations we can't WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN ref_table USING (key); -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT key, value FROM locally_execute_intermediate_results.table_1 -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT key, value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_1 -DEBUG: generating subplan XXX_3 for CTE cte_3: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_3.key, ref_table.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.ref_table USING (key)) +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for CTE cte_2: SELECT key, value FROM (SELECT table_1.key, table_1.value FROM locally_execute_intermediate_results.table_1) cte_1 +DEBUG: generating subplan XXX_2 for CTE cte_3: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_3.key, ref_table.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.ref_table USING (key)) DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx NOTICE: executing the command locally: SELECT key, value FROM locally_execute_intermediate_results.table_1_1580000 table_1 WHERE true NOTICE: executing the command locally: SELECT key, value FROM locally_execute_intermediate_results.table_1_1580002 table_1 WHERE true DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx -DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx -DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx -NOTICE: executing the command locally: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 -NOTICE: executing the command locally: SELECT cte_3.key, ref_table.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.ref_table_1580008 ref_table(key, value) USING (key)) +DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx +NOTICE: executing the command locally: SELECT max(key) AS key FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) cte_2 +NOTICE: executing the command locally: SELECT cte_3.key, ref_table.value FROM ((SELECT intermediate_result.key FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer)) cte_3 JOIN locally_execute_intermediate_results.ref_table_1580008 ref_table(key, value) USING (key)) key | value --------------------------------------------------------------------- 4 | 4 @@ -1525,9 +1513,9 @@ NOTICE: executing the command locally: SELECT cte_3.key, ref_table.value FROM ( -- some cases around router queries -- a router query, but the having has two cte joins -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 WHERE KEY = 3 @@ -1549,9 +1537,9 @@ NOTICE: executing the command locally: SELECT max(value) AS max FROM locally_ex -- a router query, but the having has two cte joins -- and the jointree has a join with another cte -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) WHERE KEY = 3 @@ -1576,9 +1564,9 @@ NOTICE: executing the command locally: SELECT key, value FROM locally_execute_i (0 rows) -- the same query as above, try to hit local node with either of the queries -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) WHERE KEY = 3 @@ -1604,9 +1592,9 @@ NOTICE: executing the command locally: SELECT key, value FROM locally_execute_i -- a router query, but the having has two cte joins -- and the jointree has a join with the same CTEs -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) JOIN cte_2 ON (key = MAX::int) JOIN cte_1 USING(MAX) WHERE KEY = 3 @@ -1631,8 +1619,8 @@ NOTICE: executing the command locally: SELECT key, value FROM locally_execute_i (0 rows) - subPlans needed remotely as the subquery is pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT key FROM table_1 GROUP BY key HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT key FROM table_2 GROUP BY key HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -1640,8 +1628,8 @@ SELECT * FROM ERROR: syntax error at or near "-" -- the second subquery needs to be recursively planned due to non-colocated subquery join -- so cte_2 becomes part of master query of that recursive subquery planning -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -1668,8 +1656,8 @@ NOTICE: executing the command locally: SELECT worker_column_1 AS key, max(worke (0 rows) -- now, forcing all subqueries to be on the local node -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1) LIMIT 1) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2) LIMIT 1) as bar diff --git a/src/test/regress/expected/mixed_relkind_tests.out b/src/test/regress/expected/mixed_relkind_tests.out index d0e3d153a..eca897eaf 100644 --- a/src/test/regress/expected/mixed_relkind_tests.out +++ b/src/test/regress/expected/mixed_relkind_tests.out @@ -14,7 +14,6 @@ SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); RESET client_min_messages; -- make results consistent -SET citus.enable_cte_inlining TO OFF; -- create test tables CREATE TABLE postgres_local_table (a int); CREATE TABLE partitioned_postgres_local_table(a int) PARTITION BY RANGE(a); @@ -470,7 +469,7 @@ SELECT COUNT(*) FROM unlogged_distributed_table u1 JOIN partitioned_distributed_ RESET citus.enable_repartition_joins; -- joins with cte's -WITH cte_1 AS (SELECT * FROM partitioned_distributed_table) +WITH cte_1 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table) SELECT COUNT(*) FROM cte_1; DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT a, b FROM mixed_relkind_tests.partitioned_distributed_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte_1 @@ -479,7 +478,7 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c 78 (1 row) -WITH cte_1 AS (SELECT * FROM partitioned_distributed_table) +WITH cte_1 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table) SELECT COUNT(*) FROM cte_1 JOIN partitioned_distributed_table USING (a); DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT a, b FROM mixed_relkind_tests.partitioned_distributed_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte_1 JOIN mixed_relkind_tests.partitioned_distributed_table USING (a)) @@ -488,7 +487,7 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c 1014 (1 row) -WITH cte_1 AS (SELECT * FROM foreign_distributed_table) +WITH cte_1 AS MATERIALIZED (SELECT * FROM foreign_distributed_table) SELECT COUNT(*) FROM cte_1 JOIN foreign_distributed_table USING (a); DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT a, b FROM mixed_relkind_tests.foreign_distributed_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte_1 JOIN mixed_relkind_tests.foreign_distributed_table USING (a)) @@ -497,7 +496,7 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c 0 (1 row) -WITH cte_1 AS (SELECT * FROM partitioned_distributed_table) +WITH cte_1 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table) SELECT COUNT(*) FROM cte_1 JOIN partitioned_distributed_table USING (b); DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT a, b FROM mixed_relkind_tests.partitioned_distributed_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte_1 JOIN mixed_relkind_tests.partitioned_distributed_table USING (b)) @@ -518,8 +517,8 @@ FROM (SELECT * FROM unlogged_distributed_table WHERE b = 1) AS sub1, (SELECT * FROM unlogged_distributed_table WHERE b = 2) AS sub2 WHERE sub1.a = sub2.a AND sub1.a = dt.a AND dt.a > 1; -- multi shard non-colocated update -WITH cte1 AS (SELECT * FROM partitioned_distributed_table WHERE b = 1), - cte2 AS (SELECT * FROM partitioned_distributed_table WHERE b = 2) +WITH cte1 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table WHERE b = 1), + cte2 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table WHERE b = 2) UPDATE partitioned_distributed_table dt SET b = cte1.a + cte2.a FROM cte1, cte2 WHERE cte1.a != cte2.a AND cte1.a = dt.a AND dt.a > 1; DEBUG: generating subplan XXX_1 for CTE cte1: SELECT a, b FROM mixed_relkind_tests.partitioned_distributed_table WHERE (b OPERATOR(pg_catalog.=) 1) diff --git a/src/test/regress/expected/multi_complex_expressions_0.out b/src/test/regress/expected/multi_complex_expressions_0.out deleted file mode 100644 index b9a1b42e8..000000000 --- a/src/test/regress/expected/multi_complex_expressions_0.out +++ /dev/null @@ -1,555 +0,0 @@ --- --- MULTI_COMPLEX_EXPRESSIONS --- --- Check that we can correctly handle complex expressions and aggregates. -SELECT sum(l_quantity) / avg(l_quantity) FROM lineitem; - ?column? ---------------------------------------------------------------------- - 12000.0000000000000000 -(1 row) - -SELECT sum(l_quantity) / (10 * avg(l_quantity)) FROM lineitem; - ?column? ---------------------------------------------------------------------- - 1200.0000000000000000 -(1 row) - -SELECT (sum(l_quantity) / (10 * avg(l_quantity))) + 11 FROM lineitem; - ?column? ---------------------------------------------------------------------- - 1211.0000000000000000 -(1 row) - -SELECT avg(l_quantity) as average FROM lineitem; - average ---------------------------------------------------------------------- - 25.4462500000000000 -(1 row) - -SELECT 100 * avg(l_quantity) as average_times_hundred FROM lineitem; - average_times_hundred ---------------------------------------------------------------------- - 2544.6250000000000000 -(1 row) - -SELECT 100 * avg(l_quantity) / 10 as average_times_ten FROM lineitem; - average_times_ten ---------------------------------------------------------------------- - 254.4625000000000000 -(1 row) - -SELECT l_quantity, 10 * count(*) count_quantity FROM lineitem - GROUP BY l_quantity ORDER BY count_quantity, l_quantity; - l_quantity | count_quantity ---------------------------------------------------------------------- - 44.00 | 2150 - 38.00 | 2160 - 45.00 | 2180 - 13.00 | 2190 - 47.00 | 2200 - 29.00 | 2220 - 36.00 | 2230 - 49.00 | 2230 - 3.00 | 2270 - 35.00 | 2280 - 18.00 | 2290 - 31.00 | 2290 - 43.00 | 2290 - 14.00 | 2300 - 16.00 | 2300 - 17.00 | 2300 - 26.00 | 2300 - 7.00 | 2320 - 10.00 | 2340 - 34.00 | 2340 - 15.00 | 2350 - 25.00 | 2360 - 33.00 | 2360 - 42.00 | 2360 - 2.00 | 2370 - 12.00 | 2410 - 37.00 | 2410 - 6.00 | 2420 - 22.00 | 2420 - 1.00 | 2430 - 19.00 | 2430 - 4.00 | 2440 - 20.00 | 2460 - 48.00 | 2460 - 41.00 | 2470 - 24.00 | 2490 - 27.00 | 2490 - 8.00 | 2500 - 11.00 | 2500 - 5.00 | 2540 - 21.00 | 2550 - 32.00 | 2550 - 9.00 | 2580 - 39.00 | 2600 - 46.00 | 2600 - 50.00 | 2600 - 23.00 | 2610 - 30.00 | 2640 - 40.00 | 2690 - 28.00 | 2730 -(50 rows) - --- Check that we can handle complex select clause expressions. -SELECT count(*) FROM lineitem - WHERE octet_length(l_comment || l_comment) > 40; - count ---------------------------------------------------------------------- - 8148 -(1 row) - -SELECT count(*) FROM lineitem - WHERE octet_length(concat(l_comment, l_comment)) > 40; - count ---------------------------------------------------------------------- - 8148 -(1 row) - -SELECT count(*) FROM lineitem - WHERE octet_length(l_comment) + octet_length('randomtext'::text) > 40; - count ---------------------------------------------------------------------- - 4611 -(1 row) - -SELECT count(*) FROM lineitem - WHERE octet_length(l_comment) + 10 > 40; - count ---------------------------------------------------------------------- - 4611 -(1 row) - -SELECT count(*) FROM lineitem - WHERE (l_receiptdate::timestamp - l_shipdate::timestamp) > interval '5 days'; - count ---------------------------------------------------------------------- - 10008 -(1 row) - --- can push down queries where no columns present on the WHERE clause -SELECT count(*) FROM lineitem WHERE random() = -0.1; - count ---------------------------------------------------------------------- - 0 -(1 row) - --- boolean tests can be pushed down -SELECT count(*) FROM lineitem - WHERE (l_partkey > 10000) is true; - count ---------------------------------------------------------------------- - 11423 -(1 row) - --- scalar array operator expressions can be pushed down -SELECT count(*) FROM lineitem - WHERE l_partkey = ANY(ARRAY[19353, 19354, 19355]); - count ---------------------------------------------------------------------- - 1 -(1 row) - --- some more scalar array operator expressions -SELECT count(*) FROM lineitem - WHERE l_partkey = ALL(ARRAY[19353]); - count ---------------------------------------------------------------------- - 1 -(1 row) - --- operator expressions involving arrays -SELECT count(*) FROM lineitem - WHERE ARRAY[19353, 19354, 19355] @> ARRAY[l_partkey]; - count ---------------------------------------------------------------------- - 1 -(1 row) - --- coerced via io expressions can be pushed down -SELECT count(*) FROM lineitem - WHERE (l_quantity/100)::int::bool::text::bool; - count ---------------------------------------------------------------------- - 260 -(1 row) - --- case expressions can be pushed down -SELECT count(*) FROM lineitem - WHERE (CASE WHEN l_orderkey > 4000 THEN l_partkey / 100 > 1 ELSE false END); - count ---------------------------------------------------------------------- - 7948 -(1 row) - --- coalesce expressions can be pushed down -SELECT count(*) FROM lineitem - WHERE COALESCE((l_partkey/50000)::bool, false); - count ---------------------------------------------------------------------- - 9122 -(1 row) - --- nullif expressions can be pushed down -SELECT count(*) FROM lineitem - WHERE NULLIF((l_partkey/50000)::bool, false); - count ---------------------------------------------------------------------- - 9122 -(1 row) - --- null test expressions can be pushed down -SELECT count(*) FROM orders - WHERE o_comment IS NOT null; - count ---------------------------------------------------------------------- - 2985 -(1 row) - --- functions can be pushed down -SELECT count(*) FROM lineitem - WHERE isfinite(l_shipdate); - count ---------------------------------------------------------------------- - 12000 -(1 row) - --- constant expressions can be pushed down -SELECT count(*) FROM lineitem - WHERE 0 != 0; - count ---------------------------------------------------------------------- - 0 -(1 row) - --- distinct expressions can be pushed down -SELECT count(*) FROM lineitem - WHERE l_partkey IS DISTINCT FROM 50040; - count ---------------------------------------------------------------------- - 11999 -(1 row) - --- row compare expression can be pushed down -SELECT count(*) FROM lineitem - WHERE row(l_partkey, 2, 3) > row(2000, 2, 3); - count ---------------------------------------------------------------------- - 11882 -(1 row) - --- combination of different expressions can be pushed down -SELECT count(*) FROM lineitem - WHERE - (l_quantity/100)::int::bool::text::bool AND - CASE WHEN l_orderkey > 4000 THEN l_partkey / 100 > 1 ELSE false END AND - COALESCE((l_partkey/50000)::bool, false) AND - NULLIF((l_partkey/50000)::bool, false) AND - isfinite(l_shipdate) AND - l_partkey IS DISTINCT FROM 50040 AND - row(l_partkey, 2, 3) > row(2000, 2, 3); - count ---------------------------------------------------------------------- - 137 -(1 row) - --- constant expression in the WHERE clause with a column in the target list -SELECT l_linenumber FROM lineitem - WHERE - 1!=0 - ORDER BY - l_linenumber - LIMIT 1; - l_linenumber ---------------------------------------------------------------------- - 1 -(1 row) - --- constant expression in the WHERE clause with expressions and a column the target list -SELECT count(*) * l_discount as total_discount, count(*), sum(l_tax), l_discount FROM lineitem - WHERE - 1!=0 - GROUP BY - l_discount - ORDER BY - total_discount DESC, sum(l_tax) DESC; - total_discount | count | sum | l_discount ---------------------------------------------------------------------- - 104.80 | 1048 | 41.08 | 0.10 - 98.55 | 1095 | 44.15 | 0.09 - 90.64 | 1133 | 45.94 | 0.08 - 71.05 | 1015 | 41.19 | 0.07 - 69.42 | 1157 | 45.75 | 0.06 - 53.60 | 1072 | 42.82 | 0.05 - 43.64 | 1091 | 44.40 | 0.04 - 32.55 | 1085 | 43.30 | 0.03 - 22.22 | 1111 | 45.07 | 0.02 - 11.22 | 1122 | 44.54 | 0.01 - 0.00 | 1071 | 44.00 | 0.00 -(11 rows) - --- distinct expressions in the WHERE clause with a column in the target list -SELECT l_linenumber FROM lineitem - WHERE - l_linenumber IS DISTINCT FROM 1 AND - l_orderkey IS DISTINCT FROM 8997 - ORDER BY - l_linenumber - LIMIT 1; - l_linenumber ---------------------------------------------------------------------- - 2 -(1 row) - --- distinct expressions in the WHERE clause with expressions and a column the target list -SELECT max(l_linenumber), min(l_discount), l_receiptdate FROM lineitem - WHERE - l_linenumber IS DISTINCT FROM 1 AND - l_orderkey IS DISTINCT FROM 8997 - GROUP BY - l_receiptdate - ORDER BY - l_receiptdate - LIMIT 1; - max | min | l_receiptdate ---------------------------------------------------------------------- - 3 | 0.07 | 01-09-1992 -(1 row) - --- Check that we can handle implicit and explicit join clause definitions. -SELECT count(*) FROM lineitem, orders - WHERE l_orderkey = o_orderkey AND l_quantity < 5; - count ---------------------------------------------------------------------- - 951 -(1 row) - -SELECT count(*) FROM lineitem - JOIN orders ON l_orderkey = o_orderkey AND l_quantity < 5; - count ---------------------------------------------------------------------- - 951 -(1 row) - -SELECT count(*) FROM lineitem JOIN orders ON l_orderkey = o_orderkey - WHERE l_quantity < 5; - count ---------------------------------------------------------------------- - 951 -(1 row) - --- Check that we make sure local joins are between columns only. -SELECT count(*) FROM lineitem, orders WHERE l_orderkey + 1 = o_orderkey; -ERROR: complex joins are only supported when all distributed tables are joined on their distribution columns with equal operator --- Check that we can issue limit/offset queries --- the subquery is recursively planned since it contains OFFSET, which is not pushdownable -SELECT * FROM (SELECT o_custkey FROM orders GROUP BY o_custkey ORDER BY o_custkey OFFSET 20) sq ORDER BY 1 LIMIT 5; -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries with offset are not supported yet --- the subquery is recursively planned since it contains OFFSET, which is not pushdownable -SELECT * FROM (SELECT o_orderkey FROM orders ORDER BY o_orderkey OFFSET 20) sq ORDER BY 1 LIMIT 5; -ERROR: cannot perform distributed planning on this query -DETAIL: Subqueries with offset are not supported yet --- Simple LIMIT/OFFSET with ORDER BY -SELECT o_orderkey FROM orders ORDER BY o_orderkey LIMIT 10 OFFSET 20; - o_orderkey ---------------------------------------------------------------------- - 69 - 70 - 71 - 96 - 97 - 98 - 99 - 100 - 101 - 102 -(10 rows) - --- LIMIT/OFFSET with a subquery -SELECT - customer_keys.o_custkey, - SUM(order_count) AS total_order_count -FROM - (SELECT o_custkey, o_orderstatus, COUNT(*) AS order_count - FROM orders GROUP BY o_custkey, o_orderstatus ) customer_keys -GROUP BY - customer_keys.o_custkey -ORDER BY - customer_keys.o_custkey DESC -LIMIT 10 OFFSET 20; - o_custkey | total_order_count ---------------------------------------------------------------------- - 1466 | 1 - 1465 | 2 - 1463 | 4 - 1462 | 10 - 1460 | 1 - 1459 | 6 - 1457 | 1 - 1456 | 3 - 1454 | 2 - 1453 | 5 -(10 rows) - -SELECT - customer_keys.o_custkey, - SUM(order_count) AS total_order_count -FROM - (SELECT o_custkey, o_orderstatus, COUNT(*) over (partition by o_orderstatus) AS order_count - FROM orders GROUP BY o_custkey, o_orderstatus ) customer_keys -GROUP BY - customer_keys.o_custkey -ORDER BY - customer_keys.o_custkey DESC -LIMIT 10 OFFSET 20; - o_custkey | total_order_count ---------------------------------------------------------------------- - 1466 | 759 - 1465 | 759 - 1463 | 1499 - 1462 | 1499 - 1460 | 759 - 1459 | 1499 - 1457 | 740 - 1456 | 830 - 1454 | 1499 - 1453 | 1499 -(10 rows) - -SELECT - customer_keys.o_custkey, - SUM(order_count1 + order_count) AS total_order_count -FROM - (SELECT o_custkey, o_orderstatus, count(*) order_count1, COUNT(*) over (partition by o_orderstatus) AS order_count - FROM orders GROUP BY o_custkey, o_orderstatus ) customer_keys -GROUP BY - customer_keys.o_custkey -ORDER BY - customer_keys.o_custkey DESC -LIMIT 10 OFFSET 20; - o_custkey | total_order_count ---------------------------------------------------------------------- - 1466 | 760 - 1465 | 761 - 1463 | 1503 - 1462 | 1509 - 1460 | 760 - 1459 | 1505 - 1457 | 741 - 1456 | 833 - 1454 | 1501 - 1453 | 1504 -(10 rows) - -SET client_min_messages TO DEBUG1; --- Ensure that we push down LIMIT and OFFSET properly --- No Group-By -> Push Down -CREATE TEMP TABLE temp_limit_test_1 AS -SELECT o_custkey FROM orders LIMIT 10 OFFSET 15; -DEBUG: push down of limit count: 25 --- GROUP BY without ORDER BY -> No push-down -CREATE TEMP TABLE temp_limit_test_2 AS -SELECT o_custkey FROM orders GROUP BY o_custkey LIMIT 10 OFFSET 15; --- GROUP BY and ORDER BY non-aggregate -> push-down -CREATE TEMP TABLE temp_limit_test_3 AS -SELECT o_custkey FROM orders GROUP BY o_custkey ORDER BY o_custkey LIMIT 10 OFFSET 15; -DEBUG: push down of limit count: 25 --- GROUP BY and ORDER BY aggregate -> No push-down -CREATE TEMP TABLE temp_limit_test_4 AS -SELECT o_custkey, COUNT(*) AS ccnt FROM orders GROUP BY o_custkey ORDER BY ccnt DESC LIMIT 10 OFFSET 15; --- OFFSET without LIMIT -SELECT o_custkey FROM orders ORDER BY o_custkey OFFSET 2980; - o_custkey ---------------------------------------------------------------------- - 1498 - 1498 - 1499 - 1499 - 1499 -(5 rows) - --- LIMIT/OFFSET with Joins -SELECT - li.l_partkey, - o.o_custkey, - li.l_quantity -FROM - lineitem li JOIN orders o ON li.l_orderkey = o.o_orderkey -WHERE - li.l_quantity > 25 -ORDER BY 1, 2, 3 -LIMIT 10 OFFSET 20; -DEBUG: push down of limit count: 30 - l_partkey | o_custkey | l_quantity ---------------------------------------------------------------------- - 655 | 58 | 50.00 - 669 | 319 | 34.00 - 699 | 1255 | 50.00 - 716 | 61 | 45.00 - 723 | 14 | 36.00 - 802 | 754 | 50.00 - 831 | 589 | 32.00 - 835 | 67 | 33.00 - 864 | 439 | 32.00 - 875 | 13 | 43.00 -(10 rows) - -RESET client_min_messages; --- FILTERs -SELECT - l_orderkey, - sum(l_extendedprice), - sum(l_extendedprice) FILTER (WHERE l_shipmode = 'AIR'), - count(*), - count(*) FILTER (WHERE l_shipmode = 'AIR'), - max(l_extendedprice), - max(l_extendedprice) FILTER (WHERE l_quantity < 30) - FROM lineitem - GROUP BY l_orderkey - ORDER BY 2 DESC, 1 DESC - LIMIT 10; - l_orderkey | sum | sum | count | count | max | max ---------------------------------------------------------------------- - 12804 | 440012.71 | 45788.16 | 7 | 1 | 94398.00 | 45788.16 - 9863 | 412560.63 | 175647.63 | 7 | 3 | 85723.77 | 50769.14 - 2567 | 412076.77 | 59722.26 | 7 | 1 | 94894.00 | 9784.02 - 11142 | 410502.38 | 44965.95 | 7 | 1 | 83989.44 | 44965.95 - 12039 | 407048.94 | 76406.30 | 7 | 2 | 94471.02 | 19679.30 - 2306 | 405629.96 | 28032.60 | 7 | 1 | 92838.00 | 44384.50 - 5606 | 403595.91 | 36531.51 | 7 | 2 | 94890.18 | 30582.75 - 11296 | 399079.89 | | 6 | 0 | 102449.00 | 33122.93 - 11046 | 391163.26 | 31436.34 | 7 | 2 | 94506.24 | 47519.76 - 4421 | 387313.12 | | 7 | 0 | 67301.52 | 23783.40 -(10 rows) - -SELECT - l_orderkey, - sum(l_extendedprice), - sum(l_extendedprice) FILTER (WHERE l_shipmode = 'AIR'), - count(*), - count(*) FILTER (WHERE l_shipmode = 'AIR'), - max(l_extendedprice), - max(l_extendedprice) FILTER (WHERE l_quantity < 30) - FROM lineitem - GROUP BY l_orderkey - HAVING count(*) FILTER (WHERE l_shipmode = 'AIR') > 1 - ORDER BY 2 DESC, 1 DESC - LIMIT 10; - l_orderkey | sum | sum | count | count | max | max ---------------------------------------------------------------------- - 9863 | 412560.63 | 175647.63 | 7 | 3 | 85723.77 | 50769.14 - 12039 | 407048.94 | 76406.30 | 7 | 2 | 94471.02 | 19679.30 - 5606 | 403595.91 | 36531.51 | 7 | 2 | 94890.18 | 30582.75 - 11046 | 391163.26 | 31436.34 | 7 | 2 | 94506.24 | 47519.76 - 14499 | 384140.30 | 67867.08 | 7 | 2 | 84335.36 | 46169.75 - 11623 | 380598.48 | 133709.82 | 7 | 2 | 93701.54 | 21487.65 - 10787 | 375688.09 | 99424.78 | 7 | 2 | 76732.67 | 50946.91 - 12902 | 358191.24 | 76891.00 | 7 | 2 | 82008.08 | 35602.08 - 3747 | 353701.23 | 68592.23 | 7 | 2 | 67181.10 | 46252.77 - 5158 | 349889.05 | 159753.19 | 7 | 3 | 78714.67 | 29729.20 -(10 rows) - diff --git a/src/test/regress/expected/multi_data_types.out b/src/test/regress/expected/multi_data_types.out index ab75277e3..db1cfedcc 100644 --- a/src/test/regress/expected/multi_data_types.out +++ b/src/test/regress/expected/multi_data_types.out @@ -174,7 +174,7 @@ INSERT INTO composite_type_partitioned_table VALUES (123, '(123, 456)'::other_co Node: host=localhost port=xxxxx dbname=regression -> Insert on public.composite_type_partitioned_table_530003 (actual rows=0 loops=1) -> Result (actual rows=1 loops=1) - Output: xxxxxx + Output: 123, '(123,456)'::test_composite_type (9 rows) SELECT run_command_on_coordinator_and_workers($cf$ @@ -218,7 +218,7 @@ INSERT INTO composite_type_partitioned_table VALUES (123, '(456, 678)'::other_co Node: host=localhost port=xxxxx dbname=regression -> Insert on public.composite_type_partitioned_table_530000 (actual rows=0 loops=1) -> Result (actual rows=1 loops=1) - Output: xxxxxx + Output: 123, '(456,678)'::test_composite_type (9 rows) -- create and distribute a table on enum type column diff --git a/src/test/regress/expected/multi_explain.out b/src/test/regress/expected/multi_explain.out index b0d8ca433..31152d48f 100644 --- a/src/test/regress/expected/multi_explain.out +++ b/src/test/regress/expected/multi_explain.out @@ -345,14 +345,14 @@ EXPLAIN (COSTS FALSE, ANALYZE TRUE, TIMING FALSE, SUMMARY FALSE, VERBOSE TRUE) SELECT l_quantity, count(*) count_quantity FROM lineitem GROUP BY l_quantity ORDER BY count_quantity, l_quantity; Sort (actual rows=50 loops=1) - Output: xxxxxx + Output: remote_scan.l_quantity, (COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint)) Sort Key: (COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint)), remote_scan.l_quantity Sort Method: quicksort Memory: 27kB -> HashAggregate (actual rows=50 loops=1) - Output: xxxxxx + Output: remote_scan.l_quantity, COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint) Group Key: remote_scan.l_quantity -> Custom Scan (Citus Adaptive) (actual rows=100 loops=1) - Output: xxxxxx + Output: remote_scan.l_quantity, remote_scan.count_quantity Task Count: 2 Tuple data received from nodes: 780 bytes Tasks Shown: One of 2 @@ -361,48 +361,48 @@ Sort (actual rows=50 loops=1) Tuple data received from node: 390 bytes Node: host=localhost port=xxxxx dbname=regression -> HashAggregate (actual rows=50 loops=1) - Output: xxxxxx + Output: l_quantity, count(*) Group Key: lineitem.l_quantity -> Seq Scan on public.lineitem_290000 lineitem (actual rows=6000 loops=1) - Output: xxxxxx + 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 query text output, with ANALYZE OFF EXPLAIN (COSTS FALSE, ANALYZE FALSE, TIMING FALSE, SUMMARY FALSE, VERBOSE TRUE) SELECT l_quantity, count(*) count_quantity FROM lineitem GROUP BY l_quantity ORDER BY count_quantity, l_quantity; Sort - Output: xxxxxx + Output: remote_scan.l_quantity, (COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint)) Sort Key: (COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint)), remote_scan.l_quantity -> HashAggregate - Output: xxxxxx + Output: remote_scan.l_quantity, COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint) Group Key: remote_scan.l_quantity -> Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan.l_quantity, remote_scan.count_quantity Task Count: 2 Tasks Shown: One of 2 -> Task Query: SELECT l_quantity, count(*) AS count_quantity FROM lineitem_290000 lineitem WHERE true GROUP BY l_quantity Node: host=localhost port=xxxxx dbname=regression -> HashAggregate - Output: xxxxxx + Output: l_quantity, count(*) Group Key: lineitem.l_quantity -> Seq Scan on public.lineitem_290000 lineitem - Output: xxxxxx + 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 verbose EXPLAIN (COSTS FALSE, VERBOSE TRUE) SELECT sum(l_quantity) / avg(l_quantity) FROM lineitem; Aggregate - Output: xxxxxx + Output: (sum(remote_scan."?column?") / (sum(remote_scan."?column?_1") / pg_catalog.sum(remote_scan."?column?_2"))) -> Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan."?column?", remote_scan."?column?_1", remote_scan."?column?_2" Task Count: 2 Tasks Shown: One of 2 -> Task Query: SELECT sum(l_quantity), sum(l_quantity), count(l_quantity) FROM lineitem_290000 lineitem WHERE true Node: host=localhost port=xxxxx dbname=regression -> Aggregate - Output: xxxxxx + Output: sum(l_quantity), sum(l_quantity), count(l_quantity) -> Seq Scan on public.lineitem_290000 lineitem - Output: xxxxxx + 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 @@ -525,40 +525,40 @@ EXPLAIN (COSTS FALSE, VERBOSE TRUE) SELECT sum(l_quantity) / avg(l_quantity) FROM lineitem HAVING sum(l_quantity) > 100; Aggregate - Output: xxxxxx + Output: (sum(remote_scan."?column?") / (sum(remote_scan."?column?_1") / pg_catalog.sum(remote_scan."?column?_2"))) Filter: (sum(remote_scan.worker_column_4) > '100'::numeric) -> Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan."?column?", remote_scan."?column?_1", remote_scan."?column?_2", remote_scan.worker_column_4 Task Count: 2 Tasks Shown: One of 2 -> Task Query: SELECT sum(l_quantity), sum(l_quantity), count(l_quantity), sum(l_quantity) AS worker_column_4 FROM lineitem_290000 lineitem WHERE true Node: host=localhost port=xxxxx dbname=regression -> Aggregate - Output: xxxxxx + Output: sum(l_quantity), sum(l_quantity), count(l_quantity), sum(l_quantity) -> Seq Scan on public.lineitem_290000 lineitem - Output: xxxxxx + 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 having without aggregate EXPLAIN (COSTS FALSE, VERBOSE TRUE) SELECT l_quantity FROM lineitem GROUP BY l_quantity HAVING l_quantity > (100 * random()); HashAggregate - Output: xxxxxx + Output: remote_scan.l_quantity Group Key: remote_scan.l_quantity Filter: ((remote_scan.worker_column_2)::double precision > ('100'::double precision * random())) -> Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan.l_quantity, remote_scan.worker_column_2 Task Count: 2 Tasks Shown: One of 2 -> Task Query: SELECT l_quantity, l_quantity AS worker_column_2 FROM lineitem_290000 lineitem WHERE true GROUP BY l_quantity Node: host=localhost port=xxxxx dbname=regression -> HashAggregate - Output: xxxxxx + Output: l_quantity, l_quantity Group Key: lineitem.l_quantity -> Seq Scan on public.lineitem_290000 lineitem - Output: xxxxxx + 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 -- Subquery pushdown tests with explain EXPLAIN (COSTS OFF) SELECT @@ -1385,38 +1385,36 @@ Custom Scan (Citus INSERT ... SELECT) -> Function Scan on generate_series s -> Function Scan on generate_series s_1 -- explain with recursive planning --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; EXPLAIN (COSTS OFF, VERBOSE true) -WITH keys AS ( +WITH keys AS MATERIALIZED ( SELECT DISTINCT l_orderkey FROM lineitem_hash_part ), -series AS ( +series AS MATERIALIZED ( SELECT s FROM generate_series(1,10) s ) SELECT l_orderkey FROM series JOIN keys ON (s = l_orderkey) ORDER BY s; Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan.l_orderkey -> Distributed Subplan XXX_1 -> HashAggregate - Output: xxxxxx + Output: remote_scan.l_orderkey Group Key: remote_scan.l_orderkey -> Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan.l_orderkey Task Count: 4 Tasks Shown: One of 4 -> Task Query: SELECT DISTINCT l_orderkey FROM lineitem_hash_part_360041 lineitem_hash_part WHERE true Node: host=localhost port=xxxxx dbname=regression -> HashAggregate - Output: xxxxxx + Output: l_orderkey Group Key: lineitem_hash_part.l_orderkey -> Seq Scan on public.lineitem_hash_part_360041 lineitem_hash_part - Output: xxxxxx + 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 -> Distributed Subplan XXX_2 -> Function Scan on pg_catalog.generate_series s - Output: xxxxxx + Output: s Function Call: generate_series(1, 10) Task Count: 1 Tasks Shown: All @@ -1424,21 +1422,20 @@ Custom Scan (Citus Adaptive) Query: SELECT keys.l_orderkey FROM ((SELECT intermediate_result.s FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(s integer)) series JOIN (SELECT intermediate_result.l_orderkey FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(l_orderkey bigint)) keys ON ((series.s OPERATOR(pg_catalog.=) keys.l_orderkey))) ORDER BY series.s Node: host=localhost port=xxxxx dbname=regression -> Merge Join - Output: xxxxxx + Output: intermediate_result_1.l_orderkey, intermediate_result.s Merge Cond: (intermediate_result.s = intermediate_result_1.l_orderkey) -> Sort - Output: xxxxxx + Output: intermediate_result.s Sort Key: intermediate_result.s -> Function Scan on pg_catalog.read_intermediate_result intermediate_result - Output: xxxxxx + Output: intermediate_result.s Function Call: read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) -> Sort - Output: xxxxxx + Output: intermediate_result_1.l_orderkey Sort Key: intermediate_result_1.l_orderkey -> Function Scan on pg_catalog.read_intermediate_result intermediate_result_1 - Output: xxxxxx + Output: intermediate_result_1.l_orderkey Function Call: read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) -SET citus.enable_cte_inlining TO true; SELECT true AS valid FROM explain_json($$ WITH result AS ( SELECT l_quantity, count(*) count_quantity FROM lineitem @@ -1757,7 +1754,7 @@ SELECT * FROM worker_save_query_explain_analyze('SELECT * FROM explain_analyze_t 4 (4 rows) -SELECT explain_analyze_output ~ 'Output: xxxxxx +SELECT explain_analyze_output ~ 'Output: a, b' FROM worker_last_saved_explain_analyze(); ?column? --------------------------------------------------------------------- t diff --git a/src/test/regress/expected/multi_extension_0.out b/src/test/regress/expected/multi_extension_0.out deleted file mode 100644 index 169f3b01d..000000000 --- a/src/test/regress/expected/multi_extension_0.out +++ /dev/null @@ -1,905 +0,0 @@ --- --- MULTI_EXTENSION --- --- Tests around extension creation / upgrades --- --- It'd be nice to script generation of this file, but alas, that's --- not done yet. --- differentiate the output file for pg11 and versions above, with regards to objects --- created per citus version depending on the postgres version. Upgrade tests verify the --- objects are added in citus_finish_pg_upgrade() -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 11 AS version_above_eleven; - version_above_eleven ---------------------------------------------------------------------- - f -(1 row) - -SET citus.next_shard_id TO 580000; -SELECT $definition$ -CREATE OR REPLACE FUNCTION test.maintenance_worker() - RETURNS pg_stat_activity - LANGUAGE plpgsql -AS $$ -DECLARE - activity record; -BEGIN - DO 'BEGIN END'; -- Force maintenance daemon to start - -- we don't want to wait forever; loop will exit after 20 seconds - FOR i IN 1 .. 200 LOOP - PERFORM pg_stat_clear_snapshot(); - SELECT * INTO activity FROM pg_stat_activity - WHERE application_name = 'Citus Maintenance Daemon' AND datname = current_database(); - IF activity.pid IS NOT NULL THEN - RETURN activity; - ELSE - PERFORM pg_sleep(0.1); - END IF ; - END LOOP; - -- fail if we reach the end of this loop - raise 'Waited too long for maintenance daemon to start'; -END; -$$; -$definition$ create_function_test_maintenance_worker -\gset -CREATE TABLE prev_objects(description text); -CREATE TABLE extension_diff(previous_object text COLLATE "C", - current_object text COLLATE "C"); -CREATE FUNCTION print_extension_changes() -RETURNS TABLE(previous_object text, current_object text) -AS $func$ -BEGIN - TRUNCATE TABLE extension_diff; - - CREATE TABLE current_objects AS - SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS description - FROM pg_catalog.pg_depend, pg_catalog.pg_extension e - WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass - AND refobjid = e.oid - AND deptype = 'e' - AND e.extname='citus'; - - INSERT INTO extension_diff - SELECT p.description previous_object, c.description current_object - FROM current_objects c FULL JOIN prev_objects p - ON p.description = c.description - WHERE p.description is null OR c.description is null; - - DROP TABLE prev_objects; - ALTER TABLE current_objects RENAME TO prev_objects; - - RETURN QUERY SELECT * FROM extension_diff ORDER BY 1, 2; -END -$func$ LANGUAGE plpgsql; -CREATE SCHEMA test; -:create_function_test_maintenance_worker --- check maintenance daemon is started -SELECT datname, current_database(), - usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus') -FROM test.maintenance_worker(); - datname | current_database | usename | extowner ---------------------------------------------------------------------- - regression | regression | postgres | postgres -(1 row) - --- ensure no unexpected objects were created outside pg_catalog -SELECT pgio.type, pgio.identity -FROM pg_depend AS pgd, - pg_extension AS pge, - LATERAL pg_identify_object(pgd.classid, pgd.objid, pgd.objsubid) AS pgio -WHERE pgd.refclassid = 'pg_extension'::regclass AND - pgd.refobjid = pge.oid AND - pge.extname = 'citus' AND - pgio.schema NOT IN ('pg_catalog', 'citus', 'citus_internal', 'test', 'columnar') -ORDER BY 1, 2; - type | identity ---------------------------------------------------------------------- - view | public.citus_tables -(1 row) - --- DROP EXTENSION pre-created by the regression suite -DROP EXTENSION citus; -\c --- these tests switch between citus versions and call ddl's that require pg_dist_object to be created -SET citus.enable_object_propagation TO 'false'; -SET citus.enable_version_checks TO 'false'; -CREATE EXTENSION citus VERSION '8.0-1'; -ALTER EXTENSION citus UPDATE TO '8.0-2'; -ALTER EXTENSION citus UPDATE TO '8.0-3'; -ALTER EXTENSION citus UPDATE TO '8.0-4'; -ALTER EXTENSION citus UPDATE TO '8.0-5'; -ALTER EXTENSION citus UPDATE TO '8.0-6'; -ALTER EXTENSION citus UPDATE TO '8.0-7'; -ALTER EXTENSION citus UPDATE TO '8.0-8'; -ALTER EXTENSION citus UPDATE TO '8.0-9'; -ALTER EXTENSION citus UPDATE TO '8.0-10'; -ALTER EXTENSION citus UPDATE TO '8.0-11'; -ALTER EXTENSION citus UPDATE TO '8.0-12'; -ALTER EXTENSION citus UPDATE TO '8.0-13'; -ALTER EXTENSION citus UPDATE TO '8.1-1'; -ALTER EXTENSION citus UPDATE TO '8.2-1'; -ALTER EXTENSION citus UPDATE TO '8.2-2'; -ALTER EXTENSION citus UPDATE TO '8.2-3'; -ALTER EXTENSION citus UPDATE TO '8.2-4'; -ALTER EXTENSION citus UPDATE TO '8.3-1'; -ALTER EXTENSION citus UPDATE TO '9.0-1'; -ALTER EXTENSION citus UPDATE TO '9.0-2'; -ALTER EXTENSION citus UPDATE TO '9.1-1'; -ALTER EXTENSION citus UPDATE TO '9.2-1'; -ALTER EXTENSION citus UPDATE TO '9.2-2'; --- Snapshot of state at 9.2-2 -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- - | event trigger citus_cascade_to_partition - | function alter_role_if_exists(text,text) - | function any_value(anyelement) - | function any_value_agg(anyelement,anyelement) - | function array_cat_agg(anyarray) - | function assign_distributed_transaction_id(integer,bigint,timestamp with time zone) - | function authinfo_valid(text) - | function broadcast_intermediate_result(text,text) - | function check_distributed_deadlocks() - | function citus_add_rebalance_strategy(name,regproc,regproc,regproc,real,real) - | function citus_blocking_pids(integer) - | function citus_create_restore_point(text) - | function citus_dist_stat_activity() - | function citus_drop_trigger() - | function citus_executor_name(integer) - | function citus_extradata_container(internal) - | function citus_finish_pg_upgrade() - | function citus_internal.find_groupid_for_node(text,integer) - | function citus_internal.pg_dist_node_trigger_func() - | function citus_internal.pg_dist_rebalance_strategy_enterprise_check() - | function citus_internal.pg_dist_rebalance_strategy_trigger_func() - | function citus_internal.pg_dist_shard_placement_trigger_func() - | function citus_internal.refresh_isolation_tester_prepared_statement() - | function citus_internal.replace_isolation_tester_func() - | function citus_internal.restore_isolation_tester_func() - | function citus_isolation_test_session_is_blocked(integer,integer[]) - | function citus_json_concatenate(json,json) - | function citus_json_concatenate_final(json) - | function citus_jsonb_concatenate(jsonb,jsonb) - | function citus_jsonb_concatenate_final(jsonb) - | function citus_node_capacity_1(integer) - | function citus_prepare_pg_upgrade() - | function citus_query_stats() - | function citus_relation_size(regclass) - | function citus_server_id() - | function citus_set_default_rebalance_strategy(text) - | function citus_shard_allowed_on_node_true(bigint,integer) - | function citus_shard_cost_1(bigint) - | function citus_shard_cost_by_disk_size(bigint) - | function citus_stat_statements() - | function citus_stat_statements_reset() - | function citus_table_is_visible(oid) - | function citus_table_size(regclass) - | function citus_text_send_as_jsonb(text) - | function citus_total_relation_size(regclass) - | function citus_truncate_trigger() - | function citus_validate_rebalance_strategy_functions(regproc,regproc,regproc) - | function citus_version() - | function citus_worker_stat_activity() - | function column_name_to_column(regclass,text) - | function column_to_column_name(regclass,text) - | function coord_combine_agg(oid,cstring,anyelement) - | function coord_combine_agg_ffunc(internal,oid,cstring,anyelement) - | function coord_combine_agg_sfunc(internal,oid,cstring,anyelement) - | function create_distributed_function(regprocedure,text,text) - | function create_distributed_table(regclass,text,citus.distribution_type,text) - | function create_intermediate_result(text,text) - | function create_reference_table(regclass) - | function distributed_tables_colocated(regclass,regclass) - | function dump_global_wait_edges() - | function dump_local_wait_edges() - | function fetch_intermediate_results(text[],text,integer) - | function get_all_active_transactions() - | function get_colocated_shard_array(bigint) - | function get_colocated_table_array(regclass) - | function get_current_transaction_id() - | function get_global_active_transactions() - | function get_rebalance_progress() - | function get_rebalance_table_shards_plan(regclass,real,integer,bigint[],boolean,name) - | function get_shard_id_for_distribution_column(regclass,"any") - | function isolate_tenant_to_new_shard(regclass,"any",text) - | function json_cat_agg(json) - | function jsonb_cat_agg(jsonb) - | function lock_relation_if_exists(text,text) - | function lock_shard_metadata(integer,bigint[]) - | function lock_shard_resources(integer,bigint[]) - | function mark_tables_colocated(regclass,regclass[]) - | function master_activate_node(text,integer) - | function master_add_inactive_node(text,integer,integer,noderole,name) - | function master_add_node(text,integer,integer,noderole,name) - | function master_add_secondary_node(text,integer,text,integer,name) - | function master_append_table_to_shard(bigint,text,text,integer) - | function master_apply_delete_command(text) - | function master_conninfo_cache_invalidate() - | function master_copy_shard_placement(bigint,text,integer,text,integer,boolean,citus.shard_transfer_mode) - | function master_create_distributed_table(regclass,text,citus.distribution_type) - | function master_create_empty_shard(text) - | function master_create_worker_shards(text,integer,integer) - | function master_disable_node(text,integer) - | function master_dist_local_group_cache_invalidate() - | function master_dist_node_cache_invalidate() - | function master_dist_object_cache_invalidate() - | function master_dist_partition_cache_invalidate() - | function master_dist_placement_cache_invalidate() - | function master_dist_shard_cache_invalidate() - | function master_drain_node(text,integer,citus.shard_transfer_mode,name) - | function master_drop_all_shards(regclass,text,text) - | function master_drop_sequences(text[]) - | function master_get_active_worker_nodes() - | function master_get_new_placementid() - | function master_get_new_shardid() - | function master_get_table_ddl_events(text) - | function master_get_table_metadata(text) - | function master_modify_multiple_shards(text) - | function master_move_shard_placement(bigint,text,integer,text,integer,citus.shard_transfer_mode) - | function master_remove_distributed_table_metadata_from_workers(regclass,text,text) - | function master_remove_node(text,integer) - | function master_remove_partition_metadata(regclass,text,text) - | function master_run_on_worker(text[],integer[],text[],boolean) - | function master_set_node_property(text,integer,text,boolean) - | function master_unmark_object_distributed(oid,oid,integer) - | function master_update_node(integer,text,integer,boolean,integer) - | function master_update_shard_statistics(bigint) - | function master_update_table_statistics(regclass) - | function poolinfo_valid(text) - | function read_intermediate_result(text,citus_copy_format) - | function read_intermediate_results(text[],citus_copy_format) - | function rebalance_table_shards(regclass,real,integer,bigint[],citus.shard_transfer_mode,boolean,name) - | function recover_prepared_transactions() - | function relation_is_a_known_shard(regclass) - | function replicate_table_shards(regclass,integer,integer,bigint[],citus.shard_transfer_mode) - | function role_exists(name) - | function run_command_on_colocated_placements(regclass,regclass,text,boolean) - | function run_command_on_placements(regclass,text,boolean) - | function run_command_on_shards(regclass,text,boolean) - | function run_command_on_workers(text,boolean) - | function shard_name(regclass,bigint) - | function start_metadata_sync_to_node(text,integer) - | function stop_metadata_sync_to_node(text,integer) - | function task_tracker_assign_task(bigint,integer,text) - | function task_tracker_cleanup_job(bigint) - | function task_tracker_conninfo_cache_invalidate() - | function task_tracker_task_status(bigint,integer) - | function upgrade_to_reference_table(regclass) - | function worker_append_table_to_shard(text,text,text,integer) - | function worker_apply_inter_shard_ddl_command(bigint,text,bigint,text,text) - | function worker_apply_sequence_command(text) - | function worker_apply_sequence_command(text,regtype) - | function worker_apply_shard_ddl_command(bigint,text) - | function worker_apply_shard_ddl_command(bigint,text,text) - | function worker_cleanup_job_schema_cache() - | function worker_create_or_replace_object(text) - | function worker_create_schema(bigint,text) - | function worker_create_truncate_trigger(regclass) - | function worker_drop_distributed_table(text) - | function worker_execute_sql_task(bigint,integer,text,boolean) - | function worker_fetch_foreign_file(text,text,bigint,text[],integer[]) - | function worker_fetch_partition_file(bigint,integer,integer,integer,text,integer) - | function worker_hash("any") - | function worker_hash_partition_table(bigint,integer,text,text,oid,anyarray) - | function worker_merge_files_and_run_query(bigint,integer,text,text) - | function worker_merge_files_into_table(bigint,integer,text[],text[]) - | function worker_partial_agg(oid,anyelement) - | function worker_partial_agg_ffunc(internal) - | function worker_partial_agg_sfunc(internal,oid,anyelement) - | function worker_partition_query_result(text,text,integer,citus.distribution_type,text[],text[],boolean) - | function worker_range_partition_table(bigint,integer,text,text,oid,anyarray) - | function worker_repartition_cleanup(bigint) - | schema citus - | schema citus_internal - | sequence pg_dist_colocationid_seq - | sequence pg_dist_groupid_seq - | sequence pg_dist_node_nodeid_seq - | sequence pg_dist_placement_placementid_seq - | sequence pg_dist_shardid_seq - | table citus.pg_dist_object - | table pg_dist_authinfo - | table pg_dist_colocation - | table pg_dist_local_group - | table pg_dist_node - | table pg_dist_node_metadata - | table pg_dist_partition - | table pg_dist_placement - | table pg_dist_poolinfo - | table pg_dist_rebalance_strategy - | table pg_dist_shard - | table pg_dist_transaction - | type citus.distribution_type - | type citus.shard_transfer_mode - | type citus_copy_format - | type noderole - | view citus_dist_stat_activity - | view citus_lock_waits - | view citus_shard_indexes_on_worker - | view citus_shards_on_worker - | view citus_stat_statements - | view citus_worker_stat_activity - | view pg_dist_shard_placement -(188 rows) - --- Test downgrade to 9.2-2 from 9.2-4 -ALTER EXTENSION citus UPDATE TO '9.2-4'; -ALTER EXTENSION citus UPDATE TO '9.2-2'; --- Should be empty result since upgrade+downgrade should be a no-op -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - -/* - * As we mistakenly bumped schema version to 9.3-1 in a bad release, we support - * updating citus schema from 9.3-1 to 9.2-4, but we do not support updates to 9.3-1. - * - * Hence the query below should fail. - */ -ALTER EXTENSION citus UPDATE TO '9.3-1'; -ERROR: extension "citus" has no update path from version "9.2-2" to version "9.3-1" -ALTER EXTENSION citus UPDATE TO '9.2-4'; --- Snapshot of state at 9.2-4 -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Test downgrade to 9.2-4 from 9.3-2 -ALTER EXTENSION citus UPDATE TO '9.3-2'; -ALTER EXTENSION citus UPDATE TO '9.2-4'; --- Should be empty result since upgrade+downgrade should be a no-op -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Snapshot of state at 9.3-2 -ALTER EXTENSION citus UPDATE TO '9.3-2'; -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- - | function citus_remote_connection_stats() - | function replicate_reference_tables() - | function truncate_local_data_after_distributing_table(regclass) - | function update_distributed_table_colocation(regclass,text) - | function worker_create_or_alter_role(text,text,text) -(5 rows) - --- Test downgrade to 9.3-2 from 9.4-1 -ALTER EXTENSION citus UPDATE TO '9.4-1'; -ALTER EXTENSION citus UPDATE TO '9.3-2'; --- Should be empty result since upgrade+downgrade should be a no-op -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Snapshot of state at 9.4-1 -ALTER EXTENSION citus UPDATE TO '9.4-1'; -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- - | function worker_last_saved_explain_analyze() - | function worker_save_query_explain_analyze(text,jsonb) -(2 rows) - --- Test downgrade to 9.4-1 from 9.5-1 -ALTER EXTENSION citus UPDATE TO '9.5-1'; -BEGIN; - SELECT master_add_node('localhost', :master_port, groupId=>0); - master_add_node ---------------------------------------------------------------------- - 1 -(1 row) - - CREATE TABLE citus_local_table (a int); - SELECT create_citus_local_table('citus_local_table'); -NOTICE: create_citus_local_table is deprecated in favour of citus_add_local_table_to_metadata - create_citus_local_table ---------------------------------------------------------------------- - -(1 row) - - -- downgrade from 9.5-1 to 9.4-1 should fail as we have a citus local table - ALTER EXTENSION citus UPDATE TO '9.4-1'; -ERROR: citus local tables are introduced in Citus 9.5 -HINT: To downgrade Citus to an older version, you should first convert each citus local table to a postgres table by executing SELECT undistribute_table("%s") -CONTEXT: PL/pgSQL function inline_code_block line 11 at RAISE -ROLLBACK; --- now we can downgrade as there is no citus local table -ALTER EXTENSION citus UPDATE TO '9.4-1'; --- Should be empty result since upgrade+downgrade should be a no-op -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Snapshot of state at 9.5-1 -ALTER EXTENSION citus UPDATE TO '9.5-1'; -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- - function master_drop_sequences(text[]) | - function task_tracker_assign_task(bigint,integer,text) | - function task_tracker_cleanup_job(bigint) | - function task_tracker_conninfo_cache_invalidate() | - function task_tracker_task_status(bigint,integer) | - function worker_execute_sql_task(bigint,integer,text,boolean) | - function worker_merge_files_and_run_query(bigint,integer,text,text) | - | function create_citus_local_table(regclass) - | function undistribute_table(regclass) - | function worker_record_sequence_dependency(regclass,regclass,name) -(10 rows) - --- Test downgrade to 9.5-1 from 10.0-1 -ALTER EXTENSION citus UPDATE TO '10.0-1'; -ALTER EXTENSION citus UPDATE TO '9.5-1'; --- Should be empty result since upgrade+downgrade should be a no-op -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Snapshot of state at 10.0-1 -ALTER EXTENSION citus UPDATE TO '10.0-1'; -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- - function citus_total_relation_size(regclass) | - function create_citus_local_table(regclass) | - function mark_tables_colocated(regclass,regclass[]) | - function master_conninfo_cache_invalidate() | - function master_create_distributed_table(regclass,text,citus.distribution_type) | - function master_create_worker_shards(text,integer,integer) | - function master_dist_local_group_cache_invalidate() | - function master_dist_node_cache_invalidate() | - function master_dist_object_cache_invalidate() | - function master_dist_partition_cache_invalidate() | - function master_dist_placement_cache_invalidate() | - function master_dist_shard_cache_invalidate() | - function master_drop_all_shards(regclass,text,text) | - function master_modify_multiple_shards(text) | - function undistribute_table(regclass) | - function upgrade_to_reference_table(regclass) | - | function alter_distributed_table(regclass,text,integer,text,boolean) - | function alter_old_partitions_set_access_method(regclass,timestamp with time zone,name) - | function alter_table_set_access_method(regclass,text) - | function citus_activate_node(text,integer) - | function citus_add_inactive_node(text,integer,integer,noderole,name) - | function citus_add_local_table_to_metadata(regclass,boolean) - | function citus_add_node(text,integer,integer,noderole,name) - | function citus_add_secondary_node(text,integer,text,integer,name) - | function citus_conninfo_cache_invalidate() - | function citus_copy_shard_placement(bigint,text,integer,text,integer,boolean,citus.shard_transfer_mode) - | function citus_disable_node(text,integer) - | function citus_dist_local_group_cache_invalidate() - | function citus_dist_node_cache_invalidate() - | function citus_dist_object_cache_invalidate() - | function citus_dist_partition_cache_invalidate() - | function citus_dist_placement_cache_invalidate() - | function citus_dist_shard_cache_invalidate() - | function citus_drain_node(text,integer,citus.shard_transfer_mode,name) - | function citus_drop_all_shards(regclass,text,text) - | function citus_internal.columnar_ensure_objects_exist() - | function citus_move_shard_placement(bigint,text,integer,text,integer,citus.shard_transfer_mode) - | function citus_remove_node(text,integer) - | function citus_set_coordinator_host(text,integer,noderole,name) - | function citus_set_node_property(text,integer,text,boolean) - | function citus_shard_sizes() - | function citus_total_relation_size(regclass,boolean) - | function citus_unmark_object_distributed(oid,oid,integer) - | function citus_update_node(integer,text,integer,boolean,integer) - | function citus_update_shard_statistics(bigint) - | function citus_update_table_statistics(regclass) - | function fix_pre_citus10_partitioned_table_constraint_names() - | function fix_pre_citus10_partitioned_table_constraint_names(regclass) - | function notify_constraint_dropped() - | function remove_local_tables_from_metadata() - | function time_partition_range(regclass) - | function undistribute_table(regclass,boolean) - | function worker_change_sequence_dependency(regclass,regclass,regclass) - | function worker_fix_pre_citus10_partitioned_table_constraint_names(regclass,bigint,text) - | schema columnar - | sequence columnar.storageid_seq - | table columnar.chunk - | table columnar.chunk_group - | table columnar.options - | table columnar.stripe - | view citus_shards - | view citus_tables - | view time_partitions -(63 rows) - --- Test downgrade to 10.0-1 from 10.0-2 -ALTER EXTENSION citus UPDATE TO '10.0-2'; -ALTER EXTENSION citus UPDATE TO '10.0-1'; --- Should be empty result since upgrade+downgrade should be a no-op -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Snapshot of state at 10.0-2 -ALTER EXTENSION citus UPDATE TO '10.0-2'; -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Test downgrade to 10.0-2 from 10.0-3 -ALTER EXTENSION citus UPDATE TO '10.0-3'; -ALTER EXTENSION citus UPDATE TO '10.0-2'; --- Should be empty result since upgrade+downgrade should be a no-op -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Snapshot of state at 10.0-3 -ALTER EXTENSION citus UPDATE TO '10.0-3'; -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- - | function citus_get_active_worker_nodes() -(1 row) - --- Test downgrade to 10.0-3 from 10.1-1 -ALTER EXTENSION citus UPDATE TO '10.1-1'; -ALTER EXTENSION citus UPDATE TO '10.0-3'; --- Should be empty result since upgrade+downgrade should be a no-op -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - --- Snapshot of state at 10.1-1 -ALTER EXTENSION citus UPDATE TO '10.1-1'; -SELECT * FROM print_extension_changes(); - previous_object | current_object ---------------------------------------------------------------------- -(0 rows) - -DROP TABLE prev_objects, extension_diff; --- show running version -SHOW citus.version; - citus.version ---------------------------------------------------------------------- - 10.1devel -(1 row) - --- ensure no unexpected objects were created outside pg_catalog -SELECT pgio.type, pgio.identity -FROM pg_depend AS pgd, - pg_extension AS pge, - LATERAL pg_identify_object(pgd.classid, pgd.objid, pgd.objsubid) AS pgio -WHERE pgd.refclassid = 'pg_extension'::regclass AND - pgd.refobjid = pge.oid AND - pge.extname = 'citus' AND - pgio.schema NOT IN ('pg_catalog', 'citus', 'citus_internal', 'test', 'columnar') -ORDER BY 1, 2; - type | identity ---------------------------------------------------------------------- - view | public.citus_tables -(1 row) - --- see incompatible version errors out -RESET citus.enable_version_checks; -DROP EXTENSION citus; -CREATE EXTENSION citus VERSION '8.0-1'; -ERROR: specified version incompatible with loaded Citus library -DETAIL: Loaded library requires 10.1, but 8.0-1 was specified. -HINT: If a newer library is present, restart the database and try the command again. --- Test non-distributed queries work even in version mismatch -SET citus.enable_version_checks TO 'false'; -CREATE EXTENSION citus VERSION '8.1-1'; -SET citus.enable_version_checks TO 'true'; --- Test CREATE TABLE -CREATE TABLE version_mismatch_table(column1 int); --- Test COPY -\copy version_mismatch_table FROM STDIN; --- Test INSERT -INSERT INTO version_mismatch_table(column1) VALUES(5); --- Test SELECT -SELECT * FROM version_mismatch_table ORDER BY column1; - column1 ---------------------------------------------------------------------- - 0 - 1 - 2 - 3 - 4 - 5 -(6 rows) - --- Test SELECT from pg_catalog -SELECT d.datname as "Name", - pg_catalog.pg_get_userbyid(d.datdba) as "Owner", - pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges" -FROM pg_catalog.pg_database d -ORDER BY 1; - Name | Owner | Access privileges ---------------------------------------------------------------------- - postgres | postgres | - regression | postgres | - template0 | postgres | =c/postgres + - | | postgres=CTc/postgres - template1 | postgres | =c/postgres + - | | postgres=CTc/postgres -(4 rows) - --- We should not distribute table in version mistmatch -SELECT create_distributed_table('version_mismatch_table', 'column1'); -ERROR: loaded Citus library version differs from installed extension version -DETAIL: Loaded library requires 10.1, but the installed extension version is 8.1-1. -HINT: Run ALTER EXTENSION citus UPDATE and try again. --- This function will cause fail in next ALTER EXTENSION -CREATE OR REPLACE FUNCTION pg_catalog.relation_is_a_known_shard(regclass) -RETURNS void LANGUAGE plpgsql -AS $function$ -BEGIN -END; -$function$; -ERROR: cannot change return type of existing function -HINT: Use DROP FUNCTION relation_is_a_known_shard(regclass) first. -SET citus.enable_version_checks TO 'false'; --- This will fail because of previous function declaration -ALTER EXTENSION citus UPDATE TO '8.1-1'; -NOTICE: version "8.1-1" of extension "citus" is already installed --- We can DROP problematic function and continue ALTER EXTENSION even when version checks are on -SET citus.enable_version_checks TO 'true'; -DROP FUNCTION pg_catalog.relation_is_a_known_shard(regclass); -ERROR: cannot drop function relation_is_a_known_shard(regclass) because extension citus requires it -HINT: You can drop extension citus instead. -SET citus.enable_version_checks TO 'false'; -ALTER EXTENSION citus UPDATE TO '8.1-1'; -NOTICE: version "8.1-1" of extension "citus" is already installed --- Test updating to the latest version without specifying the version number -ALTER EXTENSION citus UPDATE; --- re-create in newest version -DROP EXTENSION citus; -\c -CREATE EXTENSION citus; --- test cache invalidation in workers -\c - - - :worker_1_port -DROP EXTENSION citus; -SET citus.enable_version_checks TO 'false'; -CREATE EXTENSION citus VERSION '8.0-1'; -SET citus.enable_version_checks TO 'true'; --- during ALTER EXTENSION, we should invalidate the cache -ALTER EXTENSION citus UPDATE; --- if cache is invalidated succesfull, this \d should work without any problem -\d - List of relations - Schema | Name | Type | Owner ---------------------------------------------------------------------- - public | citus_tables | view | postgres -(1 row) - -\c - - - :master_port --- test https://github.com/citusdata/citus/issues/3409 -CREATE USER testuser2 SUPERUSER; -NOTICE: not propagating CREATE ROLE/USER commands to worker nodes -HINT: Connect to worker nodes directly to manually create all necessary users and roles. -SET ROLE testuser2; -DROP EXTENSION Citus; --- Loop until we see there's no maintenance daemon running -DO $$begin - for i in 0 .. 100 loop - if i = 100 then raise 'Waited too long'; end if; - PERFORM pg_stat_clear_snapshot(); - perform * from pg_stat_activity where application_name = 'Citus Maintenance Daemon'; - if not found then exit; end if; - perform pg_sleep(0.1); - end loop; -end$$; -SELECT datid, datname, usename FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon'; - datid | datname | usename ---------------------------------------------------------------------- -(0 rows) - -CREATE EXTENSION Citus; --- Loop until we there's a maintenance daemon running -DO $$begin - for i in 0 .. 100 loop - if i = 100 then raise 'Waited too long'; end if; - PERFORM pg_stat_clear_snapshot(); - perform * from pg_stat_activity where application_name = 'Citus Maintenance Daemon'; - if found then exit; end if; - perform pg_sleep(0.1); - end loop; -end$$; -SELECT datid, datname, usename FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon'; - datid | datname | usename ---------------------------------------------------------------------- - 16384 | regression | testuser2 -(1 row) - -RESET ROLE; --- check that maintenance daemon gets (re-)started for the right user -DROP EXTENSION citus; -CREATE USER testuser SUPERUSER; -SET ROLE testuser; -CREATE EXTENSION citus; -SELECT datname, current_database(), - usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus') -FROM test.maintenance_worker(); - datname | current_database | usename | extowner ---------------------------------------------------------------------- - regression | regression | testuser | testuser -(1 row) - --- and recreate as the right owner -RESET ROLE; -DROP EXTENSION citus; -CREATE EXTENSION citus; --- Check that maintenance daemon can also be started in another database -CREATE DATABASE another; -NOTICE: Citus partially supports CREATE DATABASE for distributed databases -DETAIL: Citus does not propagate CREATE DATABASE command to workers -HINT: You can manually create a database and its extensions on workers. -\c another -CREATE EXTENSION citus; -CREATE SCHEMA test; -:create_function_test_maintenance_worker --- see that the daemon started -SELECT datname, current_database(), - usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus') -FROM test.maintenance_worker(); - datname | current_database | usename | extowner ---------------------------------------------------------------------- - another | another | postgres | postgres -(1 row) - --- Test that database with active worker can be dropped. -\c regression -CREATE SCHEMA test_daemon; --- we create a similar function on the regression database --- note that this function checks for the existence of the daemon --- when not found, returns true else tries for 5 times and --- returns false -CREATE OR REPLACE FUNCTION test_daemon.maintenance_daemon_died(p_dbname text) - RETURNS boolean - LANGUAGE plpgsql -AS $$ -DECLARE - activity record; -BEGIN - PERFORM pg_stat_clear_snapshot(); - SELECT * INTO activity FROM pg_stat_activity - WHERE application_name = 'Citus Maintenance Daemon' AND datname = p_dbname; - IF activity.pid IS NULL THEN - RETURN true; - ELSE - RETURN false; - END IF; -END; -$$; --- drop the database and see that the daemon is dead -DROP DATABASE another; -SELECT - * -FROM - test_daemon.maintenance_daemon_died('another'); - maintenance_daemon_died ---------------------------------------------------------------------- - t -(1 row) - --- we don't need the schema and the function anymore -DROP SCHEMA test_daemon CASCADE; -NOTICE: drop cascades to function test_daemon.maintenance_daemon_died(text) --- verify citus does not crash while creating a table when run against an older worker --- create_distributed_table piggybacks multiple commands into single one, if one worker --- did not have the required UDF it should fail instead of crash. --- create a test database, configure citus with single node -CREATE DATABASE another; -NOTICE: Citus partially supports CREATE DATABASE for distributed databases -DETAIL: Citus does not propagate CREATE DATABASE command to workers -HINT: You can manually create a database and its extensions on workers. -\c - - - :worker_1_port -CREATE DATABASE another; -NOTICE: Citus partially supports CREATE DATABASE for distributed databases -DETAIL: Citus does not propagate CREATE DATABASE command to workers -HINT: You can manually create a database and its extensions on workers. -\c - - - :master_port -\c another -CREATE EXTENSION citus; -SET citus.enable_object_propagation TO off; -- prevent distributed transactions during add node -SELECT FROM master_add_node('localhost', :worker_1_port); -WARNING: citus.enable_object_propagation is off, not creating distributed objects on worker -DETAIL: distributed objects are only kept in sync when citus.enable_object_propagation is set to on. Newly activated nodes will not get these objects created --- -(1 row) - -\c - - - :worker_1_port -CREATE EXTENSION citus; -ALTER FUNCTION assign_distributed_transaction_id(initiator_node_identifier integer, transaction_number bigint, transaction_stamp timestamp with time zone) -RENAME TO dummy_assign_function; -\c - - - :master_port -SET citus.shard_replication_factor to 1; --- create_distributed_table command should fail -CREATE TABLE t1(a int, b int); -SET client_min_messages TO ERROR; -DO $$ -BEGIN - BEGIN - SELECT create_distributed_table('t1', 'a'); - EXCEPTION WHEN OTHERS THEN - RAISE 'create distributed table failed'; - END; -END; -$$; -ERROR: create distributed table failed -CONTEXT: PL/pgSQL function inline_code_block line 6 at RAISE -\c regression -\c - - - :master_port -DROP DATABASE another; -\c - - - :worker_1_port -DROP DATABASE another; -\c - - - :master_port --- only the regression database should have a maintenance daemon -SELECT count(*) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon'; - count ---------------------------------------------------------------------- - 1 -(1 row) - --- recreate the extension immediately after the maintenancae daemon errors -SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon'; - pg_cancel_backend ---------------------------------------------------------------------- - t -(1 row) - -DROP EXTENSION citus; -CREATE EXTENSION citus; --- wait for maintenance daemon restart -SELECT datname, current_database(), - usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus') -FROM test.maintenance_worker(); - datname | current_database | usename | extowner ---------------------------------------------------------------------- - regression | regression | postgres | postgres -(1 row) - --- confirm that there is only one maintenance daemon -SELECT count(*) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon'; - count ---------------------------------------------------------------------- - 1 -(1 row) - --- kill the maintenance daemon -SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon'; - pg_cancel_backend ---------------------------------------------------------------------- - t -(1 row) - --- reconnect -\c - - - :master_port --- run something that goes through planner hook and therefore kicks of maintenance daemon -SELECT 1; - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - --- wait for maintenance daemon restart -SELECT datname, current_database(), - usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus') -FROM test.maintenance_worker(); - datname | current_database | usename | extowner ---------------------------------------------------------------------- - regression | regression | postgres | postgres -(1 row) - --- confirm that there is only one maintenance daemon -SELECT count(*) FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon'; - count ---------------------------------------------------------------------- - 1 -(1 row) - -DROP TABLE version_mismatch_table; diff --git a/src/test/regress/expected/multi_insert_select.out b/src/test/regress/expected/multi_insert_select.out index e556d3e8e..24b7d2a2d 100644 --- a/src/test/regress/expected/multi_insert_select.out +++ b/src/test/regress/expected/multi_insert_select.out @@ -654,11 +654,8 @@ DEBUG: Router planner cannot handle multi-shard select queries (9 rows) -- We support CTEs --- but prefer to prevent inlining of the CTE --- in order not to diverge from pg 11 vs pg 12 BEGIN; -SET LOCAL citus.enable_cte_inlining TO false; -WITH fist_table_agg AS +WITH fist_table_agg AS MATERIALIZED (SELECT max(value_1)+1 as v1_agg, user_id FROM raw_events_first GROUP BY user_id) INSERT INTO agg_events (value_1_agg, user_id) diff --git a/src/test/regress/expected/multi_insert_select_conflict.out b/src/test/regress/expected/multi_insert_select_conflict.out index 1810fa69f..5f2344432 100644 --- a/src/test/regress/expected/multi_insert_select_conflict.out +++ b/src/test/regress/expected/multi_insert_select_conflict.out @@ -172,10 +172,8 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator 10 | 0 (10 rows) --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -- Get the select part from cte and do nothing on conflict -WITH cte AS( +WITH cte AS MATERIALIZED ( SELECT col_1, col_2 FROM source_table_1 ) INSERT INTO target_table SELECT * FROM cte ON CONFLICT DO NOTHING; @@ -184,7 +182,7 @@ DEBUG: generating subplan XXX_1 for CTE cte: SELECT col_1, col_2 FROM on_confli DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT col_1, col_2 FROM (SELECT cte.col_1, cte.col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer)) cte) citus_insert_select_subquery DEBUG: Collecting INSERT ... SELECT results on coordinator -- Get the select part from cte and update on conflict -WITH cte AS( +WITH cte AS MATERIALIZED ( SELECT col_1, col_2 FROM source_table_1 ) INSERT INTO target_table SELECT * FROM cte ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1; @@ -207,7 +205,6 @@ SELECT * FROM target_table ORDER BY 1; 10 | 0 (10 rows) -SET citus.enable_cte_inlining TO true; -- Test with multiple CTEs WITH cte AS( SELECT col_1, col_2 FROM source_table_1 @@ -234,17 +231,15 @@ SELECT * FROM target_table ORDER BY 1; 10 | 11 (10 rows) --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -WITH inserted_table AS ( - WITH cte AS( +WITH inserted_table AS MATERIALIZED ( + WITH cte AS MATERIALIZED ( SELECT col_1, col_2, col_3 FROM source_table_1 - ), cte_2 AS( + ), cte_2 AS MATERIALIZED ( SELECT col_1, col_2 FROM cte ) INSERT INTO target_table SELECT * FROM cte_2 ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1 RETURNING * ) SELECT * FROM inserted_table ORDER BY 1; -DEBUG: generating subplan XXX_1 for CTE inserted_table: WITH cte AS (SELECT source_table_1.col_1, source_table_1.col_2, source_table_1.col_3 FROM on_conflict.source_table_1), cte_2 AS (SELECT cte.col_1, cte.col_2 FROM cte) INSERT INTO on_conflict.target_table (col_1, col_2) SELECT col_1, col_2 FROM cte_2 ON CONFLICT(col_1) DO UPDATE SET col_2 = (excluded.col_2 OPERATOR(pg_catalog.+) 1) RETURNING target_table.col_1, target_table.col_2 +DEBUG: generating subplan XXX_1 for CTE inserted_table: WITH cte AS MATERIALIZED (SELECT source_table_1.col_1, source_table_1.col_2, source_table_1.col_3 FROM on_conflict.source_table_1), cte_2 AS MATERIALIZED (SELECT cte.col_1, cte.col_2 FROM cte) INSERT INTO on_conflict.target_table (col_1, col_2) SELECT col_1, col_2 FROM cte_2 ON CONFLICT(col_1) DO UPDATE SET col_2 = (excluded.col_2 OPERATOR(pg_catalog.+) 1) RETURNING target_table.col_1, target_table.col_2 DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: generating subplan XXX_1 for CTE cte: SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT col_1, col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2, intermediate_result.col_3 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer, col_3 integer)) cte @@ -260,14 +255,14 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator 5 | 6 (5 rows) -WITH cte AS ( - WITH basic AS ( +WITH cte AS MATERIALIZED ( + WITH basic AS MATERIALIZED ( SELECT col_1, col_2 FROM source_table_1 ) INSERT INTO target_table (SELECT * FROM basic) ON CONFLICT DO NOTHING RETURNING * ) UPDATE target_table SET col_2 = 4 WHERE col_1 IN (SELECT col_1 FROM cte); -DEBUG: generating subplan XXX_1 for CTE cte: WITH basic AS (SELECT source_table_1.col_1, source_table_1.col_2 FROM on_conflict.source_table_1) INSERT INTO on_conflict.target_table (col_1, col_2) SELECT col_1, col_2 FROM basic ON CONFLICT DO NOTHING RETURNING target_table.col_1, target_table.col_2 +DEBUG: generating subplan XXX_1 for CTE cte: WITH basic AS MATERIALIZED (SELECT source_table_1.col_1, source_table_1.col_2 FROM on_conflict.source_table_1) INSERT INTO on_conflict.target_table (col_1, col_2) SELECT col_1, col_2 FROM basic ON CONFLICT DO NOTHING RETURNING target_table.col_1, target_table.col_2 DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: generating subplan XXX_1 for CTE basic: SELECT col_1, col_2 FROM on_conflict.source_table_1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT col_1, col_2 FROM (SELECT basic.col_1, basic.col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer)) basic) citus_insert_select_subquery @@ -290,17 +285,14 @@ SELECT FROM cte, source_table_1 WHERE cte.col_1 = source_table_1.col_1 ON CONFLICT DO NOTHING; $Q$); - coordinator_plan + coordinator_plan --------------------------------------------------------------------- Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: repartition -> Custom Scan (Citus Adaptive) - -> Distributed Subplan XXX_1 - -> Custom Scan (Citus Adaptive) - Task Count: 4 -(6 rows) + Task Count: 4 +(4 rows) -SET citus.enable_cte_inlining TO true; -- Tests with foreign key to reference table CREATE TABLE test_ref_table (key int PRIMARY KEY); SELECT create_reference_table('test_ref_table'); @@ -515,11 +507,9 @@ SELECT * FROM target_table ORDER BY 1; 10 | 0 (10 rows) --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -WITH cte AS( +WITH cte AS MATERIALIZED( SELECT col_1, col_2, col_3 FROM source_table_1 -), cte_2 AS( +), cte_2 AS MATERIALIZED( SELECT col_1, col_2 FROM cte ) INSERT INTO target_table SELECT * FROM cte_2 ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1; diff --git a/src/test/regress/expected/multi_modifying_xacts.out b/src/test/regress/expected/multi_modifying_xacts.out index c684fc1dc..24b6e7133 100644 --- a/src/test/regress/expected/multi_modifying_xacts.out +++ b/src/test/regress/expected/multi_modifying_xacts.out @@ -357,7 +357,7 @@ ORDER BY nodeport; (2 rows) -- register after insert trigger -SELECT * FROM run_command_on_placements('researchers', 'CREATE CONSTRAINT TRIGGER reject_large_researcher_id AFTER INSERT ON %s DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION reject_large_id()') +SELECT * FROM run_command_on_placements('researchers', 'CREATE CONSTRAINT TRIGGER reject_large_researcher_id AFTER INSERT ON %s DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE reject_large_id()') ORDER BY nodeport, shardid; nodename | nodeport | shardid | success | result --------------------------------------------------------------------- @@ -519,7 +519,7 @@ $rb$ LANGUAGE plpgsql; CREATE CONSTRAINT TRIGGER reject_bad AFTER INSERT ON objects_1200003 DEFERRABLE INITIALLY IMMEDIATE -FOR EACH ROW EXECUTE FUNCTION reject_bad(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad(); \c - - - :master_port -- test partial failure; worker_1 succeeds, 2 fails -- in this case, we expect the transaction to abort @@ -570,7 +570,7 @@ $rb$ LANGUAGE plpgsql; CREATE CONSTRAINT TRIGGER reject_bad AFTER INSERT ON labs_1200002 DEFERRABLE INITIALLY IMMEDIATE -FOR EACH ROW EXECUTE FUNCTION reject_bad(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad(); \c - - - :master_port BEGIN; INSERT INTO objects VALUES (1, 'apple'); @@ -611,7 +611,7 @@ DROP TRIGGER reject_bad ON objects_1200003; CREATE CONSTRAINT TRIGGER reject_bad AFTER INSERT ON objects_1200003 DEFERRABLE INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION reject_bad(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad(); \c - - - :master_port -- should be the same story as before, just at COMMIT time BEGIN; @@ -654,7 +654,7 @@ DROP TRIGGER reject_bad ON labs_1200002; CREATE CONSTRAINT TRIGGER reject_bad AFTER INSERT ON labs_1200002 DEFERRABLE INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION reject_bad(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad(); \c - - - :master_port BEGIN; INSERT INTO objects VALUES (1, 'apple'); @@ -856,7 +856,7 @@ $rb$ LANGUAGE plpgsql; CREATE CONSTRAINT TRIGGER reject_bad_reference AFTER INSERT ON reference_modifying_xacts_1200006 DEFERRABLE INITIALLY IMMEDIATE -FOR EACH ROW EXECUTE FUNCTION reject_bad_reference(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_reference(); \c - - - :master_port \set VERBOSITY terse -- try without wrapping inside a transaction @@ -873,7 +873,7 @@ DROP TRIGGER reject_bad_reference ON reference_modifying_xacts_1200006; CREATE CONSTRAINT TRIGGER reject_bad_reference AFTER INSERT ON reference_modifying_xacts_1200006 DEFERRABLE INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION reject_bad_reference(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_reference(); \c - - - :master_port \set VERBOSITY terse -- try without wrapping inside a transaction @@ -944,7 +944,7 @@ $rb$ LANGUAGE plpgsql; CREATE CONSTRAINT TRIGGER reject_bad_hash AFTER INSERT ON hash_modifying_xacts_1200007 DEFERRABLE INITIALLY IMMEDIATE -FOR EACH ROW EXECUTE FUNCTION reject_bad_hash(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_hash(); \c - - - :master_port \set VERBOSITY terse -- the transaction as a whole should fail @@ -967,7 +967,7 @@ DROP TRIGGER reject_bad_hash ON hash_modifying_xacts_1200007; CREATE CONSTRAINT TRIGGER reject_bad_hash AFTER INSERT ON hash_modifying_xacts_1200007 DEFERRABLE INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION reject_bad_hash(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_hash(); \c - - - :master_port \set VERBOSITY terse -- the transaction as a whole should fail @@ -1004,7 +1004,7 @@ ORDER BY s.logicalrelid, sp.shardstate; CREATE CONSTRAINT TRIGGER reject_bad_reference AFTER INSERT ON reference_modifying_xacts_1200006 DEFERRABLE INITIALLY IMMEDIATE -FOR EACH ROW EXECUTE FUNCTION reject_bad_reference(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_reference(); \c - - - :master_port \set VERBOSITY terse BEGIN; diff --git a/src/test/regress/expected/multi_mx_explain.out b/src/test/regress/expected/multi_mx_explain.out index e0ffa7618..c5495a839 100644 --- a/src/test/regress/expected/multi_mx_explain.out +++ b/src/test/regress/expected/multi_mx_explain.out @@ -285,18 +285,18 @@ Sort EXPLAIN (COSTS FALSE, VERBOSE TRUE) SELECT sum(l_quantity) / avg(l_quantity) FROM lineitem_mx; Aggregate - Output: xxxxxx + Output: (sum(remote_scan."?column?") / (sum(remote_scan."?column?_1") / pg_catalog.sum(remote_scan."?column?_2"))) -> Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan."?column?", remote_scan."?column?_1", remote_scan."?column?_2" Task Count: 16 Tasks Shown: One of 16 -> Task Query: SELECT sum(l_quantity), sum(l_quantity), count(l_quantity) FROM lineitem_mx_1220052 lineitem_mx WHERE true Node: host=localhost port=xxxxx dbname=regression -> Aggregate - Output: xxxxxx + Output: sum(l_quantity), sum(l_quantity), count(l_quantity) -> Seq Scan on public.lineitem_mx_1220052 lineitem_mx - Output: xxxxxx + 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 diff --git a/src/test/regress/expected/multi_mx_modifying_xacts.out b/src/test/regress/expected/multi_mx_modifying_xacts.out index 072c9b422..0466c847e 100644 --- a/src/test/regress/expected/multi_mx_modifying_xacts.out +++ b/src/test/regress/expected/multi_mx_modifying_xacts.out @@ -239,7 +239,7 @@ $rb$ LANGUAGE plpgsql; CREATE CONSTRAINT TRIGGER reject_bad_mx AFTER INSERT ON objects_mx_1220103 DEFERRABLE INITIALLY IMMEDIATE -FOR EACH ROW EXECUTE FUNCTION reject_bad_mx(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_mx(); -- test partial failure; statement 1 successed, statement 2 fails \set VERBOSITY terse BEGIN; @@ -283,7 +283,7 @@ SELECT * FROM labs_mx WHERE id = 7; CREATE CONSTRAINT TRIGGER reject_bad_mx AFTER INSERT ON labs_mx_1220102 DEFERRABLE INITIALLY IMMEDIATE -FOR EACH ROW EXECUTE FUNCTION reject_bad_mx(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_mx(); BEGIN; SET LOCAL citus.enable_local_execution TO off; INSERT INTO objects_mx VALUES (1, 'apple'); @@ -333,7 +333,7 @@ DROP TRIGGER reject_bad_mx ON objects_mx_1220103; CREATE CONSTRAINT TRIGGER reject_bad_mx AFTER INSERT ON objects_mx_1220103 DEFERRABLE INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION reject_bad_mx(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_mx(); -- should be the same story as before, just at COMMIT time BEGIN; SET LOCAL citus.enable_local_execution TO off; @@ -361,7 +361,7 @@ DROP TRIGGER reject_bad_mx ON labs_mx_1220102; CREATE CONSTRAINT TRIGGER reject_bad_mx AFTER INSERT ON labs_mx_1220102 DEFERRABLE INITIALLY DEFERRED -FOR EACH ROW EXECUTE FUNCTION reject_bad_mx(); +FOR EACH ROW EXECUTE PROCEDURE reject_bad_mx(); BEGIN; SET LOCAL citus.enable_local_execution TO off; INSERT INTO objects_mx VALUES (1, 'apple'); diff --git a/src/test/regress/expected/multi_mx_router_planner.out b/src/test/regress/expected/multi_mx_router_planner.out index ee4c670bb..84ebb243b 100644 --- a/src/test/regress/expected/multi_mx_router_planner.out +++ b/src/test/regress/expected/multi_mx_router_planner.out @@ -2,9 +2,7 @@ -- test router planner functionality for single shard select queries -- =================================================================== -- run all the router queries from the one of the workers --- prevent PG 11 - PG 12 outputs to diverge -- and CTE inlining is not relevant to router plannery anyway -SET citus.enable_cte_inlining TO false; \c - - - :worker_1_port -- this table is used in a CTE test CREATE TABLE authors_hash_mx ( name text, id bigint ); diff --git a/src/test/regress/expected/multi_query_directory_cleanup_0.out b/src/test/regress/expected/multi_query_directory_cleanup_0.out deleted file mode 100644 index 37fbcc364..000000000 --- a/src/test/regress/expected/multi_query_directory_cleanup_0.out +++ /dev/null @@ -1,288 +0,0 @@ --- --- MULTI_QUERY_DIRECTORY_CLEANUP --- --- We execute sub-queries on worker nodes, and copy query results to a directory --- on the master node for final processing. When the query completes or fails, --- the resource owner should automatically clean up these intermediate query --- result files. -SET citus.next_shard_id TO 810000; -SET citus.enable_unique_job_ids TO off; -CREATE FUNCTION citus_rm_job_directory(bigint) - RETURNS void - AS 'citus' - LANGUAGE C STRICT; -with silence as ( - SELECT citus_rm_job_directory(split_part(f, '_', 2)::bigint) - from pg_ls_dir('base/pgsql_job_cache') f -) -select count(*) * 0 zero -from silence; - zero ---------------------------------------------------------------------- - 0 -(1 row) - -BEGIN; --- pg_ls_dir() displays jobids. We explicitly set the jobId sequence --- here so that the regression output becomes independent of the --- number of jobs executed prior to running this test. -SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -SELECT pg_ls_dir('base/pgsql_job_cache'); - pg_ls_dir ---------------------------------------------------------------------- -(0 rows) - -COMMIT; -SELECT pg_ls_dir('base/pgsql_job_cache'); - pg_ls_dir ---------------------------------------------------------------------- -(0 rows) - -BEGIN; -SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -SELECT pg_ls_dir('base/pgsql_job_cache'); - pg_ls_dir ---------------------------------------------------------------------- -(0 rows) - -ROLLBACK; -SELECT pg_ls_dir('base/pgsql_job_cache'); - pg_ls_dir ---------------------------------------------------------------------- -(0 rows) - --- Test that multiple job directories are all cleaned up correctly, --- both individually (by closing a cursor) and in bulk when ending a --- transaction. -BEGIN; -DECLARE c_00 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_00; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_01 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_01; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_02 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_02; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_03 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_03; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_04 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_04; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_05 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_05; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_06 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_06; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_07 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_07; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_08 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_08; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_09 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_09; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_10 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_10; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_11 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_11; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_12 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_12; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_13 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_13; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_14 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_14; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_15 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_15; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_16 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_16; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_17 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_17; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_18 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_18; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -DECLARE c_19 CURSOR FOR SELECT sum(l_extendedprice * l_discount) as revenue FROM lineitem; -FETCH 1 FROM c_19; - revenue ---------------------------------------------------------------------- - 22770844.7654 -(1 row) - -SELECT * FROM pg_ls_dir('base/pgsql_job_cache') f ORDER BY f; - f ---------------------------------------------------------------------- - master_job_0007 - master_job_0008 - master_job_0009 - master_job_0010 - master_job_0011 - master_job_0012 - master_job_0013 - master_job_0014 - master_job_0015 - master_job_0016 - master_job_0017 - master_job_0018 - master_job_0019 - master_job_0020 - master_job_0021 - master_job_0022 - master_job_0023 - master_job_0024 - master_job_0025 - master_job_0026 -(20 rows) - --- close first, 17th (first after re-alloc) and last cursor. -CLOSE c_00; -CLOSE c_16; -CLOSE c_19; -SELECT * FROM pg_ls_dir('base/pgsql_job_cache') f ORDER BY f; - f ---------------------------------------------------------------------- - master_job_0008 - master_job_0009 - master_job_0010 - master_job_0011 - master_job_0012 - master_job_0013 - master_job_0014 - master_job_0015 - master_job_0016 - master_job_0017 - master_job_0018 - master_job_0019 - master_job_0020 - master_job_0021 - master_job_0022 - master_job_0024 - master_job_0025 -(17 rows) - -ROLLBACK; -SELECT pg_ls_dir('base/pgsql_job_cache'); - pg_ls_dir ---------------------------------------------------------------------- -(0 rows) - diff --git a/src/test/regress/expected/multi_router_planner.out b/src/test/regress/expected/multi_router_planner.out index 11c52c29b..fb9cfb7d5 100644 --- a/src/test/regress/expected/multi_router_planner.out +++ b/src/test/regress/expected/multi_router_planner.out @@ -6,9 +6,6 @@ SET citus.next_shard_id TO 840000; -- router planner, so we're disabling it in this file. We've bunch of -- other tests that triggers fast-path-router planner SET citus.enable_fast_path_router_planner TO false; --- prevent PG 11 - PG 12 outputs to diverge --- and CTE inlining is not relevant to router plannery anyway -SET citus.enable_cte_inlining TO false; CREATE TABLE articles_hash ( id bigint NOT NULL, author_id bigint NOT NULL, @@ -288,7 +285,7 @@ DEBUG: query has a single distribution column value: 1 (5 rows) -- queries with CTEs are supported -WITH first_author AS ( SELECT id FROM articles_hash WHERE author_id = 1) +WITH first_author AS MATERIALIZED ( SELECT id FROM articles_hash WHERE author_id = 1) SELECT * FROM first_author; DEBUG: Creating router plan DEBUG: query has a single distribution column value: 1 @@ -303,7 +300,7 @@ DEBUG: query has a single distribution column value: 1 -- SELECT FOR UPDATE is supported if not involving reference table BEGIN; -WITH first_author AS ( +WITH first_author AS MATERIALIZED ( SELECT articles_hash.id, auref.name FROM articles_hash, authors_reference auref WHERE author_id = 2 AND auref.id = author_id FOR UPDATE @@ -317,7 +314,7 @@ DEBUG: query has a single distribution column value: 2 DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE public.articles_hash SET title = first_author.name FROM (SELECT intermediate_result.id, intermediate_result.name FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(id bigint, name character varying(20))) first_author WHERE ((articles_hash.author_id OPERATOR(pg_catalog.=) 2) AND (articles_hash.id OPERATOR(pg_catalog.=) first_author.id)) DEBUG: Creating router plan DEBUG: query has a single distribution column value: 2 -WITH first_author AS ( +WITH first_author AS MATERIALIZED ( SELECT id, word_count FROM articles_hash WHERE author_id = 2 FOR UPDATE ) @@ -326,7 +323,7 @@ FROM first_author WHERE articles_hash.author_id = 2 AND articles_hash.id = first DEBUG: Creating router plan DEBUG: query has a single distribution column value: 2 -- Without FOR UPDATE this is router plannable -WITH first_author AS ( +WITH first_author AS MATERIALIZED ( SELECT articles_hash.id, auref.name FROM articles_hash, authors_reference auref WHERE author_id = 2 AND auref.id = author_id ) @@ -336,7 +333,7 @@ DEBUG: Creating router plan DEBUG: query has a single distribution column value: 2 ROLLBACK; -- queries with CTEs are supported even if CTE is not referenced inside query -WITH first_author AS ( SELECT id FROM articles_hash WHERE author_id = 1) +WITH first_author AS MATERIALIZED ( SELECT id FROM articles_hash WHERE author_id = 1) SELECT title FROM articles_hash WHERE author_id = 1; DEBUG: Creating router plan DEBUG: query has a single distribution column value: 1 @@ -350,8 +347,8 @@ DEBUG: query has a single distribution column value: 1 (5 rows) -- two CTE joins are supported if they go to the same worker -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 1) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 1) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; DEBUG: Creating router plan DEBUG: query has a single distribution column value: 1 @@ -364,8 +361,8 @@ DEBUG: query has a single distribution column value: 1 41 | 1 | 41 | aznavour (5 rows) -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 3) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 3) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; DEBUG: Creating router plan id | author_id | id | title @@ -373,8 +370,8 @@ DEBUG: Creating router plan (0 rows) -- CTE joins are supported because they are both planned recursively -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 2) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 2) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; DEBUG: cannot run command which targets multiple shards DEBUG: generating subplan XXX_1 for CTE id_author: SELECT id, author_id FROM public.articles_hash WHERE (author_id OPERATOR(pg_catalog.=) 1) @@ -428,7 +425,7 @@ INSERT INTO company_employees values(3, 3, 1); DEBUG: Creating router plan DEBUG: query has a single distribution column value: 3 -- find employees at top 2 level within company hierarchy -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 @@ -450,7 +447,7 @@ DEBUG: query has a single distribution column value: 1 -- query becomes not router plannble and gets rejected -- if filter on company is dropped -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 @@ -464,7 +461,7 @@ DEBUG: Router planner cannot handle multi-shard select queries ERROR: recursive CTEs are not supported in distributed queries -- logically wrong query, query involves different shards -- from the same table -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 3 and manager_id = 0 @@ -478,7 +475,7 @@ SELECT * FROM hierarchy WHERE LEVEL <= 2; DEBUG: cannot run command which targets multiple shards ERROR: recursive CTEs are not supported in distributed queries -- Test router modifying CTEs -WITH new_article AS ( +WITH new_article AS MATERIALIZED( INSERT INTO articles_hash VALUES (1, 1, 'arsenous', 9) RETURNING * ) SELECT * FROM new_article; @@ -493,7 +490,7 @@ DEBUG: Creating router plan 1 | 1 | arsenous | 9 (1 row) -WITH update_article AS ( +WITH update_article AS MATERIALIZED( UPDATE articles_hash SET word_count = 10 WHERE id = 1 AND word_count = 9 RETURNING * ) SELECT * FROM update_article; @@ -507,7 +504,7 @@ DEBUG: Creating router plan 1 | 1 | arsenous | 10 (1 row) -WITH update_article AS ( +WITH update_article AS MATERIALIZED ( UPDATE articles_hash SET word_count = 11 WHERE id = 1 AND word_count = 10 RETURNING * ) SELECT coalesce(1,random()); @@ -521,7 +518,7 @@ DEBUG: Creating router plan 1 (1 row) -WITH update_article AS ( +WITH update_article AS MATERIALIZED ( UPDATE articles_hash SET word_count = 10 WHERE author_id = 1 AND id = 1 AND word_count = 11 RETURNING * ) SELECT coalesce(1,random()); @@ -532,7 +529,7 @@ DEBUG: query has a single distribution column value: 1 1 (1 row) -WITH update_article AS ( +WITH update_article AS MATERIALIZED ( UPDATE authors_reference SET name = '' WHERE id = 0 RETURNING * ) SELECT coalesce(1,random()); @@ -546,7 +543,7 @@ DEBUG: Creating router plan 1 (1 row) -WITH delete_article AS ( +WITH delete_article AS MATERIALIZED ( DELETE FROM articles_hash WHERE id = 1 AND word_count = 10 RETURNING * ) SELECT * FROM delete_article; @@ -561,8 +558,8 @@ DEBUG: Creating router plan (1 row) -- Modifying statement in nested CTE case is covered by PostgreSQL itself -WITH new_article AS ( - WITH nested_cte AS ( +WITH new_article AS MATERIALIZED( + WITH nested_cte AS MATERIALIZED( INSERT INTO articles_hash VALUES (1, 1, 'arsenous', 9572) RETURNING * ) SELECT * FROM nested_cte @@ -571,7 +568,7 @@ SELECT * FROM new_article; ERROR: WITH clause containing a data-modifying statement must be at the top level -- Modifying statement in a CTE in subquery is also covered by PostgreSQL SELECT * FROM ( - WITH new_article AS ( + WITH new_article AS MATERIALIZED ( INSERT INTO articles_hash VALUES (1, 1, 'arsenous', 9572) RETURNING * ) SELECT * FROM new_article @@ -809,7 +806,7 @@ DEBUG: query has a single distribution column value: 10 -- following join is not router plannable since there are no -- workers containing both shards, but will work through recursive -- planning -WITH single_shard as (SELECT * FROM articles_single_shard_hash) +WITH single_shard as MATERIALIZED(SELECT * FROM articles_single_shard_hash) SELECT a.author_id as first_author, b.word_count as second_word_count FROM articles_hash a, single_shard b WHERE a.author_id = 2 and a.author_id = b.author_id @@ -1623,7 +1620,7 @@ DEBUG: Creating router plan (0 rows) -- with a CTE in a view -WITH cte AS (SELECT * FROM num_db) +WITH cte AS MATERIALIZED (SELECT * FROM num_db) SELECT 1 FROM authors_reference r JOIN cte ON (r.id = cte.datid) LIMIT 1; DEBUG: found no worker with all shard placements DEBUG: generating subplan XXX_1 for CTE cte: SELECT datid FROM (SELECT s.datid FROM (public.number1() s(datid) LEFT JOIN pg_database d ON (((s.datid)::oid OPERATOR(pg_catalog.=) d.oid)))) num_db @@ -1636,7 +1633,7 @@ DEBUG: Creating router plan -- hide changes between major versions RESET client_min_messages; -- with pg_stat_activity view -WITH pg_stat_activity AS ( +WITH pg_stat_activity AS MATERIALIZED( SELECT pg_stat_activity.datid, pg_stat_activity.application_name, @@ -1653,15 +1650,15 @@ SET client_min_messages TO DEBUG2; -- terse because distribution column inference varies between pg11 & pg12 \set VERBOSITY terse RESET client_min_messages; -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 1 and 1=0) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 1 and 1=0) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; id | author_id | id | title --------------------------------------------------------------------- (0 rows) -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 1) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 1) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id and 1=0; id | author_id | id | title --------------------------------------------------------------------- @@ -1669,7 +1666,7 @@ SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id and 1=0; SET client_min_messages TO DEBUG2; \set VERBOSITY DEFAULT -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 @@ -1686,7 +1683,7 @@ DEBUG: query has a single distribution column value: 1 --------------------------------------------------------------------- (0 rows) -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 @@ -1704,7 +1701,7 @@ DEBUG: query has a single distribution column value: 1 1 | 1 | 0 | 1 (1 row) -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 AND 1=0 diff --git a/src/test/regress/expected/multi_simple_queries_0.out b/src/test/regress/expected/multi_simple_queries_0.out deleted file mode 100644 index 461d7db0a..000000000 --- a/src/test/regress/expected/multi_simple_queries_0.out +++ /dev/null @@ -1,711 +0,0 @@ -SET citus.next_shard_id TO 850000; --- many of the tests in this file is intended for testing non-fast-path --- router planner, so we're explicitly disabling it in this file. --- We've bunch of other tests that triggers fast-path-router -SET citus.enable_fast_path_router_planner TO false; -SET citus.coordinator_aggregation_strategy TO 'disabled'; --- =================================================================== --- test end-to-end query functionality --- =================================================================== -CREATE TABLE articles ( - id bigint NOT NULL, - author_id bigint NOT NULL, - title varchar(20) NOT NULL, - word_count integer NOT NULL CHECK (word_count > 0) -); --- this table is used in a CTE test -CREATE TABLE authors ( name text, id bigint ); --- this table is used in router executor tests -CREATE TABLE articles_single_shard (LIKE articles); -SELECT master_create_distributed_table('articles', 'author_id', 'hash'); - master_create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT master_create_distributed_table('articles_single_shard', 'author_id', 'hash'); - master_create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -SELECT master_create_worker_shards('articles', 2, 1); - master_create_worker_shards ---------------------------------------------------------------------- - -(1 row) - -SELECT master_create_worker_shards('articles_single_shard', 1, 1); - master_create_worker_shards ---------------------------------------------------------------------- - -(1 row) - --- create a bunch of test data -INSERT INTO articles VALUES ( 1, 1, 'arsenous', 9572); -INSERT INTO articles VALUES ( 2, 2, 'abducing', 13642); -INSERT INTO articles VALUES ( 3, 3, 'asternal', 10480); -INSERT INTO articles VALUES ( 4, 4, 'altdorfer', 14551); -INSERT INTO articles VALUES ( 5, 5, 'aruru', 11389); -INSERT INTO articles VALUES ( 6, 6, 'atlases', 15459); -INSERT INTO articles VALUES ( 7, 7, 'aseptic', 12298); -INSERT INTO articles VALUES ( 8, 8, 'agatized', 16368); -INSERT INTO articles VALUES ( 9, 9, 'alligate', 438); -INSERT INTO articles VALUES (10, 10, 'aggrandize', 17277); -INSERT INTO articles VALUES (11, 1, 'alamo', 1347); -INSERT INTO articles VALUES (12, 2, 'archiblast', 18185); -INSERT INTO articles VALUES (13, 3, 'aseyev', 2255); -INSERT INTO articles VALUES (14, 4, 'andesite', 19094); -INSERT INTO articles VALUES (15, 5, 'adversa', 3164); -INSERT INTO articles VALUES (16, 6, 'allonym', 2); -INSERT INTO articles VALUES (17, 7, 'auriga', 4073); -INSERT INTO articles VALUES (18, 8, 'assembly', 911); -INSERT INTO articles VALUES (19, 9, 'aubergiste', 4981); -INSERT INTO articles VALUES (20, 10, 'absentness', 1820); -INSERT INTO articles VALUES (21, 1, 'arcading', 5890); -INSERT INTO articles VALUES (22, 2, 'antipope', 2728); -INSERT INTO articles VALUES (23, 3, 'abhorring', 6799); -INSERT INTO articles VALUES (24, 4, 'audacious', 3637); -INSERT INTO articles VALUES (25, 5, 'antehall', 7707); -INSERT INTO articles VALUES (26, 6, 'abington', 4545); -INSERT INTO articles VALUES (27, 7, 'arsenous', 8616); -INSERT INTO articles VALUES (28, 8, 'aerophyte', 5454); -INSERT INTO articles VALUES (29, 9, 'amateur', 9524); -INSERT INTO articles VALUES (30, 10, 'andelee', 6363); -INSERT INTO articles VALUES (31, 1, 'athwartships', 7271); -INSERT INTO articles VALUES (32, 2, 'amazon', 11342); -INSERT INTO articles VALUES (33, 3, 'autochrome', 8180); -INSERT INTO articles VALUES (34, 4, 'amnestied', 12250); -INSERT INTO articles VALUES (35, 5, 'aminate', 9089); -INSERT INTO articles VALUES (36, 6, 'ablation', 13159); -INSERT INTO articles VALUES (37, 7, 'archduchies', 9997); -INSERT INTO articles VALUES (38, 8, 'anatine', 14067); -INSERT INTO articles VALUES (39, 9, 'anchises', 10906); -INSERT INTO articles VALUES (40, 10, 'attemper', 14976); -INSERT INTO articles VALUES (41, 1, 'aznavour', 11814); -INSERT INTO articles VALUES (42, 2, 'ausable', 15885); -INSERT INTO articles VALUES (43, 3, 'affixal', 12723); -INSERT INTO articles VALUES (44, 4, 'anteport', 16793); -INSERT INTO articles VALUES (45, 5, 'afrasia', 864); -INSERT INTO articles VALUES (46, 6, 'atlanta', 17702); -INSERT INTO articles VALUES (47, 7, 'abeyance', 1772); -INSERT INTO articles VALUES (48, 8, 'alkylic', 18610); -INSERT INTO articles VALUES (49, 9, 'anyone', 2681); -INSERT INTO articles VALUES (50, 10, 'anjanette', 19519); --- insert a single row for the test -INSERT INTO articles_single_shard VALUES (50, 10, 'anjanette', 19519); --- zero-shard modifications should succeed -UPDATE articles SET title = '' WHERE author_id = 1 AND author_id = 2; -UPDATE articles SET title = '' WHERE 0 = 1; -DELETE FROM articles WHERE author_id = 1 AND author_id = 2; --- single-shard tests --- test simple select for a single row -SELECT * FROM articles WHERE author_id = 10 AND id = 50; - id | author_id | title | word_count ---------------------------------------------------------------------- - 50 | 10 | anjanette | 19519 -(1 row) - --- get all titles by a single author -SELECT title FROM articles WHERE author_id = 10; - title ---------------------------------------------------------------------- - aggrandize - absentness - andelee - attemper - anjanette -(5 rows) - --- try ordering them by word count -SELECT title, word_count FROM articles - WHERE author_id = 10 - ORDER BY word_count DESC NULLS LAST; - title | word_count ---------------------------------------------------------------------- - anjanette | 19519 - aggrandize | 17277 - attemper | 14976 - andelee | 6363 - absentness | 1820 -(5 rows) - --- look at last two articles by an author -SELECT title, id FROM articles - WHERE author_id = 5 - ORDER BY id - LIMIT 2; - title | id ---------------------------------------------------------------------- - aruru | 5 - adversa | 15 -(2 rows) - --- find all articles by two authors in same shard -SELECT title, author_id FROM articles - WHERE author_id = 7 OR author_id = 8 - ORDER BY author_id ASC, id; - title | author_id ---------------------------------------------------------------------- - aseptic | 7 - auriga | 7 - arsenous | 7 - archduchies | 7 - abeyance | 7 - agatized | 8 - assembly | 8 - aerophyte | 8 - anatine | 8 - alkylic | 8 -(10 rows) - --- add in some grouping expressions -SELECT author_id, sum(word_count) AS corpus_size FROM articles - WHERE author_id = 1 OR author_id = 2 OR author_id = 8 OR author_id = 10 - GROUP BY author_id - HAVING sum(word_count) > 40000 - ORDER BY sum(word_count) DESC; - author_id | corpus_size ---------------------------------------------------------------------- - 2 | 61782 - 10 | 59955 - 8 | 55410 -(3 rows) - --- UNION/INTERSECT queries are supported if on multiple shards -SELECT * FROM articles WHERE author_id = 10 UNION -SELECT * FROM articles WHERE author_id = 2 -ORDER BY 1,2,3; - id | author_id | title | word_count ---------------------------------------------------------------------- - 2 | 2 | abducing | 13642 - 10 | 10 | aggrandize | 17277 - 12 | 2 | archiblast | 18185 - 20 | 10 | absentness | 1820 - 22 | 2 | antipope | 2728 - 30 | 10 | andelee | 6363 - 32 | 2 | amazon | 11342 - 40 | 10 | attemper | 14976 - 42 | 2 | ausable | 15885 - 50 | 10 | anjanette | 19519 -(10 rows) - --- queries using CTEs are supported -WITH long_names AS ( SELECT id FROM authors WHERE char_length(name) > 15 ) -SELECT title FROM articles ORDER BY 1 LIMIT 5; - title ---------------------------------------------------------------------- - abducing - abeyance - abhorring - abington - ablation -(5 rows) - --- queries which involve functions in FROM clause are recursively planned -SELECT * FROM articles, position('om' in 'Thomas') ORDER BY 2 DESC, 1 DESC, 3 DESC LIMIT 5; - id | author_id | title | word_count | position ---------------------------------------------------------------------- - 50 | 10 | anjanette | 19519 | 3 - 40 | 10 | attemper | 14976 | 3 - 30 | 10 | andelee | 6363 | 3 - 20 | 10 | absentness | 1820 | 3 - 10 | 10 | aggrandize | 17277 | 3 -(5 rows) - --- subqueries are supported in WHERE clause in Citus even if the relations are not distributed -SELECT * FROM articles WHERE author_id IN (SELECT id FROM authors WHERE name LIKE '%a'); -ERROR: Complex subqueries and CTEs are not supported when task_executor_type is set to 'adaptive' --- subqueries are supported in FROM clause -SELECT articles.id,test.word_count -FROM articles, (SELECT id, word_count FROM articles) AS test WHERE test.id = articles.id -ORDER BY articles.id; -ERROR: Complex subqueries and CTEs are not supported when task_executor_type is set to 'adaptive' --- subqueries are not supported in SELECT clause -SELECT a.title AS name, (SELECT a2.id FROM articles_single_shard a2 WHERE a.id = a2.id LIMIT 1) - AS special_price FROM articles a; -ERROR: could not run distributed query with subquery outside the FROM, WHERE and HAVING clauses -HINT: Consider using an equality filter on the distributed table's partition column. --- joins are supported between local and distributed tables -SELECT title, authors.name FROM authors, articles WHERE authors.id = articles.author_id; -ERROR: Complex subqueries and CTEs are not supported when task_executor_type is set to 'task-tracker' --- inner joins are supported -SELECT * FROM (articles INNER JOIN authors ON articles.id = authors.id); -ERROR: Complex subqueries and CTEs are not supported when task_executor_type is set to 'task-tracker' --- test use of EXECUTE statements within plpgsql -DO $sharded_execute$ - BEGIN - EXECUTE 'SELECT COUNT(*) FROM articles ' || - 'WHERE author_id = $1 AND author_id = $2' USING 1, 2; - END -$sharded_execute$; --- test use of bare SQL within plpgsql -DO $sharded_sql$ - BEGIN - SELECT COUNT(*) FROM articles WHERE author_id = 1 AND author_id = 2; - END -$sharded_sql$; -ERROR: query has no destination for result data -HINT: If you want to discard the results of a SELECT, use PERFORM instead. -CONTEXT: PL/pgSQL function inline_code_block line 3 at SQL statement --- test cross-shard queries -SELECT COUNT(*) FROM articles; - count ---------------------------------------------------------------------- - 50 -(1 row) - --- test with empty target list -SELECT FROM articles; --- -(50 rows) - -SELECT FROM articles WHERE author_id = 3737; --- -(0 rows) - -SELECT FROM articles WHERE word_count = 65500; --- -(0 rows) - --- having queries supported in Citus -SELECT author_id, sum(word_count) AS corpus_size FROM articles - GROUP BY author_id - HAVING sum(word_count) > 25000 - ORDER BY sum(word_count) DESC - LIMIT 5; - author_id | corpus_size ---------------------------------------------------------------------- - 4 | 66325 - 2 | 61782 - 10 | 59955 - 8 | 55410 - 6 | 50867 -(5 rows) - -SELECT author_id FROM articles - GROUP BY author_id - HAVING sum(word_count) > 50000 - ORDER BY author_id; - author_id ---------------------------------------------------------------------- - 2 - 4 - 6 - 8 - 10 -(5 rows) - -SELECT author_id FROM articles - GROUP BY author_id - HAVING sum(word_count) > 50000 AND author_id < 5 - ORDER BY author_id; - author_id ---------------------------------------------------------------------- - 2 - 4 -(2 rows) - -SELECT author_id FROM articles - GROUP BY author_id - HAVING sum(word_count) > 50000 OR author_id < 5 - ORDER BY author_id; - author_id ---------------------------------------------------------------------- - 1 - 2 - 3 - 4 - 6 - 8 - 10 -(7 rows) - -SELECT author_id FROM articles - GROUP BY author_id - HAVING author_id <= 2 OR author_id = 8 - ORDER BY author_id; - author_id ---------------------------------------------------------------------- - 1 - 2 - 8 -(3 rows) - -SELECT o_orderstatus, count(*), avg(o_totalprice) FROM orders - GROUP BY o_orderstatus - HAVING count(*) > 1450 OR avg(o_totalprice) > 150000 - ORDER BY o_orderstatus; - o_orderstatus | count | avg ---------------------------------------------------------------------- - O | 1461 | 143326.447029431896 - P | 75 | 164847.914533333333 -(2 rows) - -SELECT o_orderstatus, sum(l_linenumber), avg(l_linenumber) FROM lineitem, orders - WHERE l_orderkey = o_orderkey AND l_orderkey > 9030 - GROUP BY o_orderstatus - HAVING sum(l_linenumber) > 1000 - ORDER BY o_orderstatus; - o_orderstatus | sum | avg ---------------------------------------------------------------------- - F | 8559 | 3.0126715945089757 - O | 8904 | 3.0040485829959514 -(2 rows) - --- now, test the cases where Citus do or do not need to create --- the master queries -SET client_min_messages TO 'DEBUG2'; --- start with the simple lookup query -SELECT * - FROM articles - WHERE author_id = 1; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- - 1 | 1 | arsenous | 9572 - 11 | 1 | alamo | 1347 - 21 | 1 | arcading | 5890 - 31 | 1 | athwartships | 7271 - 41 | 1 | aznavour | 11814 -(5 rows) - --- below query hits a single shard, so no need to create the master query -SELECT * - FROM articles - WHERE author_id = 1 OR author_id = 17; -DEBUG: Creating router plan -DEBUG: Plan is router executable - id | author_id | title | word_count ---------------------------------------------------------------------- - 1 | 1 | arsenous | 9572 - 11 | 1 | alamo | 1347 - 21 | 1 | arcading | 5890 - 31 | 1 | athwartships | 7271 - 41 | 1 | aznavour | 11814 -(5 rows) - --- below query hits two shards, so needs to create the master query -SELECT * - FROM articles - WHERE author_id = 1 OR author_id = 18; -DEBUG: Router planner cannot handle multi-shard select queries - id | author_id | title | word_count ---------------------------------------------------------------------- - 1 | 1 | arsenous | 9572 - 11 | 1 | alamo | 1347 - 21 | 1 | arcading | 5890 - 31 | 1 | athwartships | 7271 - 41 | 1 | aznavour | 11814 -(5 rows) - --- rename the output columns on a no master query case -SELECT id as article_id, word_count * id as random_value - FROM articles - WHERE author_id = 1; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - article_id | random_value ---------------------------------------------------------------------- - 1 | 9572 - 11 | 14817 - 21 | 123690 - 31 | 225401 - 41 | 484374 -(5 rows) - --- we can push down co-located joins to a single worker without the --- master query being required for only the same tables -SELECT a.author_id as first_author, b.word_count as second_word_count - FROM articles a, articles b - WHERE a.author_id = 10 and a.author_id = b.author_id - LIMIT 3; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 10 - first_author | second_word_count ---------------------------------------------------------------------- - 10 | 17277 - 10 | 1820 - 10 | 6363 -(3 rows) - --- now show that JOINs with multiple tables are not router executable --- they are executed by real-time executor -SELECT a.author_id as first_author, b.word_count as second_word_count - FROM articles a, articles_single_shard b - WHERE a.author_id = 10 and a.author_id = b.author_id - LIMIT 3; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 10 - first_author | second_word_count ---------------------------------------------------------------------- - 10 | 19519 - 10 | 19519 - 10 | 19519 -(3 rows) - --- do not create the master query for LIMIT on a single shard SELECT -SELECT * - FROM articles - WHERE author_id = 1 - LIMIT 2; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- - 1 | 1 | arsenous | 9572 - 11 | 1 | alamo | 1347 -(2 rows) - --- This query hits a single shard. So GROUP BY can be --- pushed down to the workers directly. This query is --- equivalent to SELECT DISTINCT on a single shard. -SELECT id - FROM articles - WHERE author_id = 1 - GROUP BY id - ORDER BY id; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id ---------------------------------------------------------------------- - 1 - 11 - 21 - 31 - 41 -(5 rows) - --- copying from a single shard table does not require the master query -COPY articles_single_shard TO stdout; -50 10 anjanette 19519 -SELECT avg(word_count) - FROM articles - WHERE author_id = 2; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 2 - avg ---------------------------------------------------------------------- - 12356.400000000000 -(1 row) - --- error out on unsupported aggregate -SET client_min_messages to 'NOTICE'; -CREATE AGGREGATE public.invalid(int) ( - sfunc = int4pl, - stype = int -); -SELECT invalid(word_count) FROM articles; -ERROR: unsupported aggregate function invalid -DROP AGGREGATE invalid(int); -SET client_min_messages to 'DEBUG2'; --- max, min, sum, count is somehow implemented --- differently in distributed planning -SELECT max(word_count) as max, min(word_count) as min, - sum(word_count) as sum, count(word_count) as cnt - FROM articles - WHERE author_id = 2; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 2 - max | min | sum | cnt ---------------------------------------------------------------------- - 18185 | 2728 | 61782 | 5 -(1 row) - --- error out for queries with repartition jobs -SELECT * - FROM articles a, articles b - WHERE a.id = b.id AND a.author_id = 1; -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: join prunable for task partitionId 0 and 1 -DEBUG: join prunable for task partitionId 0 and 2 -DEBUG: join prunable for task partitionId 0 and 3 -DEBUG: join prunable for task partitionId 1 and 0 -DEBUG: join prunable for task partitionId 1 and 2 -DEBUG: join prunable for task partitionId 1 and 3 -DEBUG: join prunable for task partitionId 2 and 0 -DEBUG: join prunable for task partitionId 2 and 1 -DEBUG: join prunable for task partitionId 2 and 3 -DEBUG: join prunable for task partitionId 3 and 0 -DEBUG: join prunable for task partitionId 3 and 1 -DEBUG: join prunable for task partitionId 3 and 2 -DEBUG: pruning merge fetch taskId 1 -DETAIL: Creating dependency on merge taskId 2 -DEBUG: pruning merge fetch taskId 2 -DETAIL: Creating dependency on merge taskId 3 -DEBUG: pruning merge fetch taskId 4 -DETAIL: Creating dependency on merge taskId 4 -DEBUG: pruning merge fetch taskId 5 -DETAIL: Creating dependency on merge taskId 6 -DEBUG: pruning merge fetch taskId 7 -DETAIL: Creating dependency on merge taskId 6 -DEBUG: pruning merge fetch taskId 8 -DETAIL: Creating dependency on merge taskId 9 -DEBUG: pruning merge fetch taskId 10 -DETAIL: Creating dependency on merge taskId 8 -DEBUG: pruning merge fetch taskId 11 -DETAIL: Creating dependency on merge taskId 12 -ERROR: the query contains a join that requires repartitioning -HINT: Set citus.enable_repartition_joins to on to enable repartitioning --- system columns from shard tables can be queried and retrieved -SELECT count(*) FROM ( - SELECT tableoid, ctid, cmin, cmax, xmin, xmax - FROM articles - WHERE tableoid IS NOT NULL OR - ctid IS NOT NULL OR - cmin IS NOT NULL OR - cmax IS NOT NULL OR - xmin IS NOT NULL OR - xmax IS NOT NULL -) x; -DEBUG: Router planner cannot handle multi-shard select queries - count ---------------------------------------------------------------------- - 50 -(1 row) - --- tablesample is supported -SELECT * FROM articles TABLESAMPLE SYSTEM (0) WHERE author_id = 1; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- -(0 rows) - -SELECT * FROM articles TABLESAMPLE BERNOULLI (0) WHERE author_id = 1; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- -(0 rows) - -SELECT * FROM articles TABLESAMPLE SYSTEM (100) WHERE author_id = 1 ORDER BY id; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- - 1 | 1 | arsenous | 9572 - 11 | 1 | alamo | 1347 - 21 | 1 | arcading | 5890 - 31 | 1 | athwartships | 7271 - 41 | 1 | aznavour | 11814 -(5 rows) - -SELECT * FROM articles TABLESAMPLE BERNOULLI (100) WHERE author_id = 1 ORDER BY id; -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- - 1 | 1 | arsenous | 9572 - 11 | 1 | alamo | 1347 - 21 | 1 | arcading | 5890 - 31 | 1 | athwartships | 7271 - 41 | 1 | aznavour | 11814 -(5 rows) - --- test tablesample with fast path as well -SET citus.enable_fast_path_router_planner TO true; -SELECT * FROM articles TABLESAMPLE SYSTEM (0) WHERE author_id = 1; -DEBUG: Distributed planning for a fast-path router query -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- -(0 rows) - -SELECT * FROM articles TABLESAMPLE BERNOULLI (0) WHERE author_id = 1; -DEBUG: Distributed planning for a fast-path router query -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- -(0 rows) - -SELECT * FROM articles TABLESAMPLE SYSTEM (100) WHERE author_id = 1 ORDER BY id; -DEBUG: Distributed planning for a fast-path router query -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- - 1 | 1 | arsenous | 9572 - 11 | 1 | alamo | 1347 - 21 | 1 | arcading | 5890 - 31 | 1 | athwartships | 7271 - 41 | 1 | aznavour | 11814 -(5 rows) - -SELECT * FROM articles TABLESAMPLE BERNOULLI (100) WHERE author_id = 1 ORDER BY id; -DEBUG: Distributed planning for a fast-path router query -DEBUG: Creating router plan -DEBUG: Plan is router executable -DETAIL: distribution column value: 1 - id | author_id | title | word_count ---------------------------------------------------------------------- - 1 | 1 | arsenous | 9572 - 11 | 1 | alamo | 1347 - 21 | 1 | arcading | 5890 - 31 | 1 | athwartships | 7271 - 41 | 1 | aznavour | 11814 -(5 rows) - -SET client_min_messages to 'NOTICE'; --- we should be able to use nextval in the target list -CREATE SEQUENCE query_seq; -SELECT nextval('query_seq') FROM articles WHERE author_id = 1; - nextval ---------------------------------------------------------------------- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -SELECT nextval('query_seq') FROM articles LIMIT 3; - nextval ---------------------------------------------------------------------- - 6 - 7 - 8 -(3 rows) - -SELECT nextval('query_seq')*2 FROM articles LIMIT 3; - ?column? ---------------------------------------------------------------------- - 18 - 20 - 22 -(3 rows) - -SELECT * FROM (SELECT nextval('query_seq') FROM articles LIMIT 3) vals; - nextval ---------------------------------------------------------------------- - 12 - 13 - 14 -(3 rows) - --- but not elsewhere -SELECT sum(nextval('query_seq')) FROM articles; -ERROR: relation "public.query_seq" does not exist -CONTEXT: while executing command on localhost:xxxxx -SELECT n FROM (SELECT nextval('query_seq') n, random() FROM articles) vals; -ERROR: relation "public.query_seq" does not exist -CONTEXT: while executing command on localhost:xxxxx -DROP SEQUENCE query_seq; diff --git a/src/test/regress/expected/multi_subquery_window_functions.out b/src/test/regress/expected/multi_subquery_window_functions.out index eab938e6c..97a481df0 100644 --- a/src/test/regress/expected/multi_subquery_window_functions.out +++ b/src/test/regress/expected/multi_subquery_window_functions.out @@ -712,68 +712,68 @@ EXPLAIN (COSTS FALSE, VERBOSE TRUE) QUERY PLAN --------------------------------------------------------------------- Limit - Output: xxxxxx + Output: remote_scan.user_id, remote_scan.sum -> Sort - Output: xxxxxx + Output: remote_scan.user_id, remote_scan.sum Sort Key: remote_scan.sum DESC, remote_scan.user_id DESC -> Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan.user_id, remote_scan.sum Task Count: 4 Tasks Shown: One of 4 -> Task Query: SELECT worker_column_1 AS user_id, worker_column_2 AS sum FROM (SELECT ftop.user_id AS worker_column_1, ftop.sum AS worker_column_2 FROM (SELECT user_id_1.user_id, sum(user_id_1.counter) AS sum FROM (SELECT users_table.user_id, sum(users_table.value_2) OVER (PARTITION BY users_table.user_id) AS counter FROM public.users_table_1400256 users_table UNION SELECT events_table.user_id, sum(events_table.value_2) OVER (PARTITION BY events_table.user_id) AS counter FROM public.events_table_1400260 events_table) user_id_1 GROUP BY user_id_1.user_id UNION SELECT user_id_2.user_id, sum(user_id_2.counter) AS sum FROM (SELECT users_table.user_id, sum(users_table.value_2) OVER (PARTITION BY users_table.user_id) AS counter FROM public.users_table_1400256 users_table UNION SELECT events_table.user_id, sum(events_table.value_2) OVER (PARTITION BY events_table.user_id) AS counter FROM public.events_table_1400260 events_table) user_id_2 GROUP BY user_id_2.user_id) ftop) worker_subquery ORDER BY worker_column_2 DESC, worker_column_1 DESC LIMIT '5'::bigint Node: host=localhost port=xxxxx dbname=regression -> Limit - Output: xxxxxx + Output: users_table.user_id, (sum((sum(users_table.value_2) OVER (?)))) -> Sort - Output: xxxxxx + Output: users_table.user_id, (sum((sum(users_table.value_2) OVER (?)))) Sort Key: (sum((sum(users_table.value_2) OVER (?)))) DESC, users_table.user_id DESC -> HashAggregate - Output: xxxxxx + Output: users_table.user_id, (sum((sum(users_table.value_2) OVER (?)))) Group Key: users_table.user_id, (sum((sum(users_table.value_2) OVER (?)))) -> Append -> HashAggregate - Output: xxxxxx + Output: users_table.user_id, sum((sum(users_table.value_2) OVER (?))) Group Key: users_table.user_id -> HashAggregate - Output: xxxxxx + Output: users_table.user_id, (sum(users_table.value_2) OVER (?)) Group Key: users_table.user_id, (sum(users_table.value_2) OVER (?)) -> Append -> WindowAgg - Output: xxxxxx + Output: users_table.user_id, sum(users_table.value_2) OVER (?) -> Sort - Output: xxxxxx + Output: users_table.user_id, users_table.value_2 Sort Key: users_table.user_id -> Seq Scan on public.users_table_1400256 users_table - Output: xxxxxx + Output: users_table.user_id, users_table.value_2 -> WindowAgg - Output: xxxxxx + Output: events_table.user_id, sum(events_table.value_2) OVER (?) -> Sort - Output: xxxxxx + Output: events_table.user_id, events_table.value_2 Sort Key: events_table.user_id -> Seq Scan on public.events_table_1400260 events_table - Output: xxxxxx + Output: events_table.user_id, events_table.value_2 -> HashAggregate - Output: xxxxxx + Output: users_table_1.user_id, sum((sum(users_table_1.value_2) OVER (?))) Group Key: users_table_1.user_id -> HashAggregate - Output: xxxxxx + Output: users_table_1.user_id, (sum(users_table_1.value_2) OVER (?)) Group Key: users_table_1.user_id, (sum(users_table_1.value_2) OVER (?)) -> Append -> WindowAgg - Output: xxxxxx + Output: users_table_1.user_id, sum(users_table_1.value_2) OVER (?) -> Sort - Output: xxxxxx + Output: users_table_1.user_id, users_table_1.value_2 Sort Key: users_table_1.user_id -> Seq Scan on public.users_table_1400256 users_table_1 - Output: xxxxxx + Output: users_table_1.user_id, users_table_1.value_2 -> WindowAgg - Output: xxxxxx + Output: events_table_1.user_id, sum(events_table_1.value_2) OVER (?) -> Sort - Output: xxxxxx + Output: events_table_1.user_id, events_table_1.value_2 Sort Key: events_table_1.user_id -> Seq Scan on public.events_table_1400260 events_table_1 - Output: xxxxxx + Output: events_table_1.user_id, events_table_1.value_2 (63 rows) -- test with window functions which aren't pushed down diff --git a/src/test/regress/expected/multi_task_assignment_policy.out b/src/test/regress/expected/multi_task_assignment_policy.out index 4c52df355..93e2591f1 100644 --- a/src/test/regress/expected/multi_task_assignment_policy.out +++ b/src/test/regress/expected/multi_task_assignment_policy.out @@ -311,15 +311,13 @@ SELECT create_distributed_table('task_assignment_test_table_2', 'test_id'); SET citus.task_assignment_policy TO 'round-robin'; -- Run the query two times to make sure that it hits two different workers -- on consecutive runs --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; INSERT INTO explain_outputs SELECT parse_explain_output($cmd$ -EXPLAIN WITH q1 AS (SELECT * FROM task_assignment_test_table_2) SELECT * FROM q1 +EXPLAIN WITH q1 AS MATERIALIZED (SELECT * FROM task_assignment_test_table_2) SELECT * FROM q1 $cmd$, 'task_assignment_test_table_2'); INSERT INTO explain_outputs SELECT parse_explain_output($cmd$ -EXPLAIN WITH q1 AS (SELECT * FROM task_assignment_test_table_2) SELECT * FROM q1 +EXPLAIN WITH q1 AS MATERIALIZED (SELECT * FROM task_assignment_test_table_2) SELECT * FROM q1 $cmd$, 'task_assignment_test_table_2'); -- The count should be 2 since the intermediate results are processed on -- different workers diff --git a/src/test/regress/expected/non_colocated_subquery_joins.out b/src/test/regress/expected/non_colocated_subquery_joins.out index 4904dd1f0..f03091f9a 100644 --- a/src/test/regress/expected/non_colocated_subquery_joins.out +++ b/src/test/regress/expected/non_colocated_subquery_joins.out @@ -551,10 +551,8 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c (1 row) -- subquery joins should work fine when used with CTEs --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; SELECT true AS valid FROM explain_json_2($$ - WITH q1 AS (SELECT user_id FROM users_table) + WITH q1 AS MATERIALIZED (SELECT user_id FROM users_table) SELECT count(*) FROM q1, (SELECT users_table.user_id, random() FROM @@ -568,7 +566,6 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c t (1 row) -SET citus.enable_cte_inlining TO true; -- should work fine within UNIONs SELECT true AS valid FROM explain_json_2($$ (SELECT users_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.value_2 AND event_type IN (1,2,3,4)) UNION diff --git a/src/test/regress/expected/pg12_1.out b/src/test/regress/expected/pg12_1.out deleted file mode 100644 index 9d92533f9..000000000 --- a/src/test/regress/expected/pg12_1.out +++ /dev/null @@ -1,6 +0,0 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 11 AS server_version_above_eleven -\gset -\if :server_version_above_eleven -\else -\q diff --git a/src/test/regress/expected/set_operation_and_local_tables.out b/src/test/regress/expected/set_operation_and_local_tables.out index 2d1b1989a..fa38331e7 100644 --- a/src/test/regress/expected/set_operation_and_local_tables.out +++ b/src/test/regress/expected/set_operation_and_local_tables.out @@ -207,19 +207,17 @@ DEBUG: Router planner cannot handle multi-shard select queries (2 rows) -- same query with subquery in where is wrapped in CTE -SET citus.enable_cte_inlining TO off; SELECT * FROM test a WHERE x IN (WITH cte AS (SELECT x FROM test b UNION SELECT y FROM test c UNION SELECT y FROM local_test d) SELECT * FROM cte) ORDER BY 1,2; -DEBUG: Local tables cannot be used in distributed queries. -DEBUG: generating subplan XXX_1 for CTE cte: SELECT b.x FROM recursive_set_local.test b UNION SELECT c.y FROM recursive_set_local.test c UNION SELECT d.y FROM recursive_set_local.local_test d +DEBUG: CTE cte is going to be inlined via distributed planning DEBUG: Local tables cannot be used in distributed queries. DEBUG: generating subplan XXX_1 for subquery SELECT y FROM recursive_set_local.local_test d DEBUG: Router planner cannot handle multi-shard select queries DEBUG: generating subplan XXX_2 for subquery SELECT x FROM recursive_set_local.test b DEBUG: Router planner cannot handle multi-shard select queries DEBUG: generating subplan XXX_3 for subquery SELECT y FROM recursive_set_local.test c -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.x FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer) UNION SELECT intermediate_result.y FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(y integer) UNION SELECT intermediate_result.y FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(y integer) DEBUG: Creating router plan -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT x, y FROM recursive_set_local.test a WHERE (x OPERATOR(pg_catalog.=) ANY (SELECT cte.x FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) cte)) ORDER BY x, y +DEBUG: generating subplan XXX_4 for subquery SELECT intermediate_result.x FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer) UNION SELECT intermediate_result.y FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(y integer) UNION SELECT intermediate_result.y FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(y integer) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT x, y FROM recursive_set_local.test a WHERE (x OPERATOR(pg_catalog.=) ANY (SELECT cte.x FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) cte)) ORDER BY x, y DEBUG: Router planner cannot handle multi-shard select queries x | y --------------------------------------------------------------------- @@ -227,7 +225,6 @@ DEBUG: Router planner cannot handle multi-shard select queries 2 | 2 (2 rows) -RESET citus.enable_cte_inlining; -- supported since final step only has local table and intermediate result SELECT * FROM ((SELECT * FROM test) EXCEPT (SELECT * FROM test ORDER BY x LIMIT 1)) u JOIN local_test USING (x) ORDER BY 1,2; DEBUG: Local tables cannot be used in distributed queries. diff --git a/src/test/regress/expected/single_node.out b/src/test/regress/expected/single_node.out index 4c9dd7589..f919e8779 100644 --- a/src/test/regress/expected/single_node.out +++ b/src/test/regress/expected/single_node.out @@ -488,11 +488,7 @@ COMMIT; CREATE INDEX single_node_i1 ON test(x); CREATE INDEX single_node_i2 ON test(x,y); REINDEX SCHEMA single_node; --- PG 11 does not support CONCURRENTLY --- and we do not want to add a new output --- file just for that. Enable the test --- once we remove PG_VERSION_11 ---REINDEX SCHEMA CONCURRENTLY single_node; +REINDEX SCHEMA CONCURRENTLY single_node; -- keep one of the indexes -- drop w/wout tx blocks BEGIN; @@ -805,7 +801,6 @@ SELECT * FROM test_2 WHERE z = (83, 'citus8.3')::new_type OR z = (82, 'citus8.2' -- final query is only intermediate result -- we want PG 11/12/13 behave consistently, the CTEs should be MATERIALIZED -SET citus.enable_cte_inlining TO FALSE; WITH cte_1 AS (SELECT * FROM test_2) SELECT * FROM cte_1 ORDER BY 1,2; x | y | z --------------------------------------------------------------------- diff --git a/src/test/regress/expected/subquery_and_cte.out b/src/test/regress/expected/subquery_and_cte.out index f2352bb0b..f175b6940 100644 --- a/src/test/regress/expected/subquery_and_cte.out +++ b/src/test/regress/expected/subquery_and_cte.out @@ -2,8 +2,6 @@ -- test recursive planning functionality with subqueries and CTEs -- =================================================================== SET search_path TO subquery_and_ctes; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; CREATE TABLE users_table_local AS SELECT * FROM users_table; CREATE TABLE dist_table (id int, value int); SELECT create_distributed_table('dist_table', 'id', colocate_with => 'users_table'); @@ -19,11 +17,11 @@ $$ LANGUAGE SQL; SET client_min_messages TO DEBUG1; -- CTEs are recursively planned, and subquery foo is also recursively planned -- final plan becomes a router plan -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -42,7 +40,7 @@ FROM ORDER BY 1 DESC LIMIT 5 ) as foo WHERE foo.user_id = cte.user_id; -DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS MATERIALIZED (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM subquery_and_ctes.users_table_local DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM subquery_and_ctes.events_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) @@ -64,6 +62,8 @@ SELECT cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type FROM cte1, cte2 ORDER BY cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type LIMIT 5; +DEBUG: CTE cte1 is going to be inlined via distributed planning +DEBUG: CTE cte2 is going to be inlined via distributed planning user_id | value_1 | user_id | event_type --------------------------------------------------------------------- 1 | 1 | 1 | 0 @@ -74,9 +74,9 @@ LIMIT 5; (5 rows) -- CTEs aren't colocated, CTEs become intermediate results -WITH cte1 AS ( +WITH cte1 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 -), cte2 AS ( +), cte2 AS MATERIALIZED ( SELECT * FROM events_table WHERE user_id = 6 ) SELECT cte1.user_id, cte1.value_1, cte2.user_id, cte2.user_id @@ -110,9 +110,9 @@ WITH cte1 AS ( UPDATE dist_table dt SET value = cte1.value_1 + cte2.event_type FROM cte1, cte2 WHERE cte1.user_id = dt.id AND dt.id = 1; -- all relations are not colocated, CTEs become intermediate results -WITH cte1 AS ( +WITH cte1 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 -), cte2 AS ( +), cte2 AS MATERIALIZED ( SELECT * FROM events_table WHERE user_id = 6 ) UPDATE dist_table dt SET value = cte1.value_1 + cte2.event_type @@ -121,7 +121,7 @@ DEBUG: generating subplan XXX_1 for CTE cte1: SELECT user_id, "time", value_1, DEBUG: generating subplan XXX_2 for CTE cte2: SELECT user_id, "time", event_type, value_2, value_3, value_4 FROM subquery_and_ctes.events_table WHERE (user_id OPERATOR(pg_catalog.=) 6) DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE subquery_and_ctes.dist_table dt SET value = (cte1.value_1 OPERATOR(pg_catalog.+) cte2.event_type) FROM (SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) cte1, (SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.event_type, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, event_type integer, value_2 integer, value_3 double precision, value_4 bigint)) cte2 WHERE ((cte1.user_id OPERATOR(pg_catalog.=) dt.id) AND (dt.id OPERATOR(pg_catalog.=) 1)) -- volatile function calls should not be routed -WITH cte1 AS (SELECT id, value FROM func()) +WITH cte1 AS MATERIALIZED (SELECT id, value FROM func()) UPDATE dist_table dt SET value = cte1.value FROM cte1 WHERE dt.id = 1; DEBUG: generating subplan XXX_1 for CTE cte1: SELECT id, value FROM subquery_and_ctes.func() func(id, value) @@ -129,11 +129,11 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE subquery_and_ -- CTEs are recursively planned, and subquery foo is also recursively planned -- final plan becomes a real-time plan since we also have events_table in the -- range table entries -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -152,7 +152,7 @@ FROM ORDER BY 1 DESC LIMIT 5 ) as foo, events_table WHERE foo.user_id = cte.user_id AND events_table.user_id = cte.user_id; -DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS MATERIALIZED (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM subquery_and_ctes.users_table_local DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM subquery_and_ctes.events_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) @@ -167,11 +167,11 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c -- CTEs are replaced and subquery in WHERE is also replaced -- but the query is still real-time query since users_table is in the -- range table list -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -182,7 +182,7 @@ WHERE users_table.user_id = cte.user_id AND users_table.user_id IN (SELECT DISTINCT value_2 FROM users_table WHERE value_1 >= 1 AND value_1 <= 20 ORDER BY 1 LIMIT 5) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS MATERIALIZED (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM subquery_and_ctes.users_table_local DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM subquery_and_ctes.events_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) @@ -199,11 +199,11 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT cte. -- subquery in WHERE clause is planned recursively due to the recurring table -- in FROM clause -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -213,7 +213,7 @@ FROM cte WHERE cte.user_id IN (SELECT DISTINCT user_id FROM users_table WHERE value_1 >= 1 AND value_1 <= 20) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS MATERIALIZED (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM subquery_and_ctes.users_table_local DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM subquery_and_ctes.events_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) @@ -235,7 +235,7 @@ SELECT user_id FROM ( - WITH cte AS ( + WITH cte AS MATERIALIZED ( SELECT DISTINCT users_table.user_id FROM @@ -264,7 +264,7 @@ SELECT bar.user_id FROM ( - WITH cte AS ( + WITH cte AS MATERIALIZED ( SELECT DISTINCT users_table.user_id FROM @@ -303,7 +303,7 @@ SELECT DISTINCT bar.user_id FROM ( - WITH cte AS ( + WITH cte AS MATERIALIZED ( SELECT DISTINCT users_table.user_id FROM @@ -319,7 +319,7 @@ FROM FROM users_table, ( - WITH cte AS ( + WITH cte AS MATERIALIZED ( SELECT event_type, users_table.user_id FROM @@ -352,11 +352,11 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT bar. -- DISTINCT on a non-partition key SELECT * FROM ( - WITH cte AS ( - WITH local_cte AS ( + WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -374,12 +374,13 @@ SELECT * FROM foo.user_id = events_table.value_2 ORDER BY 3 DESC, 2 DESC, 1 DESC LIMIT 5; -DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: CTE cte_in_where is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS MATERIALIZED (SELECT events_table.user_id FROM subquery_and_ctes.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM subquery_and_ctes.users_table_local DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM subquery_and_ctes.events_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) -DEBUG: generating subplan XXX_2 for CTE cte_in_where: SELECT DISTINCT value_2 FROM subquery_and_ctes.users_table WHERE ((value_1 OPERATOR(pg_catalog.>=) 1) AND (value_1 OPERATOR(pg_catalog.<=) 20)) ORDER BY value_2 LIMIT 5 DEBUG: push down of limit count: 5 +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT value_2 FROM subquery_and_ctes.users_table WHERE ((value_1 OPERATOR(pg_catalog.>=) 1) AND (value_1 OPERATOR(pg_catalog.<=) 20)) ORDER BY value_2 LIMIT 5 DEBUG: generating subplan XXX_3 for subquery SELECT DISTINCT cte.user_id FROM subquery_and_ctes.users_table, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte WHERE ((users_table.user_id OPERATOR(pg_catalog.=) cte.user_id) AND (users_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT cte_in_where.value_2 FROM (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer)) cte_in_where))) ORDER BY cte.user_id DESC DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT foo.user_id, events_table.user_id, events_table."time", events_table.event_type, events_table.value_2, events_table.value_3, events_table.value_4 FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, subquery_and_ctes.events_table WHERE (foo.user_id OPERATOR(pg_catalog.=) events_table.value_2) ORDER BY events_table."time" DESC, events_table.user_id DESC, foo.user_id DESC LIMIT 5 DEBUG: push down of limit count: 5 @@ -393,11 +394,11 @@ DEBUG: push down of limit count: 5 (5 rows) -- now recursively plan subqueries inside the CTEs that contains LIMIT and OFFSET -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM @@ -423,7 +424,7 @@ FROM ORDER BY 1 DESC LIMIT 5 ) as foo WHERE foo.user_id = cte.user_id; -DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS (SELECT events_table.user_id FROM subquery_and_ctes.events_table, (SELECT DISTINCT users_table.value_2 FROM subquery_and_ctes.users_table OFFSET 0) foo WHERE ((events_table.user_id OPERATOR(pg_catalog.=) foo.value_2) AND (events_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT users_table.value_1 FROM subquery_and_ctes.users_table ORDER BY users_table.value_1 LIMIT 3)))) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS MATERIALIZED (SELECT events_table.user_id FROM subquery_and_ctes.events_table, (SELECT DISTINCT users_table.value_2 FROM subquery_and_ctes.users_table OFFSET 0) foo WHERE ((events_table.user_id OPERATOR(pg_catalog.=) foo.value_2) AND (events_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT users_table.value_1 FROM subquery_and_ctes.users_table ORDER BY users_table.value_1 LIMIT 3)))) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM subquery_and_ctes.users_table_local DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT events_table.user_id FROM subquery_and_ctes.events_table, (SELECT DISTINCT users_table.value_2 FROM subquery_and_ctes.users_table OFFSET 0) foo WHERE ((events_table.user_id OPERATOR(pg_catalog.=) foo.value_2) AND (events_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT users_table.value_1 FROM subquery_and_ctes.users_table ORDER BY users_table.value_1 LIMIT 3))) DEBUG: push down of limit count: 3 @@ -444,11 +445,11 @@ SELECT * FROM ( - WITH cte AS ( - WITH local_cte AS ( + WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM @@ -477,7 +478,7 @@ FROM ) as foo, users_table WHERE foo.cnt > users_table.value_2 ORDER BY 3 DESC, 1 DESC, 2 DESC, 4 DESC LIMIT 5; -DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS (SELECT events_table.user_id FROM subquery_and_ctes.events_table, (SELECT DISTINCT users_table.value_2 FROM subquery_and_ctes.users_table OFFSET 0) foo WHERE ((events_table.user_id OPERATOR(pg_catalog.=) foo.value_2) AND (events_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT users_table.value_1 FROM subquery_and_ctes.users_table ORDER BY users_table.value_1 LIMIT 3)))) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_and_ctes.users_table_local), dist_cte AS MATERIALIZED (SELECT events_table.user_id FROM subquery_and_ctes.events_table, (SELECT DISTINCT users_table.value_2 FROM subquery_and_ctes.users_table OFFSET 0) foo WHERE ((events_table.user_id OPERATOR(pg_catalog.=) foo.value_2) AND (events_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT users_table.value_1 FROM subquery_and_ctes.users_table ORDER BY users_table.value_1 LIMIT 3)))) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM subquery_and_ctes.users_table_local DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT events_table.user_id FROM subquery_and_ctes.events_table, (SELECT DISTINCT users_table.value_2 FROM subquery_and_ctes.users_table OFFSET 0) foo WHERE ((events_table.user_id OPERATOR(pg_catalog.=) foo.value_2) AND (events_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT users_table.value_1 FROM subquery_and_ctes.users_table ORDER BY users_table.value_1 LIMIT 3))) DEBUG: push down of limit count: 3 @@ -504,7 +505,7 @@ SELECT bar.user_id FROM ( - WITH RECURSIVE cte AS ( + WITH RECURSIVE cte AS MATERIALIZED ( SELECT DISTINCT users_table.user_id FROM @@ -570,7 +571,7 @@ SELECT count(*) FROM (SELECT DISTINCT ref_table_1.a + 1 +ref_table_2.a + ref_table_1.a as a FROM ref_table_1 JOIN ref_table_2 ON (ref_table_1.a = ref_table_2.a)) as foo JOIN dist - ON(dist.a = foo.a); + ON(dist.a = foo.a); count --------------------------------------------------------------------- 3 @@ -590,7 +591,7 @@ SELECT count(*) FROM (SELECT ref_table_1.a + ref_table_2.a + 1 as a FROM ref_table_1 JOIN ref_table_2 ON (ref_table_1.a = ref_table_2.a) GROUP BY ref_table_1.a + ref_table_2.a + 1) as foo JOIN dist - ON(dist.a = foo.a); + ON(dist.a = foo.a); count --------------------------------------------------------------------- 4 @@ -609,7 +610,6 @@ SELECT count(*) FROM ( 10 (1 row) -SET citus.enable_cte_inlining to true; WITH foo AS ( SELECT DISTINCT ref_table_1.a + 1 as a FROM ref_table_1 JOIN ref_table_2 ON (ref_table_1.a = ref_table_2.a) ) @@ -631,7 +631,7 @@ SELECT count(*) FROM foo JOIN dist - ON(dist.a = foo.a); + ON(dist.a = foo.a); DEBUG: CTE foo is going to be inlined via distributed planning count --------------------------------------------------------------------- @@ -659,7 +659,7 @@ SELECT count(*) FROM foo JOIN dist - ON(dist.a = foo.a); + ON(dist.a = foo.a); DEBUG: CTE foo is going to be inlined via distributed planning count --------------------------------------------------------------------- @@ -679,7 +679,6 @@ DEBUG: CTE foo is going to be inlined via distributed planning 10 (1 row) -SET citus.enable_cte_inlining to false; -- We error-out when there's an error in execution of the query. By repeating it -- multiple times, we increase the chance of this test failing before PR #1903. SET client_min_messages TO ERROR; diff --git a/src/test/regress/expected/subquery_complex_target_list.out b/src/test/regress/expected/subquery_complex_target_list.out index 5d44ae5c5..d53434a76 100644 --- a/src/test/regress/expected/subquery_complex_target_list.out +++ b/src/test/regress/expected/subquery_complex_target_list.out @@ -554,10 +554,10 @@ $$); coordinator_plan --------------------------------------------------------------------- Sort - Output: xxxxxx + Output: remote_scan.k1, remote_scan.k2, remote_scan.k3, remote_scan.v1, remote_scan.v2, remote_scan.v3, remote_scan.va1, remote_scan.va2, remote_scan.va3, remote_scan.count Sort Key: remote_scan.k1 -> Custom Scan (Citus Adaptive) - Output: xxxxxx + Output: remote_scan.k1, remote_scan.k2, remote_scan.k3, remote_scan.v1, remote_scan.v2, remote_scan.v3, remote_scan.va1, remote_scan.va2, remote_scan.va3, remote_scan.count Task Count: 4 (6 rows) diff --git a/src/test/regress/expected/subquery_in_where.out b/src/test/regress/expected/subquery_in_where.out index ac98d85f6..c5aedd31d 100644 --- a/src/test/regress/expected/subquery_in_where.out +++ b/src/test/regress/expected/subquery_in_where.out @@ -5,10 +5,8 @@ CREATE SCHEMA subquery_in_where; SET search_path TO subquery_in_where, public; SET client_min_messages TO DEBUG1; --CTEs can be used as a recurring tuple with subqueries in WHERE --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; WITH event_id - AS (SELECT user_id AS events_user_id, + AS MATERIALIZED (SELECT user_id AS events_user_id, time AS events_time, event_type FROM events_table) @@ -24,10 +22,9 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c 101 (1 row) -SET citus.enable_cte_inlining TO true; --Correlated subqueries can not be used in WHERE clause WITH event_id - AS (SELECT user_id AS events_user_id, + AS(SELECT user_id AS events_user_id, time AS events_time, event_type FROM events_table) diff --git a/src/test/regress/expected/subquery_view.out b/src/test/regress/expected/subquery_view.out index fef074292..5f50e6e6d 100644 --- a/src/test/regress/expected/subquery_view.out +++ b/src/test/regress/expected/subquery_view.out @@ -417,11 +417,11 @@ DEBUG: push down of limit count: 5 CREATE VIEW subquery_and_ctes_second AS SELECT time, event_type, value_2, value_3 FROM ( - WITH cte AS ( - WITH local_cte AS ( + WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -437,17 +437,16 @@ SELECT time, event_type, value_2, value_3 FROM events_table WHERE foo.user_id = events_table.value_2; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; SELECT * FROM subquery_and_ctes_second ORDER BY 3 DESC, 2 DESC, 1 DESC LIMIT 5; -DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_view.users_table_local), dist_cte AS (SELECT events_table.user_id FROM public.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: CTE cte_in_where is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM subquery_view.users_table_local), dist_cte AS MATERIALIZED (SELECT events_table.user_id FROM public.events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM subquery_view.users_table_local DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM public.events_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) -DEBUG: generating subplan XXX_2 for CTE cte_in_where: SELECT DISTINCT value_2 FROM public.users_table WHERE ((value_1 OPERATOR(pg_catalog.>=) 1) AND (value_1 OPERATOR(pg_catalog.<=) 20)) ORDER BY value_2 LIMIT 5 DEBUG: push down of limit count: 5 +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT value_2 FROM public.users_table WHERE ((value_1 OPERATOR(pg_catalog.>=) 1) AND (value_1 OPERATOR(pg_catalog.<=) 20)) ORDER BY value_2 LIMIT 5 DEBUG: generating subplan XXX_3 for subquery SELECT DISTINCT cte.user_id FROM public.users_table, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte WHERE ((users_table.user_id OPERATOR(pg_catalog.=) cte.user_id) AND (users_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT cte_in_where.value_2 FROM (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer)) cte_in_where))) ORDER BY cte.user_id DESC DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT "time", event_type, value_2, value_3 FROM (SELECT events_table."time", events_table.event_type, events_table.value_2, events_table.value_3 FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, public.events_table WHERE (foo.user_id OPERATOR(pg_catalog.=) events_table.value_2)) subquery_and_ctes_second ORDER BY value_2 DESC, event_type DESC, "time" DESC LIMIT 5 DEBUG: push down of limit count: 5 @@ -460,7 +459,6 @@ DEBUG: push down of limit count: 5 Thu Nov 23 09:33:16.992454 2017 | 3 | 4 | 1 (5 rows) -SET citus.enable_cte_inlining TO true; CREATE VIEW deep_subquery AS SELECT count(*) FROM diff --git a/src/test/regress/expected/tableam_0.out b/src/test/regress/expected/tableam_0.out deleted file mode 100644 index 9d92533f9..000000000 --- a/src/test/regress/expected/tableam_0.out +++ /dev/null @@ -1,6 +0,0 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 11 AS server_version_above_eleven -\gset -\if :server_version_above_eleven -\else -\q diff --git a/src/test/regress/expected/upgrade_columnar_before_0.out b/src/test/regress/expected/upgrade_columnar_before_0.out deleted file mode 100644 index 9d92533f9..000000000 --- a/src/test/regress/expected/upgrade_columnar_before_0.out +++ /dev/null @@ -1,6 +0,0 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int > 11 AS server_version_above_eleven -\gset -\if :server_version_above_eleven -\else -\q diff --git a/src/test/regress/expected/upgrade_pg_dist_object_test_before.out b/src/test/regress/expected/upgrade_pg_dist_object_test_before.out index 2bdcedf5c..d6da445d5 100644 --- a/src/test/regress/expected/upgrade_pg_dist_object_test_before.out +++ b/src/test/regress/expected/upgrade_pg_dist_object_test_before.out @@ -51,22 +51,8 @@ SELECT create_reference_table('another_dist_table'); -- This is because tracking the dependencies will hit to the schema for two times CREATE SCHEMA fooschema; CREATE TYPE fooschema.footype AS (x int, y int); -SELECT run_command_on_workers($$CREATE SCHEMA fooschema;$$); - run_command_on_workers ---------------------------------------------------------------------- - (localhost,57636,t,"CREATE SCHEMA") - (localhost,57637,t,"CREATE SCHEMA") - (2 rows) - - SELECT run_command_on_workers($$CREATE TYPE fooschema.footype AS (x int, y int);$$); - run_command_on_workers ---------------------------------------------------------------------- - (localhost,57636,t,"CREATE TYPE") - (localhost,57637,t,"CREATE TYPE") - (2 rows) - - CREATE TABLE fooschema.footable (f fooschema.footype); - SELECT create_reference_table('fooschema.footable'); +CREATE TABLE fooschema.footable (f fooschema.footype); +SELECT create_reference_table('fooschema.footable'); create_reference_table --------------------------------------------------------------------- diff --git a/src/test/regress/expected/with_basics.out b/src/test/regress/expected/with_basics.out index d921d203c..819b25fc5 100644 --- a/src/test/regress/expected/with_basics.out +++ b/src/test/regress/expected/with_basics.out @@ -907,13 +907,12 @@ LEFT JOIN WHERE d1.user_id = d2.user_id )) AS bar USING (user_id); ERROR: cannot pushdown the subquery DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join --- should work fine with cte inlinig disabled -SET citus.enable_cte_inlining TO false; -WITH distinct_undistribured AS ( +-- should work fine with materialized ctes +WITH distinct_undistribured AS MATERIALIZED ( SELECT DISTINCT user_id FROM test_cte ), - exsist_in_distributed AS ( + exsist_in_distributed AS MATERIALIZED ( SELECT DISTINCT user_id FROM test_cte_distributed WHERE EXISTS(SELECT * @@ -938,7 +937,7 @@ ORDER BY 2 DESC, 1 DESC; 1 | 1 (8 rows) -WITH distinct_undistribured AS +WITH distinct_undistribured AS MATERIALIZED (SELECT DISTINCT user_id FROM test_cte) SELECT * @@ -954,7 +953,7 @@ LEFT JOIN ORDER BY 2 DESC, 1 DESC; ERROR: cannot pushdown the subquery DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join -WITH distinct_undistribured AS +WITH distinct_undistribured AS MATERIALIZED (SELECT DISTINCT user_id FROM test_cte) SELECT * @@ -969,11 +968,11 @@ LEFT JOIN ERROR: cannot pushdown the subquery DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join -- NOT EXISTS struct, with cte inlining disabled -WITH distinct_undistribured AS ( +WITH distinct_undistribured AS MATERIALIZED( SELECT DISTINCT user_id FROM test_cte ), - not_exsist_in_distributed AS ( + not_exsist_in_distributed AS MATERIALIZED ( SELECT DISTINCT user_id FROM test_cte_distributed WHERE NOT EXISTS(SELECT NULL @@ -1000,7 +999,7 @@ ORDER BY 2 DESC, 1 DESC; -- similar query, but this time the second -- part of the query is not inside a CTE -WITH distinct_undistribured AS ( +WITH distinct_undistribured AS MATERIALIZED ( SELECT DISTINCT user_id FROM test_cte ) @@ -1017,11 +1016,10 @@ LEFT JOIN ERROR: cannot pushdown the subquery DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join -- some test with failures -WITH a AS (SELECT * FROM users_table LIMIT 10) +WITH a AS MATERIALIZED (SELECT * FROM users_table LIMIT 10) SELECT user_id/0 FROM users_table JOIN a USING (user_id); ERROR: division by zero CONTEXT: while executing command on localhost:xxxxx -RESET citus.enable_cte_inlining; DROP VIEW basic_view; DROP VIEW cte_view; DROP SCHEMA with_basics CASCADE; diff --git a/src/test/regress/expected/with_join.out b/src/test/regress/expected/with_join.out index ba2ffaa6a..28166b31c 100644 --- a/src/test/regress/expected/with_join.out +++ b/src/test/regress/expected/with_join.out @@ -55,7 +55,7 @@ WITH non_colocated_1 AS ( WHERE users_table.user_id = events_table.value_2 AND event_type IN (1, 2, 3) ), -non_colocated_2 AS ( +non_colocated_2 AS MATERIALIZED ( SELECT users_table.user_id FROM @@ -84,8 +84,8 @@ ORDER BY -- Subqueries in WHERE and FROM are mixed -- In this query, only subquery in WHERE is not a colocated join -- but we're able to recursively plan that as well -WITH users_events AS ( - WITH colocated_join AS ( +WITH users_events AS MATERIALIZED ( + WITH colocated_join AS MATERIALIZED ( SELECT users_table.user_id as uid, event_type FROM @@ -96,7 +96,7 @@ WITH users_events AS ( WHERE events_table.event_type IN (1, 2, 3) ), - colocated_join_2 AS ( + colocated_join_2 AS MATERIALIZED ( SELECT users_table.user_id, event_type FROM @@ -115,7 +115,7 @@ WITH users_events AS ( WHERE colocated_join.uid = colocated_join_2.user_id AND colocated_join.event_type IN ( - WITH some_events AS ( + WITH some_events AS MATERIALIZED ( SELECT event_type FROM @@ -153,9 +153,7 @@ LIMIT -- cte LEFT JOIN distributed_table should error out -- as long as the CTE is recursively planned --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 ORDER BY value_1 ) SELECT @@ -170,9 +168,8 @@ LIMIT 5; ERROR: cannot pushdown the subquery DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join -SET citus.enable_cte_inlining TO true; -- cte RIGHT JOIN distributed_table should work -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 ORDER BY value_1 ) SELECT @@ -195,7 +192,7 @@ LIMIT (5 rows) -- distributed_table LEFT JOIN cte should work -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE value_1 = 1 ORDER BY value_1 ) SELECT @@ -218,9 +215,7 @@ LIMIT (5 rows) -- distributed_table RIGHT JOIN cte should error out --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE value_1 = 1 ORDER BY value_1 ) SELECT @@ -236,7 +231,7 @@ LIMIT ERROR: cannot pushdown the subquery DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join -- cte FULL JOIN distributed_table should error out -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 ORDER BY value_1 ) SELECT @@ -251,9 +246,8 @@ LIMIT 5; ERROR: cannot pushdown the subquery DETAIL: Complex subqueries and CTEs cannot be in the outer part of the outer join -SET citus.enable_cte_inlining TO false; -- Joins with reference tables are planned as router queries -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT value_2, max(user_id) AS user_id FROM users_table WHERE value_2 = 1 GROUP BY value_2 HAVING count(*) > 1 ) SELECT @@ -315,7 +309,7 @@ join (select distrib_col,count(*) from distributed_2 group by distrib_col) d2 ON 1734 (1 row) -with d2 AS (select distrib_col,count(*) from distributed_2 group by distrib_col) +with d2 AS MATERIALIZED (select distrib_col,count(*) from distributed_2 group by distrib_col) select count(*) from distributed_1 AS d1 LEFT JOIN reference_1 AS r1 ON d1.col2=r1.col2 LEFT JOIN reference_2 AS r2 ON r2.col1 = r1.col1 @@ -325,7 +319,7 @@ join d2 ON d2.distrib_col=d1.distrib_col; 1734 (1 row) -with d2 AS (select distrib_col,col1 from distributed_2) +with d2 AS MATERIALIZED (select distrib_col,col1 from distributed_2) select count(*) from distributed_1 AS d1 LEFT JOIN reference_1 AS r1 ON d1.col2=r1.col2 LEFT JOIN reference_2 AS r2 ON r2.col1 = r1.col1 @@ -335,7 +329,7 @@ join d2 ON d2.distrib_col=d1.distrib_col; 87584 (1 row) -with cte_1 AS (select col1 from reference_1) +with cte_1 AS MATERIALIZED (select col1 from reference_1) select count(*) from distributed_1 AS d1 LEFT JOIN reference_1 AS r1 ON d1.col2=r1.col2 LEFT JOIN reference_2 AS r2 ON r2.col1 = r1.col1 diff --git a/src/test/regress/expected/with_modifying.out b/src/test/regress/expected/with_modifying.out index 5d72f951f..9f62271d5 100644 --- a/src/test/regress/expected/with_modifying.out +++ b/src/test/regress/expected/with_modifying.out @@ -587,15 +587,14 @@ raw_data AS ( SELECT * FROM raw_data ORDER BY val; ERROR: cannot perform distributed planning for the given modification DETAIL: Recursively planned distributed modifications with ctid on where clause are not supported. --- Needed becaues of CTE inlining triggering https://github.com/citusdata/citus/issues/3189 -SET citus.enable_cte_inlining TO FALSE; -WITH added_data AS ( +-- We materialize because of https://github.com/citusdata/citus/issues/3189 +WITH added_data AS MATERIALIZED ( INSERT INTO modify_table VALUES (1, trunc(10 * random())), (1, trunc(random())) RETURNING * ), -select_data AS ( +select_data AS MATERIALIZED ( SELECT val, now() FROM added_data WHERE id = 1 ), -raw_data AS ( +raw_data AS MATERIALIZED ( DELETE FROM modify_table WHERE id = 1 AND val IN (SELECT val FROM select_data) RETURNING * ) SELECT COUNT(*) FROM raw_data; @@ -604,7 +603,6 @@ SELECT COUNT(*) FROM raw_data; 2 (1 row) -SET citus.enable_cte_inlining TO TRUE; WITH added_data AS ( INSERT INTO modify_table VALUES (1, trunc(10 * random())), (1, trunc(random())) RETURNING * ), @@ -824,7 +822,7 @@ EXECUTE olu(1,ARRAY[1,2],ARRAY[1,2]); -- test insert query with insert CTE WITH insert_cte AS - (INSERT INTO with_modifying.modify_table VALUES (23, 7)) + (INSERT INTO with_modifying.modify_table VALUES (23, 7)) INSERT INTO with_modifying.anchor_table VALUES (1998); SELECT * FROM with_modifying.modify_table WHERE id = 23 AND val = 7; id | val @@ -840,7 +838,7 @@ SELECT * FROM with_modifying.anchor_table WHERE id = 1998; -- test insert query with multiple CTEs WITH select_cte AS (SELECT * FROM with_modifying.anchor_table), - modifying_cte AS (INSERT INTO with_modifying.anchor_table SELECT * FROM select_cte) + modifying_cte AS (INSERT INTO with_modifying.anchor_table SELECT * FROM select_cte) INSERT INTO with_modifying.anchor_table VALUES (1995); SELECT * FROM with_modifying.anchor_table ORDER BY 1; id @@ -866,7 +864,7 @@ SELECT * FROM with_modifying.anchor_table WHERE id IN (1996, 1997) ORDER BY 1; -- test insert query with select CTE WITH select_cte AS - (SELECT * FROM with_modifying.modify_table) + (SELECT * FROM with_modifying.modify_table) INSERT INTO with_modifying.anchor_table VALUES (1990); SELECT * FROM with_modifying.anchor_table WHERE id = 1990; id diff --git a/src/test/regress/expected/with_set_operations.out b/src/test/regress/expected/with_set_operations.out index 461a184e5..94398d33a 100644 --- a/src/test/regress/expected/with_set_operations.out +++ b/src/test/regress/expected/with_set_operations.out @@ -2,17 +2,17 @@ -- test recursive planning functionality with Set Operations and CTEs -- =================================================================== SET client_min_messages TO DEBUG1; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -- use ctes inside unions on the top level WITH cte_1 AS (SELECT user_id FROM users_table), cte_2 AS (SELECT user_id FROM events_table) (SELECT * FROM cte_1) UNION (SELECT * FROM cte_2) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT user_id FROM public.events_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_2.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_2 ORDER BY 1 DESC +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_2 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM (SELECT events_table.user_id FROM public.events_table) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) ORDER BY 1 DESC user_id --------------------------------------------------------------------- 6 @@ -32,10 +32,8 @@ SELECT FROM ( (SELECT * FROM cte_1) UNION (SELECT * FROM cte_2) ) as foo; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT user_id FROM public.events_table -DEBUG: generating subplan XXX_3 for subquery SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_2.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_2 is going to be inlined via distributed planning count --------------------------------------------------------------------- 6 @@ -47,10 +45,9 @@ cte_1 AS (SELECT user_id FROM users_table), cte_2 AS (SELECT user_id FROM events_table), cte_3 AS ((SELECT * FROM cte_1) UNION (SELECT * FROM cte_2)) SELECT * FROM cte_3 ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT user_id FROM public.events_table -DEBUG: generating subplan XXX_3 for CTE cte_3: SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_2.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_3 ORDER BY user_id DESC +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_2 is going to be inlined via distributed planning +DEBUG: CTE cte_3 is going to be inlined via distributed planning user_id --------------------------------------------------------------------- 6 @@ -65,11 +62,7 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id FROM WITH cte_1 AS ((SELECT user_id FROM users_table) UNION (SELECT user_id FROM users_table)) SELECT * FROM cte_1 ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT users_table.user_id FROM public.users_table UNION SELECT users_table.user_id FROM public.users_table -DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 ORDER BY user_id DESC +DEBUG: CTE cte_1 is going to be inlined via distributed planning user_id --------------------------------------------------------------------- 6 @@ -84,12 +77,10 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id FROM WITH cte_1 AS ((SELECT user_id FROM users_table) UNION (SELECT user_id FROM users_table)) (SELECT * FROM cte_1) INTERSECT (SELECT user_id FROM users_table) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT users_table.user_id FROM public.users_table UNION SELECT users_table.user_id FROM public.users_table -DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM public.users_table +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table UNION SELECT users_table.user_id FROM public.users_table) cte_1 DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) ORDER BY 1 DESC +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) ORDER BY 1 DESC user_id --------------------------------------------------------------------- 6 @@ -106,13 +97,11 @@ cte_1 AS ((SELECT user_id FROM users_table) UNION (SELECT user_id FROM users_tab (SELECT * FROM cte_1) INTERSECT ((SELECT user_id FROM events_table WHERE user_id < 3) EXCEPT (SELECT user_id FROM users_table WHERE user_id > 4)) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT users_table.user_id FROM public.users_table UNION SELECT users_table.user_id FROM public.users_table -DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table UNION SELECT users_table.user_id FROM public.users_table) cte_1 DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.<) 3) DEBUG: generating subplan XXX_3 for subquery SELECT user_id FROM public.users_table WHERE (user_id OPERATOR(pg_catalog.>) 4) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 INTERSECT (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) EXCEPT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) ORDER BY 1 DESC +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) EXCEPT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) ORDER BY 1 DESC user_id --------------------------------------------------------------------- 2 @@ -121,9 +110,10 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_1.user_id -- CTE inside a top level EXCEPT (WITH cte_1 AS (SELECT user_id FROM events_table WHERE user_id < 3) SELECT * FROM cte_1) INTERSECT (SELECT user_id FROM users_table) ORDER BY 1; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.<) 3) +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT events_table.user_id FROM public.events_table WHERE (events_table.user_id OPERATOR(pg_catalog.<) 3)) cte_1 DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) ORDER BY 1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) ORDER BY 1 user_id --------------------------------------------------------------------- 1 @@ -141,11 +131,11 @@ FROM WHERE users_table.user_id = foo.user_id ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT events_table.user_id FROM public.events_table WHERE (events_table.user_id OPERATOR(pg_catalog.<) 3) INTERSECT SELECT events_table.user_id FROM public.events_table WHERE (events_table.user_id OPERATOR(pg_catalog.<) 2) +DEBUG: CTE cte_1 is going to be inlined via distributed planning DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.<) 3) DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.<) 2) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT users_table.user_id FROM public.users_table, (SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1) foo WHERE (users_table.user_id OPERATOR(pg_catalog.=) foo.user_id) ORDER BY users_table.user_id DESC +DEBUG: generating subplan XXX_3 for subquery SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT users_table.user_id FROM public.users_table, (SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1) foo WHERE (users_table.user_id OPERATOR(pg_catalog.=) foo.user_id) ORDER BY users_table.user_id DESC user_id --------------------------------------------------------------------- 1 @@ -164,10 +154,13 @@ FROM (SELECT * FROM cte_1) UNION (SELECT * FROM cte_2) ) as foo WHERE foo.user_id = events_table.event_type; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT user_id FROM public.events_table -DEBUG: generating subplan XXX_3 for subquery SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_2.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.events_table, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo WHERE (foo.user_id OPERATOR(pg_catalog.=) events_table.event_type) +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_2 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM (SELECT events_table.user_id FROM public.events_table) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: generating subplan XXX_1 for subquery SELECT cte_1.user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 UNION SELECT cte_2.user_id FROM (SELECT events_table.user_id FROM public.events_table) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.events_table, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo WHERE (foo.user_id OPERATOR(pg_catalog.=) events_table.event_type) count --------------------------------------------------------------------- 95 @@ -224,13 +217,13 @@ FROM as foo WHERE foo.user_id = events_table.value_2; +DEBUG: CTE foo is going to be inlined via distributed planning DEBUG: generating subplan XXX_1 for subquery SELECT DISTINCT value_2 FROM public.events_table -DEBUG: generating subplan XXX_2 for CTE foo: SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) INTERSECT (SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) LIMIT 10) DEBUG: push down of limit count: 10 -DEBUG: generating subplan XXX_1 for subquery SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) LIMIT 10 -DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer)) events_table, (SELECT foo_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo_1) foo WHERE (foo.user_id OPERATOR(pg_catalog.=) events_table.value_2) +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) LIMIT 10 +DEBUG: generating subplan XXX_3 for subquery SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) +DEBUG: generating subplan XXX_4 for subquery SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer)) events_table, (SELECT foo_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo_1) foo WHERE (foo.user_id OPERATOR(pg_catalog.=) events_table.value_2) count --------------------------------------------------------------------- 5 @@ -246,12 +239,12 @@ FROM (SELECT DISTINCT events_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.user_id LIMIT 10)) as foo WHERE foo.user_id = events_table.user_id; -DEBUG: generating subplan XXX_1 for CTE events_table: SELECT DISTINCT user_id FROM public.events_table +DEBUG: CTE events_table is going to be inlined via distributed planning DEBUG: push down of limit count: 10 -DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) LIMIT 10 -DEBUG: generating subplan XXX_3 for subquery SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) -DEBUG: generating subplan XXX_4 for subquery SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT events_table_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) events_table_1) events_table, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_4'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo WHERE (foo.user_id OPERATOR(pg_catalog.=) events_table.user_id) +DEBUG: generating subplan XXX_1 for subquery SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) LIMIT 10 +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT events_table.user_id FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) +DEBUG: generating subplan XXX_3 for subquery SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) INTERSECT SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT events_table_1.user_id FROM (SELECT DISTINCT events_table_2.user_id FROM public.events_table events_table_2) events_table_1) events_table, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo WHERE (foo.user_id OPERATOR(pg_catalog.=) events_table.user_id) count --------------------------------------------------------------------- 6 @@ -260,9 +253,11 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c -- CTE inside unions (WITH cte_1 AS (SELECT user_id FROM users_table) SELECT * FROM cte_1) UNION (WITH cte_1 AS (SELECT user_id FROM users_table) SELECT * FROM cte_1) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 ORDER BY 1 DESC +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) ORDER BY 1 DESC user_id --------------------------------------------------------------------- 6 @@ -283,10 +278,13 @@ FROM ) as foo, users_table WHERE users_table.value_2 = foo.user_id; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_3 for subquery SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, public.users_table WHERE (users_table.value_2 OPERATOR(pg_catalog.=) foo.user_id) +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: generating subplan XXX_1 for subquery SELECT cte_1.user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 UNION SELECT cte_1.user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, public.users_table WHERE (users_table.value_2 OPERATOR(pg_catalog.=) foo.user_id) count --------------------------------------------------------------------- 92 @@ -295,9 +293,11 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c -- CTEs with less alias than the input subquery (WITH cte_1(x) AS (SELECT user_id, value_2 FROM users_table) SELECT * FROM cte_1) UNION (WITH cte_1(x) AS (SELECT user_id, value_2 FROM users_table) SELECT * FROM cte_1) ORDER BY 1 DESC, 2 DESC LIMIT 5; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id, value_2 FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_1: SELECT user_id, value_2 FROM public.users_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte_1.x, cte_1.value_2 FROM (SELECT intermediate_result.user_id AS x, intermediate_result.value_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_2 integer)) cte_1 UNION SELECT cte_1.x, cte_1.value_2 FROM (SELECT intermediate_result.user_id AS x, intermediate_result.value_2 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_2 integer)) cte_1 ORDER BY 1 DESC, 2 DESC LIMIT 5 +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT x, value_2 FROM (SELECT users_table.user_id AS x, users_table.value_2 FROM public.users_table) cte_1 +DEBUG: generating subplan XXX_2 for subquery SELECT x, value_2 FROM (SELECT users_table.user_id AS x, users_table.value_2 FROM public.users_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.x, intermediate_result.value_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer, value_2 integer) UNION SELECT intermediate_result.x, intermediate_result.value_2 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(x integer, value_2 integer) ORDER BY 1 DESC, 2 DESC LIMIT 5 x | value_2 --------------------------------------------------------------------- 6 | @@ -323,11 +323,9 @@ WHERE ) SELECT DISTINCT user_id FROM cte_1 ) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT users_table.user_id FROM public.users_table UNION SELECT events_table.user_id FROM public.events_table -DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.events_table -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.users_table WHERE (value_2 OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1)) ORDER BY (count(*)) DESC +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT DISTINCT user_id FROM (SELECT users_table.user_id FROM public.users_table UNION SELECT events_table.user_id FROM public.events_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.users_table WHERE (value_2 OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer))) ORDER BY (count(*)) DESC count --------------------------------------------------------------------- 92 @@ -347,10 +345,13 @@ WHERE (SELECT * FROM cte_1) UNION (SELECT * FROM cte_2) ) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_2: SELECT user_id FROM public.events_table -DEBUG: generating subplan XXX_3 for subquery SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_2.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.users_table WHERE (value_2 OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer))) ORDER BY (count(*)) DESC +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_2 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM (SELECT events_table.user_id FROM public.events_table) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: generating subplan XXX_1 for subquery SELECT cte_1.user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 UNION SELECT cte_2.user_id FROM (SELECT events_table.user_id FROM public.events_table) cte_2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.users_table WHERE (value_2 OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer))) ORDER BY (count(*)) DESC count --------------------------------------------------------------------- 92 @@ -375,10 +376,13 @@ WHERE WHERE users_table.value_2 = foo.user_id AND events_table.user_id = users_table.user_id ) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_3 for subquery SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT user_id FROM public.events_table WHERE (event_type OPERATOR(pg_catalog.=) ANY (SELECT users_table.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, public.users_table WHERE ((users_table.value_2 OPERATOR(pg_catalog.=) foo.user_id) AND (events_table.user_id OPERATOR(pg_catalog.=) users_table.user_id)))) ORDER BY user_id DESC +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: generating subplan XXX_1 for subquery SELECT cte_1.user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 UNION SELECT cte_1.user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT user_id FROM public.events_table WHERE (event_type OPERATOR(pg_catalog.=) ANY (SELECT users_table.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, public.users_table WHERE ((users_table.value_2 OPERATOR(pg_catalog.=) foo.user_id) AND (events_table.user_id OPERATOR(pg_catalog.=) users_table.user_id)))) ORDER BY user_id DESC user_id --------------------------------------------------------------------- 5 @@ -409,10 +413,13 @@ WHERE LIMIT 5 ) ORDER BY 1 DESC; -DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_2 for CTE cte_1: SELECT user_id FROM public.users_table -DEBUG: generating subplan XXX_3 for subquery SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 UNION SELECT cte_1.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT user_id FROM public.events_table WHERE (event_type OPERATOR(pg_catalog.=) ANY (SELECT users_table.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, public.users_table WHERE ((users_table.value_2 OPERATOR(pg_catalog.=) foo.user_id) AND (events_table.user_id OPERATOR(pg_catalog.=) users_table.user_id)) LIMIT 5)) ORDER BY user_id DESC +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: generating subplan XXX_1 for subquery SELECT cte_1.user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 UNION SELECT cte_1.user_id FROM (SELECT users_table.user_id FROM public.users_table) cte_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT user_id FROM public.events_table WHERE (event_type OPERATOR(pg_catalog.=) ANY (SELECT users_table.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, public.users_table WHERE ((users_table.value_2 OPERATOR(pg_catalog.=) foo.user_id) AND (events_table.user_id OPERATOR(pg_catalog.=) users_table.user_id)) LIMIT 5)) ORDER BY user_id DESC user_id --------------------------------------------------------------------- 5 diff --git a/src/test/regress/sql/coordinator_shouldhaveshards.sql b/src/test/regress/sql/coordinator_shouldhaveshards.sql index aeb2ccb24..85604a366 100644 --- a/src/test/regress/sql/coordinator_shouldhaveshards.sql +++ b/src/test/regress/sql/coordinator_shouldhaveshards.sql @@ -147,7 +147,6 @@ INSERT INTO local VALUES (1,2); SELECT * FROM ref JOIN local ON (a = x); ROLLBACK; -set citus.enable_cte_inlining to off; BEGIN; SELECT count(*) FROM test; @@ -220,7 +219,6 @@ CREATE TABLE dist_table1(a int); SELECT create_distributed_table('dist_table1', 'a'); ROLLBACK; -RESET citus.enable_cte_inlining; CREATE table ref_table(x int PRIMARY KEY, y int); -- this will be replicated to the coordinator because of add_coordinator test SELECT create_reference_table('ref_table'); diff --git a/src/test/regress/sql/failure_cte_subquery.sql b/src/test/regress/sql/failure_cte_subquery.sql index 63f79b00b..7d0ca2617 100644 --- a/src/test/regress/sql/failure_cte_subquery.sql +++ b/src/test/regress/sql/failure_cte_subquery.sql @@ -7,8 +7,7 @@ SET citus.next_shard_id TO 16000000; -- CTE inlining should not happen because -- the tests rely on intermediate results -SET citus.enable_cte_inlining TO false; - +-- That's why we use MATERIALIZED CTEs in the test file -- prevent using locally executing the intermediate results SET citus.task_assignment_policy TO "round-robin"; @@ -23,11 +22,11 @@ CREATE TABLE users_table_local AS SELECT * FROM users_table; -- kill at the first copy (push) SELECT citus.mitmproxy('conn.onQuery(query="^COPY").kill()'); -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -50,11 +49,11 @@ FROM -- kill at the second copy (pull) SELECT citus.mitmproxy('conn.onQuery(query="SELECT user_id FROM cte_failure.events_table_16000002").kill()'); -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -77,11 +76,11 @@ FROM -- kill at the third copy (pull) SELECT citus.mitmproxy('conn.onQuery(query="SELECT DISTINCT users_table.user").kill()'); -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -104,11 +103,11 @@ FROM -- cancel at the first copy (push) SELECT citus.mitmproxy('conn.onQuery(query="^COPY").cancel(' || :pid || ')'); -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -131,11 +130,11 @@ FROM -- cancel at the second copy (pull) SELECT citus.mitmproxy('conn.onQuery(query="SELECT user_id FROM").cancel(' || :pid || ')'); -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -158,11 +157,11 @@ FROM -- cancel at the third copy (pull) SELECT citus.mitmproxy('conn.onQuery(query="SELECT DISTINCT users_table.user").cancel(' || :pid || ')'); -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -191,14 +190,14 @@ INSERT INTO events_table VALUES (1,1,1), (1,2,1), (1,3,1), (2,1, 4), (3, 4,1), ( SELECT * FROM users_table ORDER BY 1, 2; -- following will delete and insert the same rows -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; -- verify contents are the same SELECT * FROM users_table ORDER BY 1, 2; -- kill connection during deletion SELECT citus.mitmproxy('conn.onQuery(query="^DELETE FROM").kill()'); -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; -- verify contents are the same @@ -207,7 +206,7 @@ SELECT * FROM users_table ORDER BY 1, 2; -- kill connection during insert SELECT citus.mitmproxy('conn.onQuery(query="^COPY").kill()'); -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; -- verify contents are the same @@ -216,7 +215,7 @@ SELECT * FROM users_table ORDER BY 1, 2; -- cancel during deletion SELECT citus.mitmproxy('conn.onQuery(query="^DELETE FROM").cancel(' || :pid || ')'); -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; -- verify contents are the same @@ -225,7 +224,7 @@ SELECT * FROM users_table ORDER BY 1, 2; -- cancel during insert SELECT citus.mitmproxy('conn.onQuery(query="^COPY").cancel(' || :pid || ')'); -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; -- verify contents are the same @@ -236,7 +235,7 @@ SELECT * FROM users_table ORDER BY 1, 2; SELECT citus.mitmproxy('conn.onQuery(query="^DELETE FROM").kill()'); BEGIN; SET LOCAL citus.multi_shard_modify_mode = 'sequential'; -WITH cte_delete as (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) +WITH cte_delete AS MATERIALIZED (DELETE FROM users_table WHERE user_name in ('A', 'D') RETURNING *) INSERT INTO users_table SELECT * FROM cte_delete; END; diff --git a/src/test/regress/sql/intermediate_result_pruning.sql b/src/test/regress/sql/intermediate_result_pruning.sql index b6e17723d..1fba1a700 100644 --- a/src/test/regress/sql/intermediate_result_pruning.sql +++ b/src/test/regress/sql/intermediate_result_pruning.sql @@ -19,8 +19,6 @@ SELECT create_distributed_table('table_3', 'key'); CREATE TABLE ref_table (key int, value text); SELECT create_reference_table('ref_table'); --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -- load some data INSERT INTO table_1 VALUES (1, '1'), (2, '2'), (3, '3'), (4, '4'); @@ -33,7 +31,7 @@ SET client_min_messages TO DEBUG1; -- a very basic case, where the intermediate result -- should go to both workers -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key FROM table_1 WHERE value IN ('3', '4')) SELECT count(*) @@ -44,7 +42,7 @@ FROM -- a very basic case, where the intermediate result -- should only go to one worker because the final query is a router -- we use random() to prevent postgres inline the CTE(s) -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')) SELECT count(*) @@ -54,7 +52,7 @@ FROM -- a similar query, but with a reference table now -- given that reference tables are replicated to all nodes -- we have to broadcast to all nodes -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')) SELECT count(*) @@ -64,9 +62,9 @@ FROM -- a similar query as above, but this time use the CTE inside -- another CTE -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1) SELECT count(*) @@ -75,9 +73,9 @@ FROM -- the second CTE does a join with a distributed table -- and the final query is a router query -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key)) SELECT count(*) @@ -87,9 +85,9 @@ FROM -- the first CTE is used both within second CTE and the final query -- the second CTE does a join with a distributed table -- and the final query is a router query -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key)) SELECT count(*) @@ -99,9 +97,9 @@ FROM -- the first CTE is used both within second CTE and the final query -- the second CTE does a join with a distributed table but a router query on a worker -- and the final query is another router query on another worker -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE table_2.key = 1) SELECT count(*) @@ -112,9 +110,9 @@ FROM -- the second CTE does a join with a distributed table but a router query on a worker -- and the final query is a router query on the same worker, so the first result is only -- broadcasted to a single node -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE table_2.key = 1) SELECT count(*) @@ -122,9 +120,9 @@ FROM (some_values_2 JOIN table_2 USING (key)) JOIN some_values_1 USING (key) WHERE table_2.key = 1; -- the same query with the above, but the final query is hitting all shards -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key)) SELECT count(*) @@ -134,9 +132,9 @@ FROM -- even if we add a filter on the first query and make it a router query, -- the first intermediate result still hits all workers because of the final -- join is hitting all workers -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE table_2.key = 3) SELECT count(*) @@ -146,7 +144,7 @@ FROM -- the reference table is joined with a distributed table and an intermediate -- result, but the distributed table hits all shards, so the intermediate -- result is sent to all nodes -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM ref_table WHERE value IN ('3', '4')) SELECT count(*) @@ -155,7 +153,7 @@ FROM -- similar query as above, but this time the whole query is a router -- query, so no intermediate results -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM ref_table WHERE value IN ('3', '4')) SELECT count(*) @@ -167,9 +165,9 @@ FROM -- so the first CTE should only be broadcasted to that node -- since the final query doesn't have a join, it should simply be broadcasted -- to one node -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE key = 1) SELECT count(*) @@ -179,10 +177,10 @@ FROM -- the same query inlined inside a CTE, and the final query has a -- join with a distributed table -WITH top_cte as ( - WITH some_values_1 AS +WITH top_cte as MATERIALIZED ( + WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE key = 1) SELECT DISTINCT key @@ -197,10 +195,10 @@ FROM -- very much the same query, but this time the top query is also a router query -- on a single worker, so all intermediate results only hit a single node -WITH top_cte as ( - WITH some_values_1 AS +WITH top_cte as MATERIALIZED ( + WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE key = 1) SELECT DISTINCT key @@ -215,11 +213,11 @@ FROM -- some_values_1 is first used by a single shard-query, and than with a multi-shard -- CTE, finally a cartesian product join -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1 JOIN table_2 USING (key) WHERE key = 1), - some_values_3 AS + some_values_3 AS MATERIALIZED (SELECT key FROM (some_values_2 JOIN table_2 USING (key)) JOIN some_values_1 USING (key)) SELECT * FROM some_values_3 JOIN ref_table ON (true); @@ -227,28 +225,28 @@ SELECT * FROM some_values_3 JOIN ref_table ON (true); -- join on intermediate results, so should only -- go to a single node -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM table_2 WHERE value IN ('3', '4')) SELECT count(*) FROM some_values_2 JOIN some_values_1 USING (key); -- same query with WHERE false make sure that we're not broken -- for such edge cases -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM table_2 WHERE value IN ('3', '4')) SELECT count(*) FROM some_values_2 JOIN some_values_1 USING (key) WHERE false; -- do not use some_values_2 at all, so only 2 intermediate results are -- broadcasted -WITH some_values_1 AS +WITH some_values_1 AS MATERIALIZED (SELECT key, random() FROM table_1 WHERE value IN ('3', '4')), - some_values_2 AS + some_values_2 AS MATERIALIZED (SELECT key, random() FROM some_values_1), - some_values_3 AS + some_values_3 AS MATERIALIZED (SELECT key, random() FROM some_values_1) SELECT count(*) @@ -342,13 +340,13 @@ INTERSECT (SELECT key FROM table_1 WHERE key = 2); -- the intermediate results should just hit a single worker -WITH cte_1 AS +WITH cte_1 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 1) INTERSECT (SELECT key FROM table_1 WHERE key = 2) ), -cte_2 AS +cte_2 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 3) INTERSECT @@ -362,13 +360,13 @@ SELECT * FROM cte_2; -- we join the results with distributed tables -- so cte_1 should hit all workers, but still the -- others should hit single worker each -WITH cte_1 AS +WITH cte_1 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 1) INTERSECT (SELECT key FROM table_1 WHERE key = 2) ), -cte_2 AS +cte_2 AS MATERIALIZED ( SELECT count(*) FROM table_1 JOIN cte_1 USING (key) ) @@ -404,10 +402,10 @@ WHERE -- however, the subquery in WHERE clause of the DELETE query is broadcasted to all -- nodes BEGIN; -WITH select_data AS ( +WITH select_data AS MATERIALIZED ( SELECT * FROM table_1 ), -raw_data AS ( +raw_data AS MATERIALIZED ( DELETE FROM table_2 WHERE key >= (SELECT min(key) FROM select_data WHERE key > 1) RETURNING * ) SELECT * FROM raw_data; @@ -418,10 +416,10 @@ ROLLBACK; -- however, the subquery in WHERE clause of the DELETE query is broadcasted to all -- nodes BEGIN; -WITH select_data AS ( +WITH select_data AS MATERIALIZED ( SELECT * FROM table_1 ), -raw_data AS ( +raw_data AS MATERIALIZED ( DELETE FROM table_2 WHERE value::int >= (SELECT min(key) FROM select_data WHERE key > 1 + random()) RETURNING * ) SELECT * FROM raw_data; @@ -430,10 +428,10 @@ ROLLBACK; -- now, we need only two intermediate results as the subquery in WHERE clause is -- router plannable BEGIN; -WITH select_data AS ( +WITH select_data AS MATERIALIZED ( SELECT * FROM table_1 ), -raw_data AS ( +raw_data AS MATERIALIZED ( DELETE FROM table_2 WHERE value::int >= (SELECT min(key) FROM table_1 WHERE key > random()) AND key = 6 RETURNING * ) SELECT * FROM raw_data; @@ -456,13 +454,13 @@ INSERT INTO table_1 INSERT INTO table_1 SELECT * FROM table_2 where key = 1 AND value::int IN - (WITH cte_1 AS + (WITH cte_1 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 1) INTERSECT (SELECT key FROM table_1 WHERE key = 2) ), - cte_2 AS + cte_2 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 3) INTERSECT @@ -477,13 +475,13 @@ INSERT INTO table_1 -- hits all the shards because table_2.key != 1 INSERT INTO table_1 SELECT table_2.* FROM table_2, - (WITH cte_1 AS + (WITH cte_1 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 1) INTERSECT (SELECT key FROM table_1 WHERE key = 2) ), - cte_2 AS + cte_2 AS MATERIALIZED ( (SELECT key FROM table_1 WHERE key = 3) INTERSECT @@ -565,16 +563,16 @@ INSERT INTO stats (account_id, spent) VALUES ('foo', 100); SELECT * FROM ( - WITH accounts_cte AS ( + WITH accounts_cte AS MATERIALIZED ( SELECT id AS account_id FROM accounts ), - joined_stats_cte_1 AS ( + joined_stats_cte_1 AS MATERIALIZED ( SELECT spent, account_id FROM stats INNER JOIN accounts_cte USING (account_id) ), - joined_stats_cte_2 AS ( + joined_stats_cte_2 AS MATERIALIZED ( SELECT spent, account_id FROM joined_stats_cte_1 INNER JOIN accounts_cte USING (account_id) @@ -589,16 +587,16 @@ SET citus.task_assignment_policy to 'round-robin'; SELECT * FROM ( - WITH accounts_cte AS ( + WITH accounts_cte AS MATERIALIZED ( SELECT id AS account_id FROM accounts ), - joined_stats_cte_1 AS ( + joined_stats_cte_1 AS MATERIALIZED ( SELECT spent, account_id FROM stats INNER JOIN accounts_cte USING (account_id) ), - joined_stats_cte_2 AS ( + joined_stats_cte_2 AS MATERIALIZED ( SELECT spent, account_id FROM joined_stats_cte_1 INNER JOIN accounts_cte USING (account_id) @@ -612,10 +610,10 @@ RESET citus.task_assignment_policy; -- Insert..select is planned differently, make sure we have results everywhere. -- We put the insert..select in a CTE here to prevent the CTE from being moved -- into the select, which would follow the regular code path for select. -WITH stats AS ( +WITH stats AS MATERIALIZED ( SELECT count(key) m FROM table_3 ), -inserts AS ( +inserts AS MATERIALIZED ( INSERT INTO table_2 SELECT key, count(*) FROM table_1 diff --git a/src/test/regress/sql/limit_intermediate_size.sql b/src/test/regress/sql/limit_intermediate_size.sql index 09922a10b..420923f84 100644 --- a/src/test/regress/sql/limit_intermediate_size.sql +++ b/src/test/regress/sql/limit_intermediate_size.sql @@ -1,22 +1,19 @@ SET citus.enable_repartition_joins to ON; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; - SET citus.max_intermediate_result_size TO 2; -- should fail because the copy size is ~4kB for each cte -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT * FROM users_table ), -cte2 AS ( +cte2 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT cte.user_id, cte.value_2 FROM cte,cte2 ORDER BY 1,2 LIMIT 10; SET citus.max_intermediate_result_size TO 9; -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT users_table.user_id, users_table.value_1, users_table.value_2 @@ -27,7 +24,7 @@ WITH cte AS on (users_table.value_3=events_table.value_3) ), -cte2 AS ( +cte2 AS MATERIALIZED( SELECT * FROM events_table ) SELECT @@ -42,7 +39,7 @@ LIMIT 10; -- router queries should be able to get limitted too SET citus.max_intermediate_result_size TO 2; -- this should pass, since we fetch small portions in each subplan -with cte as (select * from users_table where user_id=1), +with cte as MATERIALIZED (select * from users_table where user_id=1), cte2 as (select * from users_table where user_id=2), cte3 as (select * from users_table where user_id=3), cte4 as (select * from users_table where user_id=4), @@ -61,17 +58,17 @@ UNION -- if we fetch the same amount of data at once, it should fail -WITH cte AS (SELECT * FROM users_table WHERE user_id IN (1,2,3,4,5)) +WITH cte AS MATERIALIZED (SELECT * FROM users_table WHERE user_id IN (1,2,3,4,5)) SELECT * FROM cte ORDER BY 1,2,3,4,5 LIMIT 10; SET citus.max_intermediate_result_size TO 0; -- this should fail -WITH cte AS (SELECT * FROM users_table WHERE user_id=1), -cte2 AS (SELECT * FROM users_table WHERE user_id=2), -cte3 AS (SELECT * FROM users_table WHERE user_id=3), -cte4 AS (SELECT * FROM users_table WHERE user_id=4), -cte5 AS (SELECT * FROM users_table WHERE user_id=5) +WITH cte AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=1), +cte2 AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=2), +cte3 AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=3), +cte4 AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=4), +cte5 AS MATERIALIZED (SELECT * FROM users_table WHERE user_id=5) SELECT * FROM ( (SELECT * FROM cte) UNION @@ -88,11 +85,11 @@ UNION -- this fails since cte-subplan exceeds limit even if cte2 and cte3 don't -- WHERE EXISTS forces materialization in pg12 SET citus.max_intermediate_result_size TO 4; -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table ), - cte3 AS ( + cte3 AS MATERIALIZED( SELECT * FROM events_table ) SELECT * FROM cte2, cte3 WHERE cte2.user_id = cte3.user_id AND cte2.user_id = 1 @@ -104,11 +101,11 @@ SELECT count(*) FROM cte WHERE EXISTS (select * from cte); SET citus.max_intermediate_result_size TO 3; -- this should fail since the cte-subplan exceeds the limit even if the -- cte2 and cte3 does not -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (3,4,5,6) ), - cte3 AS ( + cte3 AS MATERIALIZED( SELECT * FROM events_table WHERE event_type = 1 ) SELECT * FROM cte2, cte3 WHERE cte2.value_1 IN (SELECT value_2 FROM cte3) @@ -118,16 +115,16 @@ SELECT count(*) FROM cte; -- this will fail in remote execution SET citus.max_intermediate_result_size TO 2; -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (1, 2) ), - cte3 AS ( + cte3 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 3 ) SELECT * FROM cte2 UNION (SELECT * FROM cte3) ), -cte4 AS ( +cte4 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT * FROM cte UNION ALL @@ -136,43 +133,43 @@ SELECT * FROM cte4 ORDER BY 1,2,3,4,5 LIMIT 5; SET citus.max_intermediate_result_size TO 1; -- this will fail in router_executor -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (1, 2) ), - cte3 AS ( + cte3 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 3 ) SELECT * FROM cte2 UNION (SELECT * FROM cte3) ), -cte4 AS ( +cte4 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT * FROM cte UNION ALL SELECT * FROM cte4 ORDER BY 1,2,3,4,5 LIMIT 5; --- Below that, all should pass since -1 disables the limit +-- Below that, all should pAS MATERIALIZEDs since -1 disables the limit SET citus.max_intermediate_result_size TO -1; -- real_time_executor + router_executor + real_time_executor will pass -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (1, 2) ), - cte3 AS ( + cte3 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 3 ) SELECT * FROM cte2 UNION (SELECT * FROM cte3) ), -cte4 AS ( +cte4 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT * FROM cte UNION ALL SELECT * FROM cte4 ORDER BY 1,2,3,4,5 LIMIT 5; -- regular adaptive executor CTE, should work since -1 disables the limit -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT users_table.user_id, users_table.value_1, users_table.value_2 @@ -183,7 +180,7 @@ WITH cte AS on (users_table.value_2=events_table.value_2) ), -cte2 AS ( +cte2 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT @@ -196,18 +193,18 @@ LIMIT 10; -- regular real-time CTE fetches around ~4kb data in each subplan -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT * FROM users_table ), -cte2 AS ( +cte2 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT cte.user_id, cte.value_2 FROM cte,cte2 ORDER BY 1,2 LIMIT 10; -- regular real-time query fetches ~4kB -WITH cte AS +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id IN (1,2,3,4,5) ) @@ -215,11 +212,11 @@ SELECT * FROM cte ORDER BY 1,2,3,4,5 LIMIT 10; -- nested CTEs -WITH cte AS ( - WITH cte2 AS ( +WITH cte AS MATERIALIZED ( + WITH cte2 AS MATERIALIZED ( SELECT * FROM users_table ), - cte3 AS ( + cte3 AS MATERIALIZED ( SELECT * FROM events_table ) SELECT diff --git a/src/test/regress/sql/locally_execute_intermediate_results.sql b/src/test/regress/sql/locally_execute_intermediate_results.sql index bc0c1e04f..526f142be 100644 --- a/src/test/regress/sql/locally_execute_intermediate_results.sql +++ b/src/test/regress/sql/locally_execute_intermediate_results.sql @@ -25,16 +25,14 @@ INSERT INTO table_2 VALUES (3, '3'), (4, '4'), (5, '5'), INSERT INTO ref_table VALUES (1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), (6, '6'); INSERT INTO local_table VALUES (3, '3'), (4, '4'), (5, '5'), (6, '6'); --- prevent PG 11 - PG 12 outputs to diverge -- and have a lot more CTEs recursively planned for the -- sake of increasing the test coverage -SET citus.enable_cte_inlining TO false; SET client_min_messages TO DEBUG1; -- the query cannot be executed locally, but still because of -- HAVING the intermediate result is written to local file as well -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -43,7 +41,7 @@ GROUP BY value HAVING max(value) > (SELECT max FROM cte_1); -- in this case, the HAVING Is also pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -52,8 +50,8 @@ GROUP BY key HAVING max(value) > (SELECT max FROM cte_1); -- subquery in the WHERE part of the query can be executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -65,8 +63,8 @@ HAVING max(value) > (SELECT max FROM cte_1); -- subquery in the WHERE part of the query should not be executed locally -- because it can be pushed down with the jointree -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2) SELECT count(*) FROM @@ -77,9 +75,9 @@ GROUP BY key HAVING max(value) > (SELECT max FROM cte_1); -- now all the intermediate results are safe to be in local files -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -91,8 +89,8 @@ HAVING max(value) > (SELECT max FROM cte_1); -- multiple CTEs are joined inside HAVING, so written to file -- locally, but nothing executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -101,8 +99,8 @@ GROUP BY value HAVING max(value) > (SELECT max FROM cte_1 JOIN cte_2 USING (max)); -- same as above, but HAVING pushed down to workers -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -113,9 +111,9 @@ HAVING max(value) > (SELECT max FROM cte_1 JOIN cte_2 USING (max)); -- multiple CTEs are joined inside HAVING, so written to file -- locally, also the join tree contains only another CTE, so should be -- executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -126,7 +124,7 @@ HAVING max(value) > (SELECT max FROM cte_1 JOIN cte_2 USING (max)); -- now, the CTE is going to be written locally, -- plus that is going to be read locally because -- of the aggragate over the cte in HAVING -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -135,7 +133,7 @@ GROUP BY value HAVING max(value) > (SELECT max(max) FROM cte_1); -- same as above, but with HAVING pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -144,8 +142,8 @@ GROUP BY key HAVING max(value) > (SELECT max(max) FROM cte_1); -- two ctes are going to be written locally and executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*) FROM @@ -155,7 +153,7 @@ HAVING max(value) < (SELECT max(max) FROM cte_1); -- this time the same CTE is both joined with a distributed -- table and used in HAVING -WITH a AS (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) +WITH a AS MATERIALIZED (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -165,7 +163,7 @@ HAVING (max(table_2.value) > (SELECT value FROM a)); -- this time the same CTE is both joined with a distributed -- table and used in HAVING -- but used in another subquery/aggregate -- so one more level of recursive planning -WITH a AS (SELECT * FROM table_1) +WITH a AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -173,7 +171,7 @@ GROUP BY key HAVING (max(table_2.value) = (SELECT max(value) FROM a)); -- same query as the above, without the aggragate -WITH a AS (SELECT max(key) as key, max(value) as value FROM ref_table) +WITH a AS MATERIALIZED (SELECT max(key) as key, max(value) as value FROM ref_table) SELECT count(*), key FROM a JOIN ref_table USING (key) @@ -184,28 +182,28 @@ HAVING (max(ref_table.value) <= (SELECT value FROM a)); -- some edge cases around CTEs used inside other CTEs -- everything can be executed locally -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) FROM cte_2) SELECT * FROM cte_3; -- the join between cte_3 and table_2 has to happen remotely -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 1; -- the join between cte_3 and table_2 has to happen remotely -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN ref_table USING (key); -- some cases around router queries -- a router query, but the having has two cte joins -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 WHERE KEY = 3 @@ -215,9 +213,9 @@ HAVING max(value) > -- a router query, but the having has two cte joins -- and the jointree has a join with another cte -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) WHERE KEY = 3 @@ -227,9 +225,9 @@ HAVING max(table_2.value) > -- a router query, but the having has two cte joins -- and the jointree has a join with the same CTEs -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) JOIN cte_2 ON (key = MAX::int) JOIN cte_1 USING(MAX) WHERE KEY = 3 @@ -238,8 +236,8 @@ HAVING max(table_2.value) > (SELECT MAX FROM cte_1 JOIN cte_2 USING (MAX)); -- subPlans needed remotely as the subquery is pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT key FROM table_1 GROUP BY key HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT key FROM table_2 GROUP BY key HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -247,31 +245,31 @@ SELECT * FROM -- the second subquery needs to be recursively planned due to non-colocated subquery join -- so cte_2 becomes part of master query of that recursive subquery planning -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2)) as bar WHERE foo.key != bar.key; -- similar to above, but having pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT key FROM table_1 GROUP BY key HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT key FROM table_2 GROUP BY key HAVING max(value) > (SELECT * FROM cte_2)) as bar WHERE foo.key != bar.key; -- now, forcing all subqueries to be on the local node -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1) LIMIT 1) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2) LIMIT 1) as bar WHERE foo.key != bar.key; -- queries in which the last step has only CTEs can use local tables -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -279,8 +277,8 @@ local_table GROUP BY key HAVING max(value) > (SELECT max FROM cte_1); -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -298,14 +296,9 @@ SET citus.log_intermediate_results TO TRUE; SET citus.log_local_commands TO TRUE; SET client_min_messages TO DEBUG1; --- prevent PG 11 - PG 12 outputs to diverge --- and have a lot more CTEs recursively planned for the --- sake of increasing the test coverage -SET citus.enable_cte_inlining TO false; - -- the query cannot be executed locally, but still because of -- HAVING the intermediate result is written to local file as well -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -315,8 +308,8 @@ HAVING max(value) > (SELECT max FROM cte_1); -- On non-mx case the subquery in the WHERE part of the query can be executed locally -- however, on Citus MX we have this limitation where the query cannot be executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -328,8 +321,8 @@ HAVING max(value) > (SELECT max FROM cte_1); -- subquery in the WHERE part of the query should not be executed locally -- because it can be pushed down with the jointree -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2) SELECT count(*) FROM @@ -341,9 +334,9 @@ HAVING max(value) > (SELECT max FROM cte_1); -- although all the intermediate results are safe to be in local files -- we currently do not support it on Citus MX -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -355,8 +348,8 @@ HAVING max(value) > (SELECT max FROM cte_1); -- multiple CTEs are joined inside HAVING, so written to file -- locally, but nothing executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -368,9 +361,9 @@ HAVING max(value) > (SELECT max FROM cte_1 JOIN cte_2 USING (max)); -- multiple CTEs are joined inside HAVING, so written to file -- locally, also the join tree contains only another CTE, so should be -- executed locally, but not on an Citus MX worker -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -382,7 +375,7 @@ HAVING max(value) > (SELECT max FROM cte_1 JOIN cte_2 USING (max)); -- plus that could have been read locally on the coordinator -- because of the aggragate over the cte in HAVING -- but not on Citus MX -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -393,8 +386,8 @@ HAVING max(value) > (SELECT max(max) FROM cte_1); -- two could have been written locally and executed locally -- on the coordinator, but not on the workers -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*) FROM @@ -404,7 +397,7 @@ HAVING max(value) < (SELECT max(max) FROM cte_1); -- this time the same CTE is both joined with a distributed -- table and used in HAVING -WITH a AS (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) +WITH a AS MATERIALIZED (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -414,7 +407,7 @@ HAVING (max(table_2.value) > (SELECT value FROM a)); -- this time the same CTE is both joined with a distributed -- table and used in HAVING -- but used in another subquery/aggregate -- so one more level of recursive planning -WITH a AS (SELECT * FROM table_1) +WITH a AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -422,7 +415,7 @@ GROUP BY key HAVING (max(table_2.value) = (SELECT max(value) FROM a)); -- same query as the above, without the aggragate -WITH a AS (SELECT max(key) as key, max(value) as value FROM ref_table) +WITH a AS MATERIALIZED (SELECT max(key) as key, max(value) as value FROM ref_table) SELECT count(*), key FROM a JOIN ref_table USING (key) @@ -434,39 +427,39 @@ HAVING (max(ref_table.value) <= (SELECT value FROM a)); -- everything could be executed locally on the coordinator, -- but not on the worker -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) FROM cte_2) SELECT * FROM cte_3; -- the join between cte_3 and table_2 has to could have happened -- locally since the key = 1 resides on this node -- but because of the current implementation limitations we can't -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 1; -- the join between cte_3 and table_2 has to cannot happen -- locally because the key = 2 resides on a remote node -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 2; -- the join between cte_3 and ref can could have happened locally -- but because of the current implementation limitations we can't -WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +WITH cte_1 as MATERIALIZED (SELECT * FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN ref_table USING (key); -- some cases around router queries -- a router query, but the having has two cte joins -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 WHERE KEY = 3 @@ -476,9 +469,9 @@ HAVING max(value) > -- a router query, but the having has two cte joins -- and the jointree has a join with another cte -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) WHERE KEY = 3 @@ -488,9 +481,9 @@ HAVING max(table_2.value) > -- a router query, but the having has two cte joins -- and the jointree has a join with the same CTEs -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) JOIN cte_2 ON (key = MAX::int) JOIN cte_1 USING(MAX) WHERE KEY = 3 @@ -498,8 +491,8 @@ GROUP BY table_2.KEY HAVING max(table_2.value) > (SELECT MAX FROM cte_1 JOIN cte_2 USING (MAX)); -- subPlans needed remotely as the subquery is pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT key FROM table_1 GROUP BY key HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT key FROM table_2 GROUP BY key HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -507,8 +500,8 @@ SELECT * FROM -- the second subquery needs to be recursively planned due to non-colocated subquery join -- so cte_2 becomes part of master query of that recursive subquery planning -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -516,8 +509,8 @@ SELECT * FROM -- now, forcing all subqueries to be on the local node -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1) LIMIT 1) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2) LIMIT 1) as bar @@ -527,7 +520,7 @@ SELECT * FROM set citus.task_assignment_policy TO "round-robin" ; -- the query cannot be executed locally, but still because of -- HAVING the intermediate result is written to local file as well -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -537,8 +530,8 @@ HAVING max(value) > (SELECT max FROM cte_1); -- On non-mx case the subquery in the WHERE part of the query can be executed locally -- however, on Citus MX we have this limitation where the query cannot be executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -550,8 +543,8 @@ HAVING max(value) > (SELECT max FROM cte_1); -- subquery in the WHERE part of the query should not be executed locally -- because it can be pushed down with the jointree -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2) SELECT count(*) FROM @@ -563,9 +556,9 @@ HAVING max(value) > (SELECT max FROM cte_1); -- although all the intermediate results are safe to be in local files -- we currently do not support it on Citus MX -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(key) FROM table_2), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(key) FROM table_2), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -577,8 +570,8 @@ HAVING max(value) > (SELECT max FROM cte_1); -- multiple CTEs are joined inside HAVING, so written to file -- locally, but nothing executed locally -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -590,9 +583,9 @@ HAVING max(value) > (SELECT max FROM cte_1 JOIN cte_2 USING (max)); -- multiple CTEs are joined inside HAVING, so written to file -- locally, also the join tree contains only another CTE, so should be -- executed locally, but not on an Citus MX worker -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT max(value) FROM table_1), -cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM @@ -604,7 +597,7 @@ HAVING max(value) > (SELECT max FROM cte_1 JOIN cte_2 USING (max)); -- plus that could have been read locally on the coordinator -- because of the aggragate over the cte in HAVING -- but not on Citus MX -WITH cte_1 AS (SELECT max(value) FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1) SELECT count(*) FROM @@ -615,8 +608,8 @@ HAVING max(value) > (SELECT max(max) FROM cte_1); -- two could have been written locally and executed locally -- on the coordinator, but not on the workers -WITH cte_1 AS (SELECT max(value) FROM table_1), -cte_2 AS (SELECT * FROM table_1) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), +cte_2 AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*) FROM @@ -626,7 +619,7 @@ HAVING max(value) < (SELECT max(max) FROM cte_1); -- this time the same CTE is both joined with a distributed -- table and used in HAVING -WITH a AS (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) +WITH a AS MATERIALIZED (SELECT * FROM table_1 ORDER BY 1,2 DESC LIMIT 1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -636,7 +629,7 @@ HAVING (max(table_2.value) > (SELECT value FROM a)); -- this time the same CTE is both joined with a distributed -- table and used in HAVING -- but used in another subquery/aggregate -- so one more level of recursive planning -WITH a AS (SELECT * FROM table_1) +WITH a AS MATERIALIZED (SELECT * FROM table_1) SELECT count(*), key FROM a JOIN table_2 USING (key) @@ -644,7 +637,7 @@ GROUP BY key HAVING (max(table_2.value) = (SELECT max(value) FROM a)); -- same query as the above, without the aggragate -WITH a AS (SELECT max(key) as key, max(value) as value FROM ref_table) +WITH a AS MATERIALIZED (SELECT max(key) as key, max(value) as value FROM ref_table) SELECT count(*), key FROM a JOIN ref_table USING (key) @@ -657,38 +650,38 @@ HAVING (max(ref_table.value) <= (SELECT value FROM a)); -- everything could be executed locally on the coordinator, -- but not on the worker WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) FROM cte_2) +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) FROM cte_2) SELECT * FROM cte_3; -- the join between cte_3 and table_2 has to could have happened -- locally since the key = 1 resides on this node -- but because of the current implementation limitations we can't WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 1; -- the join between cte_3 and table_2 has to cannot happen -- locally because the key = 2 resides on a remote node WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN table_2 USING (key) WHERE table_2.key = 2; -- the join between cte_3 and ref can could have happened locally -- but because of the current implementation limitations we can't WITH cte_1 as (SELECT * FROM table_1), -cte_2 AS (SELECT * FROM cte_1), -cte_3 AS (SELECT max(key) as key FROM cte_2) +cte_2 AS MATERIALIZED (SELECT * FROM cte_1), +cte_3 AS MATERIALIZED (SELECT max(key) as key FROM cte_2) SELECT * FROM cte_3 JOIN ref_table USING (key); -- some cases around router queries -- a router query, but the having has two cte joins -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 WHERE KEY = 3 @@ -698,9 +691,9 @@ HAVING max(value) > -- a router query, but the having has two cte joins -- and the jointree has a join with another cte -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) WHERE KEY = 3 @@ -709,9 +702,9 @@ HAVING max(table_2.value) > (SELECT MAX FROM cte_1 JOIN cte_2 USING (MAX)); -- the same query as above, try to hit local node with either of the queries -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) WHERE KEY = 3 @@ -722,9 +715,9 @@ HAVING max(table_2.value) > -- a router query, but the having has two cte joins -- and the jointree has a join with the same CTEs -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_1), - cte_3 AS (SELECT * FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_3 AS MATERIALIZED (SELECT * FROM table_2) SELECT count(*) FROM table_2 JOIN cte_3 USING(key) JOIN cte_2 ON (key = MAX::int) JOIN cte_1 USING(MAX) WHERE KEY = 3 @@ -733,8 +726,8 @@ HAVING max(table_2.value) > (SELECT MAX FROM cte_1 JOIN cte_2 USING (MAX)); - subPlans needed remotely as the subquery is pushed down -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT key FROM table_1 GROUP BY key HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT key FROM table_2 GROUP BY key HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -742,8 +735,8 @@ SELECT * FROM -- the second subquery needs to be recursively planned due to non-colocated subquery join -- so cte_2 becomes part of master query of that recursive subquery planning -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1)) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2)) as bar @@ -751,8 +744,8 @@ SELECT * FROM -- now, forcing all subqueries to be on the local node -WITH cte_1 AS (SELECT max(value) FROM table_1), - cte_2 AS (SELECT max(value) FROM table_2) +WITH cte_1 AS MATERIALIZED (SELECT max(value) FROM table_1), + cte_2 AS MATERIALIZED (SELECT max(value) FROM table_2) SELECT * FROM (SELECT value AS key FROM table_1 GROUP BY value HAVING max(value) > (SELECT * FROM cte_1) LIMIT 1) as foo, (SELECT value AS key FROM table_2 GROUP BY value HAVING max(value) > (SELECT * FROM cte_2) LIMIT 1) as bar diff --git a/src/test/regress/sql/mixed_relkind_tests.sql b/src/test/regress/sql/mixed_relkind_tests.sql index dbefbb366..e4c7e6624 100644 --- a/src/test/regress/sql/mixed_relkind_tests.sql +++ b/src/test/regress/sql/mixed_relkind_tests.sql @@ -12,7 +12,6 @@ SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); RESET client_min_messages; -- make results consistent -SET citus.enable_cte_inlining TO OFF; -- create test tables CREATE TABLE postgres_local_table (a int); @@ -156,16 +155,16 @@ SELECT COUNT(*) FROM unlogged_distributed_table u1 JOIN partitioned_distributed_ RESET citus.enable_repartition_joins; -- joins with cte's -WITH cte_1 AS (SELECT * FROM partitioned_distributed_table) +WITH cte_1 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table) SELECT COUNT(*) FROM cte_1; -WITH cte_1 AS (SELECT * FROM partitioned_distributed_table) +WITH cte_1 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table) SELECT COUNT(*) FROM cte_1 JOIN partitioned_distributed_table USING (a); -WITH cte_1 AS (SELECT * FROM foreign_distributed_table) +WITH cte_1 AS MATERIALIZED (SELECT * FROM foreign_distributed_table) SELECT COUNT(*) FROM cte_1 JOIN foreign_distributed_table USING (a); -WITH cte_1 AS (SELECT * FROM partitioned_distributed_table) +WITH cte_1 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table) SELECT COUNT(*) FROM cte_1 JOIN partitioned_distributed_table USING (b); -- multi shard colocated update @@ -182,8 +181,8 @@ FROM (SELECT * FROM unlogged_distributed_table WHERE b = 1) AS sub1, WHERE sub1.a = sub2.a AND sub1.a = dt.a AND dt.a > 1; -- multi shard non-colocated update -WITH cte1 AS (SELECT * FROM partitioned_distributed_table WHERE b = 1), - cte2 AS (SELECT * FROM partitioned_distributed_table WHERE b = 2) +WITH cte1 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table WHERE b = 1), + cte2 AS MATERIALIZED (SELECT * FROM partitioned_distributed_table WHERE b = 2) UPDATE partitioned_distributed_table dt SET b = cte1.a + cte2.a FROM cte1, cte2 WHERE cte1.a != cte2.a AND cte1.a = dt.a AND dt.a > 1; diff --git a/src/test/regress/sql/multi_explain.sql b/src/test/regress/sql/multi_explain.sql index 3a12c7093..1840fa208 100644 --- a/src/test/regress/sql/multi_explain.sql +++ b/src/test/regress/sql/multi_explain.sql @@ -587,19 +587,16 @@ INSERT INTO lineitem_hash_part ( SELECT s FROM generate_series(5,10) s); -- explain with recursive planning --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; EXPLAIN (COSTS OFF, VERBOSE true) -WITH keys AS ( +WITH keys AS MATERIALIZED ( SELECT DISTINCT l_orderkey FROM lineitem_hash_part ), -series AS ( +series AS MATERIALIZED ( SELECT s FROM generate_series(1,10) s ) SELECT l_orderkey FROM series JOIN keys ON (s = l_orderkey) ORDER BY s; -SET citus.enable_cte_inlining TO true; SELECT true AS valid FROM explain_json($$ WITH result AS ( diff --git a/src/test/regress/sql/multi_insert_select.sql b/src/test/regress/sql/multi_insert_select.sql index 2ab82dad4..2f7136037 100644 --- a/src/test/regress/sql/multi_insert_select.sql +++ b/src/test/regress/sql/multi_insert_select.sql @@ -515,11 +515,8 @@ INSERT INTO agg_events (value_1_agg, user_id) SELECT user_id, value_1_agg FROM agg_events ORDER BY 1,2; -- We support CTEs --- but prefer to prevent inlining of the CTE --- in order not to diverge from pg 11 vs pg 12 BEGIN; -SET LOCAL citus.enable_cte_inlining TO false; -WITH fist_table_agg AS +WITH fist_table_agg AS MATERIALIZED (SELECT max(value_1)+1 as v1_agg, user_id FROM raw_events_first GROUP BY user_id) INSERT INTO agg_events (value_1_agg, user_id) diff --git a/src/test/regress/sql/multi_insert_select_conflict.sql b/src/test/regress/sql/multi_insert_select_conflict.sql index bcbe278e9..ab05e9659 100644 --- a/src/test/regress/sql/multi_insert_select_conflict.sql +++ b/src/test/regress/sql/multi_insert_select_conflict.sql @@ -103,24 +103,20 @@ WITH inserted_table AS ( ON CONFLICT(col_1) DO UPDATE SET col_2 = 0 RETURNING * ) SELECT * FROM inserted_table ORDER BY 1; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; - -- Get the select part from cte and do nothing on conflict -WITH cte AS( +WITH cte AS MATERIALIZED ( SELECT col_1, col_2 FROM source_table_1 ) INSERT INTO target_table SELECT * FROM cte ON CONFLICT DO NOTHING; -- Get the select part from cte and update on conflict -WITH cte AS( +WITH cte AS MATERIALIZED ( SELECT col_1, col_2 FROM source_table_1 ) INSERT INTO target_table SELECT * FROM cte ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1; SELECT * FROM target_table ORDER BY 1; -SET citus.enable_cte_inlining TO true; -- Test with multiple CTEs WITH cte AS( @@ -131,20 +127,17 @@ WITH cte AS( INSERT INTO target_table ((SELECT * FROM cte) UNION (SELECT * FROM cte_2)) ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1; SELECT * FROM target_table ORDER BY 1; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; - -WITH inserted_table AS ( - WITH cte AS( +WITH inserted_table AS MATERIALIZED ( + WITH cte AS MATERIALIZED ( SELECT col_1, col_2, col_3 FROM source_table_1 - ), cte_2 AS( + ), cte_2 AS MATERIALIZED ( SELECT col_1, col_2 FROM cte ) INSERT INTO target_table SELECT * FROM cte_2 ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1 RETURNING * ) SELECT * FROM inserted_table ORDER BY 1; -WITH cte AS ( - WITH basic AS ( +WITH cte AS MATERIALIZED ( + WITH basic AS MATERIALIZED ( SELECT col_1, col_2 FROM source_table_1 ) INSERT INTO target_table (SELECT * FROM basic) ON CONFLICT DO NOTHING RETURNING * @@ -170,7 +163,6 @@ FROM cte, source_table_1 WHERE cte.col_1 = source_table_1.col_1 ON CONFLICT DO NOTHING; $Q$); -SET citus.enable_cte_inlining TO true; -- Tests with foreign key to reference table CREATE TABLE test_ref_table (key int PRIMARY KEY); @@ -303,12 +295,10 @@ FROM ( ON CONFLICT(col_1) DO UPDATE SET col_2 = 0; SELECT * FROM target_table ORDER BY 1; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -WITH cte AS( +WITH cte AS MATERIALIZED( SELECT col_1, col_2, col_3 FROM source_table_1 -), cte_2 AS( +), cte_2 AS MATERIALIZED( SELECT col_1, col_2 FROM cte ) INSERT INTO target_table SELECT * FROM cte_2 ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1; diff --git a/src/test/regress/sql/multi_mx_router_planner.sql b/src/test/regress/sql/multi_mx_router_planner.sql index 504c0d602..e8efbe244 100644 --- a/src/test/regress/sql/multi_mx_router_planner.sql +++ b/src/test/regress/sql/multi_mx_router_planner.sql @@ -6,9 +6,7 @@ -- run all the router queries from the one of the workers --- prevent PG 11 - PG 12 outputs to diverge -- and CTE inlining is not relevant to router plannery anyway -SET citus.enable_cte_inlining TO false; \c - - - :worker_1_port -- this table is used in a CTE test diff --git a/src/test/regress/sql/multi_router_planner.sql b/src/test/regress/sql/multi_router_planner.sql index d70388b95..ed11f1ea5 100644 --- a/src/test/regress/sql/multi_router_planner.sql +++ b/src/test/regress/sql/multi_router_planner.sql @@ -10,10 +10,6 @@ SET citus.next_shard_id TO 840000; -- other tests that triggers fast-path-router planner SET citus.enable_fast_path_router_planner TO false; --- prevent PG 11 - PG 12 outputs to diverge --- and CTE inlining is not relevant to router plannery anyway -SET citus.enable_cte_inlining TO false; - CREATE TABLE articles_hash ( id bigint NOT NULL, author_id bigint NOT NULL, @@ -167,12 +163,12 @@ SELECT * FROM articles_hash WHERE author_id IN (1, 3) ORDER BY id; SELECT * FROM articles_hash WHERE author_id IN (1, NULL) ORDER BY id; -- queries with CTEs are supported -WITH first_author AS ( SELECT id FROM articles_hash WHERE author_id = 1) +WITH first_author AS MATERIALIZED ( SELECT id FROM articles_hash WHERE author_id = 1) SELECT * FROM first_author; -- SELECT FOR UPDATE is supported if not involving reference table BEGIN; -WITH first_author AS ( +WITH first_author AS MATERIALIZED ( SELECT articles_hash.id, auref.name FROM articles_hash, authors_reference auref WHERE author_id = 2 AND auref.id = author_id FOR UPDATE @@ -180,7 +176,7 @@ WITH first_author AS ( UPDATE articles_hash SET title = first_author.name FROM first_author WHERE articles_hash.author_id = 2 AND articles_hash.id = first_author.id; -WITH first_author AS ( +WITH first_author AS MATERIALIZED ( SELECT id, word_count FROM articles_hash WHERE author_id = 2 FOR UPDATE ) @@ -188,7 +184,7 @@ UPDATE articles_hash SET title = first_author.word_count::text FROM first_author WHERE articles_hash.author_id = 2 AND articles_hash.id = first_author.id; -- Without FOR UPDATE this is router plannable -WITH first_author AS ( +WITH first_author AS MATERIALIZED ( SELECT articles_hash.id, auref.name FROM articles_hash, authors_reference auref WHERE author_id = 2 AND auref.id = author_id ) @@ -197,21 +193,21 @@ FROM first_author WHERE articles_hash.author_id = 2 AND articles_hash.id = first ROLLBACK; -- queries with CTEs are supported even if CTE is not referenced inside query -WITH first_author AS ( SELECT id FROM articles_hash WHERE author_id = 1) +WITH first_author AS MATERIALIZED ( SELECT id FROM articles_hash WHERE author_id = 1) SELECT title FROM articles_hash WHERE author_id = 1; -- two CTE joins are supported if they go to the same worker -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 1) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 1) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 3) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 3) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; -- CTE joins are supported because they are both planned recursively -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 2) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 2) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; -- recursive CTEs are supported when filtered on partition column @@ -230,7 +226,7 @@ INSERT INTO company_employees values(3, 15, 1); INSERT INTO company_employees values(3, 3, 1); -- find employees at top 2 level within company hierarchy -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 @@ -244,7 +240,7 @@ SELECT * FROM hierarchy WHERE LEVEL <= 2; -- query becomes not router plannble and gets rejected -- if filter on company is dropped -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 @@ -257,7 +253,7 @@ SELECT * FROM hierarchy WHERE LEVEL <= 2; -- logically wrong query, query involves different shards -- from the same table -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 3 and manager_id = 0 @@ -270,39 +266,39 @@ WITH RECURSIVE hierarchy as ( SELECT * FROM hierarchy WHERE LEVEL <= 2; -- Test router modifying CTEs -WITH new_article AS ( +WITH new_article AS MATERIALIZED( INSERT INTO articles_hash VALUES (1, 1, 'arsenous', 9) RETURNING * ) SELECT * FROM new_article; -WITH update_article AS ( +WITH update_article AS MATERIALIZED( UPDATE articles_hash SET word_count = 10 WHERE id = 1 AND word_count = 9 RETURNING * ) SELECT * FROM update_article; -WITH update_article AS ( +WITH update_article AS MATERIALIZED ( UPDATE articles_hash SET word_count = 11 WHERE id = 1 AND word_count = 10 RETURNING * ) SELECT coalesce(1,random()); -WITH update_article AS ( +WITH update_article AS MATERIALIZED ( UPDATE articles_hash SET word_count = 10 WHERE author_id = 1 AND id = 1 AND word_count = 11 RETURNING * ) SELECT coalesce(1,random()); -WITH update_article AS ( +WITH update_article AS MATERIALIZED ( UPDATE authors_reference SET name = '' WHERE id = 0 RETURNING * ) SELECT coalesce(1,random()); -WITH delete_article AS ( +WITH delete_article AS MATERIALIZED ( DELETE FROM articles_hash WHERE id = 1 AND word_count = 10 RETURNING * ) SELECT * FROM delete_article; -- Modifying statement in nested CTE case is covered by PostgreSQL itself -WITH new_article AS ( - WITH nested_cte AS ( +WITH new_article AS MATERIALIZED( + WITH nested_cte AS MATERIALIZED( INSERT INTO articles_hash VALUES (1, 1, 'arsenous', 9572) RETURNING * ) SELECT * FROM nested_cte @@ -311,7 +307,7 @@ SELECT * FROM new_article; -- Modifying statement in a CTE in subquery is also covered by PostgreSQL SELECT * FROM ( - WITH new_article AS ( + WITH new_article AS MATERIALIZED ( INSERT INTO articles_hash VALUES (1, 1, 'arsenous', 9572) RETURNING * ) SELECT * FROM new_article @@ -399,7 +395,7 @@ SELECT a.author_id as first_author, b.word_count as second_word_count -- following join is not router plannable since there are no -- workers containing both shards, but will work through recursive -- planning -WITH single_shard as (SELECT * FROM articles_single_shard_hash) +WITH single_shard as MATERIALIZED(SELECT * FROM articles_single_shard_hash) SELECT a.author_id as first_author, b.word_count as second_word_count FROM articles_hash a, single_shard b WHERE a.author_id = 2 and a.author_id = b.author_id @@ -747,14 +743,14 @@ SELECT s.datid FROM number1() s LEFT JOIN pg_database d ON s.datid = d.oid; SELECT 1 FROM authors_reference r JOIN num_db ON (r.id = num_db.datid) LIMIT 1; -- with a CTE in a view -WITH cte AS (SELECT * FROM num_db) +WITH cte AS MATERIALIZED (SELECT * FROM num_db) SELECT 1 FROM authors_reference r JOIN cte ON (r.id = cte.datid) LIMIT 1; -- hide changes between major versions RESET client_min_messages; -- with pg_stat_activity view -WITH pg_stat_activity AS ( +WITH pg_stat_activity AS MATERIALIZED( SELECT pg_stat_activity.datid, pg_stat_activity.application_name, @@ -770,18 +766,18 @@ SET client_min_messages TO DEBUG2; \set VERBOSITY terse RESET client_min_messages; -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 1 and 1=0) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 1 and 1=0) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id; -WITH id_author AS ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), -id_title AS (SELECT id, title from articles_hash WHERE author_id = 1) +WITH id_author AS MATERIALIZED ( SELECT id, author_id FROM articles_hash WHERE author_id = 1), +id_title AS MATERIALIZED (SELECT id, title from articles_hash WHERE author_id = 1) SELECT * FROM id_author, id_title WHERE id_author.id = id_title.id and 1=0; SET client_min_messages TO DEBUG2; \set VERBOSITY DEFAULT -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 @@ -793,7 +789,7 @@ WITH RECURSIVE hierarchy as ( ce.company_id = 1)) SELECT * FROM hierarchy WHERE LEVEL <= 2 and 1=0; -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 @@ -805,7 +801,7 @@ WITH RECURSIVE hierarchy as ( ce.company_id = 1 AND 1=0)) SELECT * FROM hierarchy WHERE LEVEL <= 2; -WITH RECURSIVE hierarchy as ( +WITH RECURSIVE hierarchy as MATERIALIZED ( SELECT *, 1 AS level FROM company_employees WHERE company_id = 1 and manager_id = 0 AND 1=0 diff --git a/src/test/regress/sql/multi_task_assignment_policy.sql b/src/test/regress/sql/multi_task_assignment_policy.sql index 2ed20493a..2d9088ee0 100644 --- a/src/test/regress/sql/multi_task_assignment_policy.sql +++ b/src/test/regress/sql/multi_task_assignment_policy.sql @@ -240,16 +240,14 @@ SET citus.task_assignment_policy TO 'round-robin'; -- Run the query two times to make sure that it hits two different workers -- on consecutive runs --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; INSERT INTO explain_outputs SELECT parse_explain_output($cmd$ -EXPLAIN WITH q1 AS (SELECT * FROM task_assignment_test_table_2) SELECT * FROM q1 +EXPLAIN WITH q1 AS MATERIALIZED (SELECT * FROM task_assignment_test_table_2) SELECT * FROM q1 $cmd$, 'task_assignment_test_table_2'); INSERT INTO explain_outputs SELECT parse_explain_output($cmd$ -EXPLAIN WITH q1 AS (SELECT * FROM task_assignment_test_table_2) SELECT * FROM q1 +EXPLAIN WITH q1 AS MATERIALIZED (SELECT * FROM task_assignment_test_table_2) SELECT * FROM q1 $cmd$, 'task_assignment_test_table_2'); -- The count should be 2 since the intermediate results are processed on diff --git a/src/test/regress/sql/non_colocated_subquery_joins.sql b/src/test/regress/sql/non_colocated_subquery_joins.sql index 925974ca9..37b22db5f 100644 --- a/src/test/regress/sql/non_colocated_subquery_joins.sql +++ b/src/test/regress/sql/non_colocated_subquery_joins.sql @@ -437,17 +437,14 @@ SELECT count(*) FROM q1, (SELECT users_table.user_id = events_table.value_2 AND event_type IN (1,2,3,4)) as bar WHERE bar.user_id = q1.user_id ;$$); -- subquery joins should work fine when used with CTEs --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; SELECT true AS valid FROM explain_json_2($$ - WITH q1 AS (SELECT user_id FROM users_table) + WITH q1 AS MATERIALIZED (SELECT user_id FROM users_table) SELECT count(*) FROM q1, (SELECT users_table.user_id, random() FROM users_table, events_table WHERE users_table.user_id = events_table.user_id AND event_type IN (1,2,3,4)) as bar WHERE bar.user_id = q1.user_id ;$$); -SET citus.enable_cte_inlining TO true; -- should work fine within UNIONs diff --git a/src/test/regress/sql/set_operation_and_local_tables.sql b/src/test/regress/sql/set_operation_and_local_tables.sql index 8930cce87..cbc024729 100644 --- a/src/test/regress/sql/set_operation_and_local_tables.sql +++ b/src/test/regress/sql/set_operation_and_local_tables.sql @@ -76,9 +76,7 @@ FROM SELECT * FROM test a WHERE x IN (SELECT x FROM test b UNION SELECT y FROM test c UNION SELECT y FROM local_test d) ORDER BY 1,2; -- same query with subquery in where is wrapped in CTE -SET citus.enable_cte_inlining TO off; SELECT * FROM test a WHERE x IN (WITH cte AS (SELECT x FROM test b UNION SELECT y FROM test c UNION SELECT y FROM local_test d) SELECT * FROM cte) ORDER BY 1,2; -RESET citus.enable_cte_inlining; -- supported since final step only has local table and intermediate result SELECT * FROM ((SELECT * FROM test) EXCEPT (SELECT * FROM test ORDER BY x LIMIT 1)) u JOIN local_test USING (x) ORDER BY 1,2; diff --git a/src/test/regress/sql/single_node.sql b/src/test/regress/sql/single_node.sql index a1d498d44..7cbb20fbd 100644 --- a/src/test/regress/sql/single_node.sql +++ b/src/test/regress/sql/single_node.sql @@ -290,11 +290,7 @@ CREATE INDEX single_node_i1 ON test(x); CREATE INDEX single_node_i2 ON test(x,y); REINDEX SCHEMA single_node; --- PG 11 does not support CONCURRENTLY --- and we do not want to add a new output --- file just for that. Enable the test --- once we remove PG_VERSION_11 ---REINDEX SCHEMA CONCURRENTLY single_node; +REINDEX SCHEMA CONCURRENTLY single_node; -- keep one of the indexes -- drop w/wout tx blocks @@ -474,7 +470,6 @@ SELECT * FROM test_2 WHERE z = (83, 'citus8.3')::new_type OR z = (82, 'citus8.2' -- final query is only intermediate result -- we want PG 11/12/13 behave consistently, the CTEs should be MATERIALIZED -SET citus.enable_cte_inlining TO FALSE; WITH cte_1 AS (SELECT * FROM test_2) SELECT * FROM cte_1 ORDER BY 1,2; -- final query is router query diff --git a/src/test/regress/sql/subquery_and_cte.sql b/src/test/regress/sql/subquery_and_cte.sql index cf908e15f..b8f33b96c 100644 --- a/src/test/regress/sql/subquery_and_cte.sql +++ b/src/test/regress/sql/subquery_and_cte.sql @@ -3,8 +3,6 @@ -- =================================================================== SET search_path TO subquery_and_ctes; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; CREATE TABLE users_table_local AS SELECT * FROM users_table; @@ -20,11 +18,11 @@ SET client_min_messages TO DEBUG1; -- CTEs are recursively planned, and subquery foo is also recursively planned -- final plan becomes a router plan -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -56,9 +54,9 @@ ORDER BY cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type LIMIT 5; -- CTEs aren't colocated, CTEs become intermediate results -WITH cte1 AS ( +WITH cte1 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 -), cte2 AS ( +), cte2 AS MATERIALIZED ( SELECT * FROM events_table WHERE user_id = 6 ) SELECT cte1.user_id, cte1.value_1, cte2.user_id, cte2.user_id @@ -83,27 +81,27 @@ UPDATE dist_table dt SET value = cte1.value_1 + cte2.event_type FROM cte1, cte2 WHERE cte1.user_id = dt.id AND dt.id = 1; -- all relations are not colocated, CTEs become intermediate results -WITH cte1 AS ( +WITH cte1 AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 -), cte2 AS ( +), cte2 AS MATERIALIZED ( SELECT * FROM events_table WHERE user_id = 6 ) UPDATE dist_table dt SET value = cte1.value_1 + cte2.event_type FROM cte1, cte2 WHERE cte1.user_id = dt.id AND dt.id = 1; -- volatile function calls should not be routed -WITH cte1 AS (SELECT id, value FROM func()) +WITH cte1 AS MATERIALIZED (SELECT id, value FROM func()) UPDATE dist_table dt SET value = cte1.value FROM cte1 WHERE dt.id = 1; -- CTEs are recursively planned, and subquery foo is also recursively planned -- final plan becomes a real-time plan since we also have events_table in the -- range table entries -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -126,11 +124,11 @@ FROM -- CTEs are replaced and subquery in WHERE is also replaced -- but the query is still real-time query since users_table is in the -- range table list -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -144,11 +142,11 @@ WHERE -- subquery in WHERE clause is planned recursively due to the recurring table -- in FROM clause -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -165,7 +163,7 @@ SELECT user_id FROM ( - WITH cte AS ( + WITH cte AS MATERIALIZED ( SELECT DISTINCT users_table.user_id FROM @@ -184,7 +182,7 @@ SELECT bar.user_id FROM ( - WITH cte AS ( + WITH cte AS MATERIALIZED ( SELECT DISTINCT users_table.user_id FROM @@ -213,7 +211,7 @@ SELECT DISTINCT bar.user_id FROM ( - WITH cte AS ( + WITH cte AS MATERIALIZED ( SELECT DISTINCT users_table.user_id FROM @@ -229,7 +227,7 @@ FROM FROM users_table, ( - WITH cte AS ( + WITH cte AS MATERIALIZED ( SELECT event_type, users_table.user_id FROM @@ -256,11 +254,11 @@ ORDER BY 1 DESC LIMIT 5; -- DISTINCT on a non-partition key SELECT * FROM ( - WITH cte AS ( - WITH local_cte AS ( + WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id @@ -281,11 +279,11 @@ LIMIT 5; -- now recursively plan subqueries inside the CTEs that contains LIMIT and OFFSET -WITH cte AS ( - WITH local_cte AS ( +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM @@ -318,11 +316,11 @@ SELECT FROM ( - WITH cte AS ( - WITH local_cte AS ( + WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM @@ -358,7 +356,7 @@ SELECT bar.user_id FROM ( - WITH RECURSIVE cte AS ( + WITH RECURSIVE cte AS MATERIALIZED ( SELECT DISTINCT users_table.user_id FROM @@ -428,7 +426,6 @@ SELECT count(*) FROM ( dist ON(dist.a = foo.a); -SET citus.enable_cte_inlining to true; WITH foo AS ( SELECT DISTINCT ref_table_1.a + 1 as a FROM ref_table_1 JOIN ref_table_2 ON (ref_table_1.a = ref_table_2.a) @@ -474,7 +471,6 @@ WITH foo AS ( ) SELECT count(*) FROM foo JOIN dist ON(dist.a = foo.a); -SET citus.enable_cte_inlining to false; -- We error-out when there's an error in execution of the query. By repeating it -- multiple times, we increase the chance of this test failing before PR #1903. diff --git a/src/test/regress/sql/subquery_in_where.sql b/src/test/regress/sql/subquery_in_where.sql index 5a383c927..90386122f 100644 --- a/src/test/regress/sql/subquery_in_where.sql +++ b/src/test/regress/sql/subquery_in_where.sql @@ -7,10 +7,8 @@ SET search_path TO subquery_in_where, public; SET client_min_messages TO DEBUG1; --CTEs can be used as a recurring tuple with subqueries in WHERE --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; WITH event_id - AS (SELECT user_id AS events_user_id, + AS MATERIALIZED (SELECT user_id AS events_user_id, time AS events_time, event_type FROM events_table) @@ -18,11 +16,10 @@ SELECT Count(*) FROM event_id WHERE events_user_id IN (SELECT user_id FROM users_table); -SET citus.enable_cte_inlining TO true; --Correlated subqueries can not be used in WHERE clause WITH event_id - AS (SELECT user_id AS events_user_id, + AS(SELECT user_id AS events_user_id, time AS events_time, event_type FROM events_table) diff --git a/src/test/regress/sql/subquery_view.sql b/src/test/regress/sql/subquery_view.sql index dbf391f93..40798fccd 100644 --- a/src/test/regress/sql/subquery_view.sql +++ b/src/test/regress/sql/subquery_view.sql @@ -319,11 +319,11 @@ LIMIT 5; CREATE VIEW subquery_and_ctes_second AS SELECT time, event_type, value_2, value_3 FROM ( - WITH cte AS ( - WITH local_cte AS ( + WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( SELECT * FROM users_table_local ), - dist_cte AS ( + dist_cte AS MATERIALIZED ( SELECT user_id FROM events_table ) SELECT dist_cte.user_id FROM local_cte join dist_cte on dist_cte.user_id=local_cte.user_id @@ -340,13 +340,10 @@ SELECT time, event_type, value_2, value_3 FROM WHERE foo.user_id = events_table.value_2; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; SELECT * FROM subquery_and_ctes_second ORDER BY 3 DESC, 2 DESC, 1 DESC LIMIT 5; -SET citus.enable_cte_inlining TO true; CREATE VIEW deep_subquery AS SELECT count(*) diff --git a/src/test/regress/sql/upgrade_pg_dist_object_test_before.sql b/src/test/regress/sql/upgrade_pg_dist_object_test_before.sql index 2244914fb..c918b6fee 100644 --- a/src/test/regress/sql/upgrade_pg_dist_object_test_before.sql +++ b/src/test/regress/sql/upgrade_pg_dist_object_test_before.sql @@ -37,8 +37,5 @@ SELECT create_reference_table('another_dist_table'); CREATE SCHEMA fooschema; CREATE TYPE fooschema.footype AS (x int, y int); -SELECT run_command_on_workers($$CREATE SCHEMA fooschema;$$); -SELECT run_command_on_workers($$CREATE TYPE fooschema.footype AS (x int, y int);$$); - CREATE TABLE fooschema.footable (f fooschema.footype); SELECT create_reference_table('fooschema.footable'); diff --git a/src/test/regress/sql/with_basics.sql b/src/test/regress/sql/with_basics.sql index 6c7d9f381..8c0bf2823 100644 --- a/src/test/regress/sql/with_basics.sql +++ b/src/test/regress/sql/with_basics.sql @@ -613,13 +613,12 @@ LEFT JOIN WHERE d1.user_id = d2.user_id )) AS bar USING (user_id); --- should work fine with cte inlinig disabled -SET citus.enable_cte_inlining TO false; -WITH distinct_undistribured AS ( +-- should work fine with materialized ctes +WITH distinct_undistribured AS MATERIALIZED ( SELECT DISTINCT user_id FROM test_cte ), - exsist_in_distributed AS ( + exsist_in_distributed AS MATERIALIZED ( SELECT DISTINCT user_id FROM test_cte_distributed WHERE EXISTS(SELECT * @@ -633,7 +632,7 @@ FROM ON distinct_undistribured.user_id = exsist_in_distributed.user_id ORDER BY 2 DESC, 1 DESC; -WITH distinct_undistribured AS +WITH distinct_undistribured AS MATERIALIZED (SELECT DISTINCT user_id FROM test_cte) SELECT * @@ -648,7 +647,7 @@ LEFT JOIN ON distinct_undistribured.user_id = exsist_in_distributed.user_id ORDER BY 2 DESC, 1 DESC; -WITH distinct_undistribured AS +WITH distinct_undistribured AS MATERIALIZED (SELECT DISTINCT user_id FROM test_cte) SELECT * @@ -662,11 +661,11 @@ LEFT JOIN WHERE distinct_undistribured.user_id = test_cte_distributed.user_id)) exsist_in_distributed ON distinct_undistribured.user_id = exsist_in_distributed.user_id; -- NOT EXISTS struct, with cte inlining disabled -WITH distinct_undistribured AS ( +WITH distinct_undistribured AS MATERIALIZED( SELECT DISTINCT user_id FROM test_cte ), - not_exsist_in_distributed AS ( + not_exsist_in_distributed AS MATERIALIZED ( SELECT DISTINCT user_id FROM test_cte_distributed WHERE NOT EXISTS(SELECT NULL @@ -682,7 +681,7 @@ ORDER BY 2 DESC, 1 DESC; -- similar query, but this time the second -- part of the query is not inside a CTE -WITH distinct_undistribured AS ( +WITH distinct_undistribured AS MATERIALIZED ( SELECT DISTINCT user_id FROM test_cte ) @@ -698,10 +697,9 @@ LEFT JOIN WHERE d1.user_id = d2.user_id )) AS bar USING (user_id); -- some test with failures -WITH a AS (SELECT * FROM users_table LIMIT 10) +WITH a AS MATERIALIZED (SELECT * FROM users_table LIMIT 10) SELECT user_id/0 FROM users_table JOIN a USING (user_id); -RESET citus.enable_cte_inlining; DROP VIEW basic_view; DROP VIEW cte_view; diff --git a/src/test/regress/sql/with_join.sql b/src/test/regress/sql/with_join.sql index 85c2c122a..54669d75f 100644 --- a/src/test/regress/sql/with_join.sql +++ b/src/test/regress/sql/with_join.sql @@ -44,7 +44,7 @@ WITH non_colocated_1 AS ( WHERE users_table.user_id = events_table.value_2 AND event_type IN (1, 2, 3) ), -non_colocated_2 AS ( +non_colocated_2 AS MATERIALIZED ( SELECT users_table.user_id FROM @@ -67,8 +67,8 @@ ORDER BY -- Subqueries in WHERE and FROM are mixed -- In this query, only subquery in WHERE is not a colocated join -- but we're able to recursively plan that as well -WITH users_events AS ( - WITH colocated_join AS ( +WITH users_events AS MATERIALIZED ( + WITH colocated_join AS MATERIALIZED ( SELECT users_table.user_id as uid, event_type FROM @@ -79,7 +79,7 @@ WITH users_events AS ( WHERE events_table.event_type IN (1, 2, 3) ), - colocated_join_2 AS ( + colocated_join_2 AS MATERIALIZED ( SELECT users_table.user_id, event_type FROM @@ -98,7 +98,7 @@ WITH users_events AS ( WHERE colocated_join.uid = colocated_join_2.user_id AND colocated_join.event_type IN ( - WITH some_events AS ( + WITH some_events AS MATERIALIZED ( SELECT event_type FROM @@ -128,9 +128,7 @@ LIMIT -- cte LEFT JOIN distributed_table should error out -- as long as the CTE is recursively planned --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 ORDER BY value_1 ) SELECT @@ -144,10 +142,9 @@ ORDER BY LIMIT 5; -SET citus.enable_cte_inlining TO true; -- cte RIGHT JOIN distributed_table should work -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 ORDER BY value_1 ) SELECT @@ -162,7 +159,7 @@ LIMIT 5; -- distributed_table LEFT JOIN cte should work -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE value_1 = 1 ORDER BY value_1 ) SELECT @@ -177,9 +174,7 @@ LIMIT 5; -- distributed_table RIGHT JOIN cte should error out --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE value_1 = 1 ORDER BY value_1 ) SELECT @@ -194,7 +189,7 @@ LIMIT 5; -- cte FULL JOIN distributed_table should error out -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT * FROM users_table WHERE user_id = 1 ORDER BY value_1 ) SELECT @@ -208,10 +203,9 @@ ORDER BY LIMIT 5; -SET citus.enable_cte_inlining TO false; -- Joins with reference tables are planned as router queries -WITH cte AS ( +WITH cte AS MATERIALIZED ( SELECT value_2, max(user_id) AS user_id FROM users_table WHERE value_2 = 1 GROUP BY value_2 HAVING count(*) > 1 ) SELECT @@ -248,19 +242,19 @@ LEFT JOIN reference_1 AS r1 ON d1.col2=r1.col2 LEFT JOIN reference_2 AS r2 ON r2.col1 = r1.col1 join (select distrib_col,count(*) from distributed_2 group by distrib_col) d2 ON d2.distrib_col=d1.distrib_col; -with d2 AS (select distrib_col,count(*) from distributed_2 group by distrib_col) +with d2 AS MATERIALIZED (select distrib_col,count(*) from distributed_2 group by distrib_col) select count(*) from distributed_1 AS d1 LEFT JOIN reference_1 AS r1 ON d1.col2=r1.col2 LEFT JOIN reference_2 AS r2 ON r2.col1 = r1.col1 join d2 ON d2.distrib_col=d1.distrib_col; -with d2 AS (select distrib_col,col1 from distributed_2) +with d2 AS MATERIALIZED (select distrib_col,col1 from distributed_2) select count(*) from distributed_1 AS d1 LEFT JOIN reference_1 AS r1 ON d1.col2=r1.col2 LEFT JOIN reference_2 AS r2 ON r2.col1 = r1.col1 join d2 ON d2.distrib_col=d1.distrib_col; -with cte_1 AS (select col1 from reference_1) +with cte_1 AS MATERIALIZED (select col1 from reference_1) select count(*) from distributed_1 AS d1 LEFT JOIN reference_1 AS r1 ON d1.col2=r1.col2 LEFT JOIN reference_2 AS r2 ON r2.col1 = r1.col1 diff --git a/src/test/regress/sql/with_modifying.sql b/src/test/regress/sql/with_modifying.sql index 0621136e1..ebdd91964 100644 --- a/src/test/regress/sql/with_modifying.sql +++ b/src/test/regress/sql/with_modifying.sql @@ -356,19 +356,17 @@ raw_data AS ( ) SELECT * FROM raw_data ORDER BY val; --- Needed becaues of CTE inlining triggering https://github.com/citusdata/citus/issues/3189 -SET citus.enable_cte_inlining TO FALSE; -WITH added_data AS ( +-- We materialize because of https://github.com/citusdata/citus/issues/3189 +WITH added_data AS MATERIALIZED ( INSERT INTO modify_table VALUES (1, trunc(10 * random())), (1, trunc(random())) RETURNING * ), -select_data AS ( +select_data AS MATERIALIZED ( SELECT val, now() FROM added_data WHERE id = 1 ), -raw_data AS ( +raw_data AS MATERIALIZED ( DELETE FROM modify_table WHERE id = 1 AND val IN (SELECT val FROM select_data) RETURNING * ) SELECT COUNT(*) FROM raw_data; -SET citus.enable_cte_inlining TO TRUE; WITH added_data AS ( INSERT INTO modify_table VALUES (1, trunc(10 * random())), (1, trunc(random())) RETURNING * diff --git a/src/test/regress/sql/with_set_operations.sql b/src/test/regress/sql/with_set_operations.sql index bbfe707a1..763456c9e 100644 --- a/src/test/regress/sql/with_set_operations.sql +++ b/src/test/regress/sql/with_set_operations.sql @@ -4,8 +4,6 @@ SET client_min_messages TO DEBUG1; --- prevent PG 11 - PG 12 outputs to diverge -SET citus.enable_cte_inlining TO false; -- use ctes inside unions on the top level WITH