diff --git a/contrib/amcheck/t/005_pitr.pl b/contrib/amcheck/t/005_pitr.pl new file mode 100644 index 00000000000..b776dd32917 --- /dev/null +++ b/contrib/amcheck/t/005_pitr.pl @@ -0,0 +1,96 @@ +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# Test integrity of intermediate states by PITR to those states +use strict; +use warnings; +use PostgresNode; +use TestLib; +use Test::More; + +# origin node: generate WAL records of interest. +my $origin = PostgresNode->new('origin'); +$origin->init(has_archiving => 1, allows_streaming => 1); +$origin->append_conf('postgresql.conf', 'autovacuum = off'); +$origin->start; +$origin->backup('my_backup'); +# Create a table with each of 6 PK values spanning 1/4 of a block. Delete the +# first four, so one index leaf is eligible for deletion. Make a replication +# slot just so pg_waldump will always have access to later WAL. +my $setup = <safe_psql('postgres', $setup); +my $before_vacuum_walfile = + $origin->safe_psql('postgres', "SELECT pg_walfile_name(pg_current_wal_lsn())"); +# VACUUM to delete the aforementioned leaf page. Force an XLogFlush() by +# dropping a permanent table. That way, the XLogReader infrastructure can +# always see VACUUM's records, even under synchronous_commit=off. Finally, +# find the LSN of that VACUUM's last UNLINK_PAGE record. +my $vacuum = <safe_psql('postgres', $vacuum); +$origin->stop; +my $unlink_lsn = do { + local %ENV = $origin->_get_env(); + my $stdout; + run_log(['pg_waldump', '-p', $origin->data_dir . '/pg_wal', + $before_vacuum_walfile, $after_unlink_walfile], + '>', \$stdout); + # CBDB/GPDB may emit this record as UNLINK_PAGE_META + $stdout =~ m|^rmgr: Btree .*, lsn: ([/0-9A-F]+), .*, desc: UNLINK_PAGE(?:_META)? left|m; + $1; +}; +die "did not find UNLINK_PAGE record" unless $unlink_lsn; + +# replica node: amcheck at notable points in the WAL stream +my $replica = PostgresNode->new('replica'); +$replica->init_from_backup($origin, 'my_backup', has_restoring => 1); +$replica->append_conf('postgresql.conf', + "recovery_target_lsn = '$unlink_lsn'"); +$replica->append_conf('postgresql.conf', 'recovery_target_inclusive = off'); +$replica->append_conf('postgresql.conf', 'recovery_target_action = promote'); +$replica->start; +$replica->poll_query_until('postgres', "SELECT pg_is_in_recovery() = 'f';") + or die "Timed out while waiting for PITR promotion"; +# recovery done; run amcheck +my $debug = "SET client_min_messages = 'debug1'"; +my ($rc, $stderr); +$rc = $replica->psql( + 'postgres', + "$debug; SELECT bt_index_parent_check('not_leftmost_pk', true)", + stderr => \$stderr); +print STDERR $stderr, "\n"; +is($rc, 0, "bt_index_parent_check passes"); +like( + $stderr, + qr/interrupted page deletion detected/, + "bt_index_parent_check: interrupted page deletion detected"); +$rc = $replica->psql( + 'postgres', + "$debug; SELECT bt_index_check('not_leftmost_pk', true)", + stderr => \$stderr); +print STDERR $stderr, "\n"; +is($rc, 0, "bt_index_check passes"); + +done_testing(); diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c index 7f8231a6815..a1b581871e6 100644 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@ -31,6 +31,7 @@ #include "access/xact.h" #include "catalog/index.h" #include "catalog/pg_am.h" +#include "catalog/pg_opfamily_d.h" #include "commands/tablecmds.h" #include "lib/bloomfilter.h" #include "miscadmin.h" @@ -145,6 +146,9 @@ static void bt_check_every_level(Relation rel, Relation heaprel, bool rootdescend); static BtreeLevel bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level); +static bool bt_leftmost_ignoring_half_dead(BtreeCheckState *state, + BlockNumber start, + BTPageOpaque start_opaque); static void bt_recheck_sibling_links(BtreeCheckState *state, BlockNumber btpo_prev_from_target, BlockNumber leftcurrent); @@ -337,10 +341,20 @@ bt_index_check_internal(Oid indrelid, bool parentcheck, bool heapallindexed, errmsg("index \"%s\" metapage has equalimage field set on unsupported nbtree version", RelationGetRelationName(indrel)))); if (allequalimage && !_bt_allequalimage(indrel, false)) + { + bool has_interval_ops = false; + + for (int i = 0; i < IndexRelationGetNumberOfKeyAttributes(indrel); i++) + if (indrel->rd_opfamily[i] == INTERVAL_BTREE_FAM_OID) + has_interval_ops = true; ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("index \"%s\" metapage incorrectly indicates that deduplication is safe", - RelationGetRelationName(indrel)))); + RelationGetRelationName(indrel)), + has_interval_ops + ? errhint("This is known of \"interval\" indexes last built on a version predating 2023-11.") + : 0)); + } /* Check index, possibly against table it is an index on */ bt_check_every_level(indrel, heaprel, heapkeyspace, parentcheck, @@ -764,7 +778,7 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level) */ if (state->readonly) { - if (!P_LEFTMOST(opaque)) + if (!bt_leftmost_ignoring_half_dead(state, current, opaque)) ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("block %u is not leftmost in index \"%s\"", @@ -818,8 +832,16 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level) */ } - /* Sibling links should be in mutual agreement */ - if (opaque->btpo_prev != leftcurrent) + /* + * Sibling links should be in mutual agreement. There arises + * leftcurrent == P_NONE && btpo_prev != P_NONE when the left sibling + * of the parent's low-key downlink is half-dead. (A half-dead page + * has no downlink from its parent.) Under heavyweight locking, the + * last bt_leftmost_ignoring_half_dead() validated this btpo_prev. + * Without heavyweight locking, validation of the P_NONE case remains + * unimplemented. + */ + if (opaque->btpo_prev != leftcurrent && leftcurrent != P_NONE) bt_recheck_sibling_links(state, opaque->btpo_prev, leftcurrent); /* Check level */ @@ -900,6 +922,66 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level) return nextleveldown; } +/* + * Like P_LEFTMOST(start_opaque), but accept an arbitrarily-long chain of + * half-dead, sibling-linked pages to the left. If a half-dead page appears + * under state->readonly, the database exited recovery between the first-stage + * and second-stage WAL records of a deletion. + */ +static bool +bt_leftmost_ignoring_half_dead(BtreeCheckState *state, + BlockNumber start, + BTPageOpaque start_opaque) +{ + BlockNumber reached = start_opaque->btpo_prev, + reached_from = start; + bool all_half_dead = true; + + /* + * To handle the !readonly case, we'd need to accept BTP_DELETED pages and + * potentially observe nbtree/README "Page deletion and backwards scans". + */ + Assert(state->readonly); + + while (reached != P_NONE && all_half_dead) + { + Page page = palloc_btree_page(state, reached); + BTPageOpaque reached_opaque = (BTPageOpaque) PageGetSpecialPointer(page); + + CHECK_FOR_INTERRUPTS(); + + /* + * Try to detect btpo_prev circular links. _bt_unlink_halfdead_page() + * writes that side-links will continue to point to the siblings. + * Check btpo_next for that property. + */ + all_half_dead = P_ISHALFDEAD(reached_opaque) && + reached != start && + reached != reached_from && + reached_opaque->btpo_next == reached_from; + if (all_half_dead) + { + XLogRecPtr pagelsn = PageGetLSN(page); + + /* pagelsn should point to an XLOG_BTREE_MARK_PAGE_HALFDEAD */ + ereport(DEBUG1, + (errcode(ERRCODE_NO_DATA), + errmsg_internal("harmless interrupted page deletion detected in index \"%s\"", + RelationGetRelationName(state->rel)), + errdetail_internal("Block=%u right block=%u page lsn=%X/%X.", + reached, reached_from, + LSN_FORMAT_ARGS(pagelsn)))); + + reached_from = reached; + reached = reached_opaque->btpo_prev; + } + + pfree(page); + } + + return all_half_dead; +} + /* * Raise an error when target page's left link does not point back to the * previous target page, called leftcurrent here. The leftcurrent page's @@ -940,6 +1022,9 @@ bt_recheck_sibling_links(BtreeCheckState *state, BlockNumber btpo_prev_from_target, BlockNumber leftcurrent) { + /* taking BTPageOpaque from metapage would give irrelevant findings */ + Assert(leftcurrent != P_NONE); + if (!state->readonly) { Buffer lbuf; @@ -1923,7 +2008,8 @@ bt_child_highkey_check(BtreeCheckState *state, opaque = (BTPageOpaque) PageGetSpecialPointer(page); /* The first page we visit at the level should be leftmost */ - if (first && !BlockNumberIsValid(state->prevrightlink) && !P_LEFTMOST(opaque)) + if (first && !BlockNumberIsValid(state->prevrightlink) && + !bt_leftmost_ignoring_half_dead(state, blkno, opaque)) ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("the first child of leftmost target page is not leftmost of its level in index \"%s\"", diff --git a/contrib/pax_storage/src/test/regress/expected/opr_sanity.out b/contrib/pax_storage/src/test/regress/expected/opr_sanity.out index 2fb7ba06584..d3bc9936213 100644 --- a/contrib/pax_storage/src/test/regress/expected/opr_sanity.out +++ b/contrib/pax_storage/src/test/regress/expected/opr_sanity.out @@ -2231,6 +2231,7 @@ ORDER BY 1, 2, 3; | complex_ops | complex_ops | complex | float_ops | float4_ops | real | float_ops | float8_ops | double precision + | interval_ops | interval_ops | interval | jsonb_ops | jsonb_ops | jsonb | multirange_ops | multirange_ops | anymultirange | numeric_ops | numeric_ops | numeric @@ -2239,7 +2240,7 @@ ORDER BY 1, 2, 3; | record_ops | record_ops | record | tsquery_ops | tsquery_ops | tsquery | tsvector_ops | tsvector_ops | tsvector -(16 rows) +(17 rows) -- **************** pg_index **************** -- Look for illegal values in pg_index fields. diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c index b4e50937609..1e1159d6382 100644 --- a/src/backend/access/brin/brin_minmax_multi.c +++ b/src/backend/access/brin/brin_minmax_multi.c @@ -2075,13 +2075,15 @@ brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS) Datum brin_minmax_multi_distance_date(PG_FUNCTION_ARGS) { + float8 delta = 0; DateADT dateVal1 = PG_GETARG_DATEADT(0); DateADT dateVal2 = PG_GETARG_DATEADT(1); - if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2)) - PG_RETURN_FLOAT8(0); + delta = (float8) dateVal2 - (float8) dateVal1; + + Assert(delta >= 0); - PG_RETURN_FLOAT8(dateVal1 - dateVal2); + PG_RETURN_FLOAT8(delta); } /* @@ -2135,10 +2137,7 @@ brin_minmax_multi_distance_timestamp(PG_FUNCTION_ARGS) Timestamp dt1 = PG_GETARG_TIMESTAMP(0); Timestamp dt2 = PG_GETARG_TIMESTAMP(1); - if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2)) - PG_RETURN_FLOAT8(0); - - delta = dt2 - dt1; + delta = (float8) dt2 - (float8) dt1; Assert(delta >= 0); @@ -2155,45 +2154,20 @@ brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS) Interval *ia = PG_GETARG_INTERVAL_P(0); Interval *ib = PG_GETARG_INTERVAL_P(1); - Interval *result; int64 dayfraction; int64 days; - result = (Interval *) palloc(sizeof(Interval)); - - result->month = ib->month - ia->month; - /* overflow check copied from int4mi */ - if (!SAMESIGN(ib->month, ia->month) && - !SAMESIGN(result->month, ib->month)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - - result->day = ib->day - ia->day; - if (!SAMESIGN(ib->day, ia->day) && - !SAMESIGN(result->day, ib->day)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - - result->time = ib->time - ia->time; - if (!SAMESIGN(ib->time, ia->time) && - !SAMESIGN(result->time, ib->time)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - /* * Delta is (fractional) number of days between the intervals. Assume * months have 30 days for consistency with interval_cmp_internal. We * don't need to be exact, in the worst case we'll build a bit less * efficient ranges. But we should not contradict interval_cmp. */ - dayfraction = result->time % USECS_PER_DAY; - days = result->time / USECS_PER_DAY; - days += result->month * INT64CONST(30); - days += result->day; + dayfraction = (ib->time % USECS_PER_DAY) - (ia->time % USECS_PER_DAY); + days = (ib->time / USECS_PER_DAY) - (ia->time / USECS_PER_DAY); + days += (int64) ib->day - (int64) ia->day; + days += ((int64) ib->month - (int64) ia->month) * INT64CONST(30); /* convert to double precision */ delta = (double) days + dayfraction / (double) USECS_PER_DAY; diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c index ba34d5ca6fa..9ea662c5be8 100644 --- a/src/backend/access/transam/xlogreader.c +++ b/src/backend/access/transam/xlogreader.c @@ -40,7 +40,7 @@ static void report_invalid_record(XLogReaderState *state, const char *fmt,...) pg_attribute_printf(2, 3); -static bool allocate_recordbuf(XLogReaderState *state, uint32 reclength); +static void allocate_recordbuf(XLogReaderState *state, uint32 reclength); static int ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen); static void XLogReaderInvalReadState(XLogReaderState *state); @@ -132,14 +132,7 @@ XLogReaderAllocate(int wal_segment_size, const char *waldir, * Allocate an initial readRecordBuf of minimal size, which can later be * enlarged if necessary. */ - if (!allocate_recordbuf(state, 0)) - { - pfree(state->errormsg_buf); - pfree(state->readBuf); - pfree(state); - return NULL; - } - + allocate_recordbuf(state, 0); return state; } @@ -168,7 +161,6 @@ XLogReaderFree(XLogReaderState *state) /* * Allocate readRecordBuf to fit a record of at least the given length. - * Returns true if successful, false if out of memory. * * readRecordBufSize is set to the new buffer size. * @@ -176,8 +168,11 @@ XLogReaderFree(XLogReaderState *state) * XLOG_BLCKSZ, and make sure it's at least 5*Max(BLCKSZ, XLOG_BLCKSZ) to start * with. (That is enough for all "normal" records, but very large commit or * abort records might need more space.) + * + * Note: This routine should *never* be called for xl_tot_len until the header + * of the record has been fully validated. */ -static bool +static void allocate_recordbuf(XLogReaderState *state, uint32 reclength) { uint32 newSize = reclength; @@ -185,36 +180,10 @@ allocate_recordbuf(XLogReaderState *state, uint32 reclength) newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ); newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ)); -#ifndef FRONTEND - - /* - * Note that in much unlucky circumstances, the random data read from a - * recycled segment can cause this routine to be called with a size - * causing a hard failure at allocation. For a standby, this would cause - * the instance to stop suddenly with a hard failure, preventing it to - * retry fetching WAL from one of its sources which could allow it to move - * on with replay without a manual restart. If the data comes from a past - * recycled segment and is still valid, then the allocation may succeed - * but record checks are going to fail so this would be short-lived. If - * the allocation fails because of a memory shortage, then this is not a - * hard failure either per the guarantee given by MCXT_ALLOC_NO_OOM. - */ - if (!AllocSizeIsValid(newSize)) - return false; - -#endif - if (state->readRecordBuf) pfree(state->readRecordBuf); - state->readRecordBuf = - (char *) palloc_extended(newSize, MCXT_ALLOC_NO_OOM); - if (state->readRecordBuf == NULL) - { - state->readRecordBufSize = 0; - return false; - } + state->readRecordBuf = (char *) palloc(newSize); state->readRecordBufSize = newSize; - return true; } /* @@ -404,7 +373,7 @@ XLogReadRecord(XLogReaderState *state, char **errormsg) } else { - /* XXX: more validation should be done here */ + /* There may be no next page if it's too small. */ if (total_len < SizeOfXLogRecord) { report_invalid_record(state, @@ -413,6 +382,7 @@ XLogReadRecord(XLogReaderState *state, char **errormsg) (uint32) SizeOfXLogRecord, total_len); goto err; } + /* We'll validate the header once we have the next page. */ gotheader = false; } @@ -428,16 +398,11 @@ XLogReadRecord(XLogReaderState *state, char **errormsg) assembled = true; /* - * Enlarge readRecordBuf as needed. + * We always have space for a couple of pages, enough to validate a + * boundary-spanning record header. */ - if (total_len > state->readRecordBufSize && - !allocate_recordbuf(state, total_len)) - { - /* We treat this as a "bogus data" condition */ - report_invalid_record(state, "record length %u at %X/%X too long", - total_len, LSN_FORMAT_ARGS(RecPtr)); - goto err; - } + Assert(state->readRecordBufSize >= XLOG_BLCKSZ * 2); + Assert(state->readRecordBufSize >= len); /* Copy the first fragment of the record from the first page. */ memcpy(state->readRecordBuf, @@ -533,8 +498,30 @@ XLogReadRecord(XLogReaderState *state, char **errormsg) goto err; gotheader = true; } - } while (gotlen < total_len); + /* + * We might need a bigger buffer. We have validated the record + * header, in the case that it split over a page boundary. We've + * also cross-checked total_len against xlp_rem_len on the second + * page, and verified xlp_pageaddr on both. + */ + Assert(gotheader); + if (total_len > state->readRecordBufSize) + { + char save_copy[XLOG_BLCKSZ * 2]; + + /* + * Save and restore the data we already had. It can't be more + * than two pages. + */ + Assert(gotlen <= lengthof(save_copy)); + Assert(gotlen <= state->readRecordBufSize); + memcpy(save_copy, state->readRecordBuf, gotlen); + allocate_recordbuf(state, total_len); + memcpy(state->readRecordBuf, save_copy, gotlen); + buffer = state->readRecordBuf + gotlen; + } + } while (gotlen < total_len); Assert(gotheader); record = (XLogRecord *) state->readRecordBuf; @@ -801,6 +788,8 @@ ValidXLogRecord(XLogReaderState *state, XLogRecord *record, XLogRecPtr recptr) { pg_crc32c crc; + Assert(record->xl_tot_len >= SizeOfXLogRecord); + /* Calculate the CRC */ INIT_CRC32C(crc); COMP_CRC32C(crc, ((char *) record) + SizeOfXLogRecord, record->xl_tot_len - SizeOfXLogRecord); diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 629eca05483..fcb51b2fbbd 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -2008,7 +2008,7 @@ ExecInitPruningContext(PartitionPruneContext *context, foreach(lc, pruning_steps) { PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc); - ListCell *lc2; + ListCell *lc2 = list_head(step->exprs); int keyno; /* not needed for other step kinds */ @@ -2017,22 +2017,27 @@ ExecInitPruningContext(PartitionPruneContext *context, Assert(list_length(step->exprs) <= partnatts); - keyno = 0; - foreach(lc2, step->exprs) + for (keyno = 0; keyno < partnatts; keyno++) { - Expr *expr = (Expr *) lfirst(lc2); + if (bms_is_member(keyno, step->nullkeys)) + continue; - /* not needed for Consts */ - if (!IsA(expr, Const)) + if (lc2 != NULL) { - int stateidx = PruneCxtStateIdx(partnatts, - step->step.step_id, - keyno); + Expr *expr = lfirst(lc2); + + /* not needed for Consts */ + if (!IsA(expr, Const)) + { + int stateidx = PruneCxtStateIdx(partnatts, + step->step.step_id, + keyno); - context->exprstates[stateidx] = - ExecInitExpr(expr, context->planstate); + context->exprstates[stateidx] = + ExecInitExpr(expr, context->planstate); + } + lc2 = lnext(step->exprs, lc2); } - keyno++; } } } diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 4e718bd7b31..8c11a1e826e 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3172,10 +3172,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * must be converted, and * - the root partitioned table used for tuple routing. * - * If it's a partitioned table, the root partition doesn't appear - * elsewhere in the plan and its RT index is given explicitly in - * node->rootRelation. Otherwise (i.e. table inheritance) the target - * relation is the first relation in the node->resultRelations list. + * If it's a partitioned or inherited table, the root partition or + * appendrel RTE doesn't appear elsewhere in the plan and its RT index is + * given explicitly in node->rootRelation. Otherwise, the target relation + * is the sole relation in the node->resultRelations list. *---------- */ if (node->rootRelation > 0) @@ -3186,6 +3186,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) } else { + Assert(list_length(node->resultRelations) == 1); mtstate->rootResultRelInfo = mtstate->resultRelInfo; ExecInitResultRelation(estate, mtstate->resultRelInfo, linitial_int(node->resultRelations)); diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index b5e7ef3b60c..82678b8a522 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -367,6 +367,21 @@ add_paths_to_join_relation(PlannerInfo *root, hash_inner_and_outer(root, joinrel, outerrel, innerrel, jointype, &extra); +#ifdef NOT_USED + /* + * createplan.c does not currently support handling of pseudoconstant + * clauses assigned to joins pushed down by extensions; check if the + * restrictlist has such clauses, and if not, allow them to consider + * pushing down joins. + */ + if ((joinrel->fdwroutine && + joinrel->fdwroutine->GetForeignJoinPaths) || + set_join_pathlist_hook) + consider_join_pushdown = !has_pseudoconstant_clauses(root, + restrictlist); + +#endif + /* * 5. If inner and outer relations are foreign tables (or joins) belonging * to the same server and assigned to the same user to check access @@ -424,7 +439,10 @@ add_paths_to_join_relation(PlannerInfo *root, } /* - * 6. Finally, give extensions a chance to manipulate the path list. + * 6. Finally, give extensions a chance to manipulate the path list. They + * could add new paths (such as CustomPaths) by calling add_path(), or + * add_partial_path() if parallel aware. They could also delete or modify + * paths added by the core code. */ if (set_join_pathlist_hook) set_join_pathlist_hook(root, joinrel, outerrel, innerrel, diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 7b55eded430..cdb2ed911e1 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -2463,6 +2463,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) parse->resultRelation); int resultRelation = -1; + /* Pass the root result rel forward to the executor. */ + rootRelation = parse->resultRelation; + /* Add only leaf children to ModifyTable. */ while ((resultRelation = bms_next_member(root->leaf_result_relids, resultRelation)) >= 0) @@ -2547,6 +2550,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) else { /* Single-relation INSERT/UPDATE/DELETE. */ + rootRelation = 0; /* there's no separate root rel */ resultRelations = list_make1_int(parse->resultRelation); if (parse->commandType == CMD_UPDATE) updateColnosLists = list_make1(root->update_colnos); @@ -2556,16 +2560,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) returningLists = list_make1(parse->returningList); } - /* - * If target is a partition root table, we need to mark the - * ModifyTable node appropriately for that. - */ - if (rt_fetch(parse->resultRelation, parse->rtable)->relkind == - RELKIND_PARTITIONED_TABLE) - rootRelation = parse->resultRelation; - else - rootRelation = 0; - /* * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node * will have dealt with fetching non-locked marked rows, else we diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 4648a2f2719..04db89e92f9 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -5815,7 +5815,7 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel, * 'operation' is the operation type * 'canSetTag' is true if we set the command tag/es_processed * 'nominalRelation' is the parent RT index for use of EXPLAIN - * 'rootRelation' is the partitioned table root RT index, or 0 if none + * 'rootRelation' is the partitioned/inherited table root RTI, or 0 if none * 'partColsUpdated' is true if any partitioning columns are being updated, * either from the target relation or a descendent partitioned table. * 'resultRelations' is an integer list of actual RT indexes of target rel(s) diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 039952aa8ae..274b38d2643 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1507,7 +1507,8 @@ ExpandRowReference(ParseState *pstate, Node *expr, * drill down to find the ultimate defining expression and attempt to infer * the tupdesc from it. We ereport if we can't determine the tupdesc. * - * levelsup is an extra offset to interpret the Var's varlevelsup correctly. + * levelsup is an extra offset to interpret the Var's varlevelsup correctly + * when recursing. Outside callers should pass zero. */ TupleDesc expandRecordVariable(ParseState *pstate, Var *var, int levelsup) @@ -1595,11 +1596,17 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) /* * Recurse into the sub-select to see what its Var refers * to. We have to build an additional level of ParseState - * to keep in step with varlevelsup in the subselect. + * to keep in step with varlevelsup in the subselect; + * furthermore, the subquery RTE might be from an outer + * query level, in which case the ParseState for the + * subselect must have that outer level as parent. */ - ParseState mypstate; + ParseState mypstate = {0}; + Index levelsup; - MemSet(&mypstate, 0, sizeof(mypstate)); + /* this loop must work, since GetRTEByRangeTablePosn did */ + for (levelsup = 0; levelsup < netlevelsup; levelsup++) + pstate = pstate->parentParseState; mypstate.parentParseState = pstate; mypstate.p_rtable = rte->subquery->rtable; /* don't bother filling the rest of the fake pstate */ @@ -1651,12 +1658,11 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) * Recurse into the CTE to see what its Var refers to. We * have to build an additional level of ParseState to keep * in step with varlevelsup in the CTE; furthermore it - * could be an outer CTE. + * could be an outer CTE (compare SUBQUERY case above). */ - ParseState mypstate; + ParseState mypstate = {0}; Index levelsup; - MemSet(&mypstate, 0, sizeof(mypstate)); /* this loop must work, since GetCTEForRTE did */ for (levelsup = 0; levelsup < rte->ctelevelsup + netlevelsup; diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index a9ff52941ca..6d7c2a5f53d 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -172,7 +172,6 @@ static List *get_steps_using_prefix(GeneratePruningStepsContext *context, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, - int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix); static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, @@ -180,7 +179,6 @@ static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, - int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix, ListCell *start, @@ -1557,7 +1555,6 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, pc->op_is_ne, pc->expr, pc->cmpfn, - 0, NULL, NIL); opsteps = list_concat(opsteps, pc_steps); @@ -1683,7 +1680,6 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, pc->op_is_ne, pc->expr, pc->cmpfn, - pc->keyno, NULL, prefix); opsteps = list_concat(opsteps, pc_steps); @@ -1757,7 +1753,6 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context, false, pc->expr, pc->cmpfn, - pc->keyno, nullkeys, prefix); opsteps = list_concat(opsteps, pc_steps); @@ -2380,25 +2375,31 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context, /* * get_steps_using_prefix - * Generate list of PartitionPruneStepOp steps each consisting of given - * opstrategy - * - * To generate steps, step_lastexpr and step_lastcmpfn are appended to - * expressions and cmpfns, respectively, extracted from the clauses in - * 'prefix'. Actually, since 'prefix' may contain multiple clauses for the - * same partition key column, we must generate steps for various combinations - * of the clauses of different keys. - * - * For list/range partitioning, callers must ensure that step_nullkeys is - * NULL, and that prefix contains at least one clause for each of the - * partition keys earlier than one specified in step_lastkeyno if it's - * greater than zero. For hash partitioning, step_nullkeys is allowed to be - * non-NULL, but they must ensure that prefix contains at least one clause - * for each of the partition keys other than those specified in step_nullkeys - * and step_lastkeyno. - * - * For both cases, callers must also ensure that clauses in prefix are sorted - * in ascending order of their partition key numbers. + * Generate a list of PartitionPruneStepOps based on the given input. + * + * 'step_lastexpr' and 'step_lastcmpfn' are the Expr and comparison function + * belonging to the final partition key that we have a clause for. 'prefix' + * is a list of PartClauseInfos for partition key numbers prior to the given + * 'step_lastexpr' and 'step_lastcmpfn'. 'prefix' may contain multiple + * PartClauseInfos belonging to a single partition key. We will generate a + * PartitionPruneStepOp for each combination of the given PartClauseInfos + * using, at most, one PartClauseInfo per partition key. + * + * For LIST and RANGE partitioned tables, callers must ensure that + * step_nullkeys is NULL, and that prefix contains at least one clause for + * each of the partition keys prior to the key that 'step_lastexpr' and + * 'step_lastcmpfn'belong to. + * + * For HASH partitioned tables, callers must ensure that 'prefix' contains at + * least one clause for each of the partition keys apart from the final key + * (the expr and comparison function for the final key are in 'step_lastexpr' + * and 'step_lastcmpfn'). A bit set in step_nullkeys can substitute clauses + * in the 'prefix' list for any given key. If a bit is set in 'step_nullkeys' + * for a given key, then there must be no PartClauseInfo for that key in the + * 'prefix' list. + * + * For each of the above cases, callers must ensure that PartClauseInfos in + * 'prefix' are sorted in ascending order of keyno. */ static List * get_steps_using_prefix(GeneratePruningStepsContext *context, @@ -2406,14 +2407,17 @@ get_steps_using_prefix(GeneratePruningStepsContext *context, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, - int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix) { + /* step_nullkeys must be empty for RANGE and LIST partitioned tables */ Assert(step_nullkeys == NULL || context->rel->part_scheme->strategy == PARTITION_STRATEGY_HASH); - /* Quick exit if there are no values to prefix with. */ + /* + * No recursive processing is required when 'prefix' is an empty list. This + * occurs when there is only 1 partition key column. + */ if (list_length(prefix) == 0) { PartitionPruneStep *step; @@ -2427,13 +2431,12 @@ get_steps_using_prefix(GeneratePruningStepsContext *context, return list_make1(step); } - /* Recurse to generate steps for various combinations. */ + /* Recurse to generate steps for every combination of clauses. */ return get_steps_using_prefix_recurse(context, step_opstrategy, step_op_is_ne, step_lastexpr, step_lastcmpfn, - step_lastkeyno, step_nullkeys, prefix, list_head(prefix), @@ -2442,13 +2445,17 @@ get_steps_using_prefix(GeneratePruningStepsContext *context, /* * get_steps_using_prefix_recurse - * Recursively generate combinations of clauses for different partition - * keys and start generating steps upon reaching clauses for the greatest - * column that is less than the one for which we're currently generating - * steps (that is, step_lastkeyno) + * Generate and return a list of PartitionPruneStepOps using the 'prefix' + * list of PartClauseInfos starting at the 'start' cell. + * + * When 'prefix' contains multiple PartClauseInfos for a single partition key + * we create a PartitionPruneStepOp for each combination of duplicated + * PartClauseInfos. The returned list will contain a PartitionPruneStepOp + * for each unique combination of input PartClauseInfos containing at most one + * PartClauseInfo per partition key. * - * 'prefix' is the list of PartClauseInfos. - * 'start' is where we should start iterating for the current invocation. + * 'prefix' is the input list of PartClauseInfos sorted by keyno. + * 'start' marks the cell that searching the 'prefix' list should start from. * 'step_exprs' and 'step_cmpfns' each contains the expressions and cmpfns * we've generated so far from the clauses for the previous part keys. */ @@ -2458,7 +2465,6 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, bool step_op_is_ne, Expr *step_lastexpr, Oid step_lastcmpfn, - int step_lastkeyno, Bitmapset *step_nullkeys, List *prefix, ListCell *start, @@ -2468,23 +2474,25 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, List *result = NIL; ListCell *lc; int cur_keyno; + int final_keyno; /* Actually, recursion would be limited by PARTITION_MAX_KEYS. */ check_stack_depth(); - /* Check if we need to recurse. */ Assert(start != NULL); cur_keyno = ((PartClauseInfo *) lfirst(start))->keyno; - if (cur_keyno < step_lastkeyno - 1) + final_keyno = ((PartClauseInfo *) llast(prefix))->keyno; + + /* Check if we need to recurse. */ + if (cur_keyno < final_keyno) { PartClauseInfo *pc; ListCell *next_start; /* - * For each clause with cur_keyno, add its expr and cmpfn to - * step_exprs and step_cmpfns, respectively, and recurse after setting - * next_start to the ListCell of the first clause for the next - * partition key. + * Find the first PartClauseInfo belonging to the next partition key, the + * next recursive call must start iteration of the prefix list from that + * point. */ for_each_cell(lc, prefix, start) { @@ -2493,8 +2501,15 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, if (pc->keyno > cur_keyno) break; } + + /* record where to start iterating in the next recursive call */ next_start = lc; + /* + * For each PartClauseInfo with keyno set to cur_keyno, add its expr and + * cmpfn to step_exprs and step_cmpfns, respectively, and recurse using + * 'next_start' as the starting point in the 'prefix' list. + */ for_each_cell(lc, prefix, start) { List *moresteps; @@ -2514,6 +2529,7 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, } else { + /* check the 'prefix' list is sorted correctly */ Assert(pc->keyno > cur_keyno); break; } @@ -2523,7 +2539,6 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, step_op_is_ne, step_lastexpr, step_lastcmpfn, - step_lastkeyno, step_nullkeys, prefix, next_start, @@ -2542,8 +2557,8 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context, * each clause with cur_keyno, which is all clauses from here onward * till the end of the list. Note that for hash partitioning, * step_nullkeys is allowed to be non-empty, in which case step_exprs - * would only contain expressions for the earlier partition keys that - * are not specified in step_nullkeys. + * would only contain expressions for the partition keys that are not + * specified in step_nullkeys. */ Assert(list_length(step_exprs) == cur_keyno || !bms_is_empty(step_nullkeys)); diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c index eed6ae262f2..788b7d61bcc 100644 --- a/src/backend/utils/adt/datum.c +++ b/src/backend/utils/adt/datum.c @@ -43,6 +43,7 @@ #include "postgres.h" #include "access/detoast.h" +#include "catalog/pg_type_d.h" #include "common/hashfn.h" #include "fmgr.h" #include "utils/builtins.h" @@ -388,20 +389,17 @@ datum_image_hash(Datum value, bool typByVal, int typLen) * datum_image_eq() in all cases can use this as their "equalimage" support * function. * - * Currently, we unconditionally assume that any B-Tree operator class that - * registers btequalimage as its support function 4 must be able to safely use - * optimizations like deduplication (i.e. we return true unconditionally). If - * it ever proved necessary to rescind support for an operator class, we could - * do that in a targeted fashion by doing something with the opcintype - * argument. + * Earlier minor releases erroneously associated this function with + * interval_ops. Detect that case to rescind deduplication support, without + * requiring initdb. *------------------------------------------------------------------------- */ Datum btequalimage(PG_FUNCTION_ARGS) { - /* Oid opcintype = PG_GETARG_OID(0); */ + Oid opcintype = PG_GETARG_OID(0); - PG_RETURN_BOOL(true); + PG_RETURN_BOOL(opcintype != INTERVALOID); } /*------------------------------------------------------------------------- diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 186cb8fed16..89d4d91c6c1 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -7727,22 +7727,28 @@ get_name_for_var_field(Var *var, int fieldno, * 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. + * subselect; furthermore, the subquery RTE might be + * from an outer query level, in which case the + * namespace for the subselect must have that outer + * level as parent namespace. */ + List *save_nslist = context->namespaces; + List *parent_namespaces; deparse_namespace mydpns; const char *result; + parent_namespaces = list_copy_tail(context->namespaces, + netlevelsup); + set_deparse_for_query(&mydpns, rte->subquery, - context->namespaces); + parent_namespaces); - context->namespaces = lcons(&mydpns, - context->namespaces); + context->namespaces = lcons(&mydpns, parent_namespaces); result = get_name_for_var_field((Var *) expr, fieldno, 0, context); - context->namespaces = - list_delete_first(context->namespaces); + context->namespaces = save_nslist; return result; } @@ -7835,7 +7841,7 @@ get_name_for_var_field(Var *var, int fieldno, attnum); if (ste == NULL || ste->resjunk) - elog(ERROR, "subquery %s does not have attribute %d", + elog(ERROR, "CTE %s does not have attribute %d", rte->eref->aliasname, attnum); expr = (Node *) ste->expr; if (IsA(expr, Var)) @@ -7843,21 +7849,22 @@ get_name_for_var_field(Var *var, int fieldno, /* * 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. + * to keep in step with varlevelsup in the CTE; + * furthermore it could be an outer CTE (compare + * SUBQUERY case above). */ List *save_nslist = context->namespaces; - List *new_nslist; + List *parent_namespaces; deparse_namespace mydpns; const char *result; + parent_namespaces = list_copy_tail(context->namespaces, + ctelevelsup); + set_deparse_for_query(&mydpns, ctequery, - context->namespaces); + parent_namespaces); - new_nslist = list_copy_tail(context->namespaces, - ctelevelsup); - context->namespaces = lcons(&mydpns, new_nslist); + context->namespaces = lcons(&mydpns, parent_namespaces); result = get_name_for_var_field((Var *) expr, fieldno, 0, context); diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat index 8ba4de4f868..5ec2f50c9fa 100644 --- a/src/include/catalog/pg_amproc.dat +++ b/src/include/catalog/pg_amproc.dat @@ -172,8 +172,6 @@ { amprocfamily => 'btree/interval_ops', amproclefttype => 'interval', amprocrighttype => 'interval', amprocnum => '3', amproc => 'in_range(interval,interval,interval,bool,bool)' }, -{ amprocfamily => 'btree/interval_ops', amproclefttype => 'interval', - amprocrighttype => 'interval', amprocnum => '4', amproc => 'btequalimage' }, { amprocfamily => 'btree/macaddr_ops', amproclefttype => 'macaddr', amprocrighttype => 'macaddr', amprocnum => '1', amproc => 'macaddr_cmp' }, { amprocfamily => 'btree/macaddr_ops', amproclefttype => 'macaddr', diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat index 8cb8ce386d2..0e73063ac3f 100644 --- a/src/include/catalog/pg_opfamily.dat +++ b/src/include/catalog/pg_opfamily.dat @@ -50,7 +50,7 @@ opfmethod => 'btree', opfname => 'integer_ops' }, { oid => '1977', opfmethod => 'hash', opfname => 'integer_ops' }, -{ oid => '1982', +{ oid => '1982', oid_symbol => 'INTERVAL_BTREE_FAM_OID', opfmethod => 'btree', opfname => 'interval_ops' }, { oid => '1983', opfmethod => 'hash', opfname => 'interval_ops' }, diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 9281ea90729..d08a5841b18 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -2369,7 +2369,7 @@ typedef struct ModifyTablePath CmdType operation; /* INSERT, UPDATE, or DELETE */ bool canSetTag; /* do we set the command tag/es_processed? */ Index nominalRelation; /* Parent RT index for use of EXPLAIN */ - Index rootRelation; /* Root RT index, if target is partitioned */ + Index rootRelation; /* Root RT index, if partitioned/inherited */ bool partColsUpdated; /* some part key in hierarchy updated? */ bool splitUpdate; /* if distribution key is updated */ List *resultRelations; /* integer list of RT indexes */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index dd214cb9996..5392015a738 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -389,11 +389,12 @@ typedef struct ProjectSet * Apply rows produced by outer plan to result table(s), * by inserting, updating, or deleting. * - * If the originally named target table is a partitioned table, both - * nominalRelation and rootRelation contain the RT index of the partition - * root, which is not otherwise mentioned in the plan. Otherwise rootRelation - * is zero. However, nominalRelation will always be set, as it's the rel that - * EXPLAIN should claim is the INSERT/UPDATE/DELETE target. + * If the originally named target table is a partitioned table or inheritance + * tree, both nominalRelation and rootRelation contain the RT index of the + * partition root or appendrel RTE, which is not otherwise mentioned in the + * plan. Otherwise rootRelation is zero. However, nominalRelation will + * always be set, as it's the rel that EXPLAIN should claim is the + * INSERT/UPDATE/DELETE target. * * Note that rowMarks and epqParam are presumed to be valid for all the * table(s); they can't contain any info that varies across tables. @@ -405,7 +406,7 @@ typedef struct ModifyTable CmdType operation; /* INSERT, UPDATE, or DELETE */ bool canSetTag; /* do we set the command tag/es_processed? */ Index nominalRelation; /* Parent RT index for use of EXPLAIN */ - Index rootRelation; /* Root RT index, if target is partitioned */ + Index rootRelation; /* Root RT index, if partitioned/inherited */ bool partColsUpdated; /* some part key in hierarchy updated? */ bool splitUpdate; /* if it's split update */ List *resultRelations; /* integer list of RT indexes */ diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm index b81f6e7b68d..0613823a1a9 100644 --- a/src/test/perl/PostgresNode.pm +++ b/src/test/perl/PostgresNode.pm @@ -2764,6 +2764,22 @@ sub log_check =pod +=item log_contains(pattern, offset) + +Find pattern in logfile of node after offset byte. + +=cut + +sub log_contains +{ + my ($self, $pattern, $offset) = @_; + + return TestLib::slurp_file($self->logfile, $offset) =~ + m/$pattern/; +} + +=pod + =item $node->run_log(...) Runs a shell command like TestLib::run_log, but with connection parameters set diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm index 7860425b543..1aed8d4c213 100644 --- a/src/test/perl/TestLib.pm +++ b/src/test/perl/TestLib.pm @@ -70,6 +70,7 @@ our @EXPORT = qw( chmod_recursive check_pg_config dir_symlink + scan_server_header system_or_bail system_log run_log @@ -648,6 +649,46 @@ sub chmod_recursive =pod +=item scan_server_header(header_path, regexp) + +Returns an array that stores all the matches of the given regular expression +within the PostgreSQL installation's C. This can be used to +retrieve specific value patterns from the installation's header files. + +=cut + +sub scan_server_header +{ + my ($header_path, $regexp) = @_; + + my ($stdout, $stderr); + my $result = IPC::Run::run [ 'pg_config', '--includedir-server' ], '>', + \$stdout, '2>', \$stderr + or die "could not execute pg_config"; + chomp($stdout); + $stdout =~ s/\r$//; + + open my $header_h, '<', "$stdout/$header_path" or die "$!"; + + my @match = undef; + while (<$header_h>) + { + my $line = $_; + + if (@match = $line =~ /^$regexp/) + { + last; + } + } + + close $header_h; + die "could not find match in header $header_path\n" + unless @match; + return @match; +} + +=pod + =item check_pg_config(regexp) Return the number of matches of the given regular expression diff --git a/src/test/recovery/t/019_replslot_limit.pl b/src/test/recovery/t/019_replslot_limit.pl index b365fcc8863..648cab393ea 100644 --- a/src/test/recovery/t/019_replslot_limit.pl +++ b/src/test/recovery/t/019_replslot_limit.pl @@ -172,8 +172,7 @@ $node_standby->stop; -ok( !find_in_log( - $node_standby, +ok( !$node_standby->log_contains( "requested WAL segment [0-9A-F]+ has already been removed"), 'check that required WAL segments are still available'); @@ -195,8 +194,7 @@ my $invalidated = 0; for (my $i = 0; $i < 10000; $i++) { - if (find_in_log( - $node_primary, + if ($node_primary->log_contains( "invalidating slot \"rep1\" because its restart_lsn [0-9A-F/]+ exceeds max_slot_wal_keep_size", $logstart)) { @@ -219,7 +217,7 @@ my $checkpoint_ended = 0; for (my $i = 0; $i < 10000; $i++) { - if (find_in_log($node_primary, "checkpoint complete: ", $logstart)) + if ($node_primary->log_contains("checkpoint complete: ", $logstart)) { $checkpoint_ended = 1; last; @@ -249,8 +247,7 @@ my $failed = 0; for (my $i = 0; $i < 10000; $i++) { - if (find_in_log( - $node_standby, + if ($node_standby->log_contains( "requested WAL segment [0-9A-F]+ has already been removed", $logstart)) { @@ -356,8 +353,7 @@ my $max_attempts = $TestLib::timeout_default; while ($max_attempts-- >= 0) { - if (find_in_log( - $node_primary3, + if ($node_primary3->log_contains( "terminating process $senderpid to release replication slot \"rep3\"", $logstart)) { @@ -379,8 +375,7 @@ $max_attempts = $TestLib::timeout_default; while ($max_attempts-- >= 0) { - if (find_in_log( - $node_primary3, + if ($node_primary3->log_contains( 'invalidating slot "rep3" because its restart_lsn', $logstart)) { ok(1, "slot invalidation logged"); @@ -418,16 +413,4 @@ sub get_log_size return (stat $node->logfile)[7]; } -# find $pat in logfile of $node after $off-th byte -sub find_in_log -{ - my ($node, $pat, $off) = @_; - - $off = 0 unless defined $off; - my $log = TestLib::slurp_file($node->logfile); - return 0 if (length($log) <= $off); - - $log = substr($log, $off); - - return $log =~ m/$pat/; -} +done_testing(); diff --git a/src/test/recovery/t/033_replay_tsp_drops.pl b/src/test/recovery/t/033_replay_tsp_drops.pl index fb0b6150f27..ac7b8fe6e4e 100644 --- a/src/test/recovery/t/033_replay_tsp_drops.pl +++ b/src/test/recovery/t/033_replay_tsp_drops.pl @@ -113,7 +113,7 @@ sub test_tablespace my $tspdir = $node_standby->data_dir . "/pg_tblspc/$tspoid"; File::Path::rmtree($tspdir); -my $logstart = get_log_size($node_standby); +my $logstart = -s $node_standby->logfile; # Create a database in the tablespace and a table in default tablespace $node_primary->safe_psql( @@ -132,34 +132,11 @@ sub test_tablespace { last if ( - find_in_log( - $node_standby, "WARNING: creating missing directory: pg_tblspc/", + $node_standby->log_contains( + qr!WARNING: ( [A-Z0-9]+:)? creating missing directory: pg_tblspc/!, $logstart)); sleep 1; } ok($max_attempts > 0, "invalid directory creation is detected"); done_testing(); - - -# return the size of logfile of $node in bytes -sub get_log_size -{ - my ($node) = @_; - - return (stat $node->logfile)[7]; -} - -# find $pat in logfile of $node after $off-th byte -sub find_in_log -{ - my ($node, $pat, $off) = @_; - - $off = 0 unless defined $off; - my $log = PostgreSQL::Test::Utils::slurp_file($node->logfile); - return 0 if (length($log) <= $off); - - $log = substr($log, $off); - - return $log =~ m/$pat/; -} diff --git a/src/test/recovery/t/039_end_of_wal.pl b/src/test/recovery/t/039_end_of_wal.pl new file mode 100644 index 00000000000..8d652354dd9 --- /dev/null +++ b/src/test/recovery/t/039_end_of_wal.pl @@ -0,0 +1,500 @@ +# Copyright (c) 2023, PostgreSQL Global Development Group +# +# Test detecting end-of-WAL conditions. This test suite generates +# fake defective page and record headers to trigger various failure +# scenarios. + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; +use Fcntl qw(SEEK_SET); + +use integer; # causes / operator to use integer math + +# Is this a big-endian system ("network" byte order)? We can't use 'Q' in +# pack() calls because it's not available in some perl builds, so we need to +# break 64 bit LSN values into two 'I' values. Fortunately we don't need to +# deal with high values, so we can just write 0 for the high order 32 bits, but +# we need to know the endianness to do that. +my $BIG_ENDIAN = pack("L", 0x12345678) eq pack("N", 0x12345678); + +# Header size of record header. +my $RECORD_HEADER_SIZE = 24; + +# Fields retrieved from code headers. +my @scan_result = scan_server_header('access/xlog_internal.h', + '#define\s+XLOG_PAGE_MAGIC\s+(\w+)'); +my $XLP_PAGE_MAGIC = hex($scan_result[0]); +@scan_result = scan_server_header('access/xlog_internal.h', + '#define\s+XLP_FIRST_IS_CONTRECORD\s+(\w+)'); +my $XLP_FIRST_IS_CONTRECORD = hex($scan_result[0]); + +# Values queried from the server +my $WAL_SEGMENT_SIZE; +my $WAL_BLOCK_SIZE; +my $TLI; + +# Build path of a WAL segment. +sub wal_segment_path +{ + my $node = shift; + my $tli = shift; + my $segment = shift; + my $wal_path = + sprintf("%s/pg_wal/%08X%08X%08X", $node->data_dir, $tli, 0, $segment); + return $wal_path; +} + +# Calculate from a LSN (in bytes) its segment number and its offset. +sub lsn_to_segment_and_offset +{ + my $lsn = shift; + return ($lsn / $WAL_SEGMENT_SIZE, $lsn % $WAL_SEGMENT_SIZE); +} + +# Write some arbitrary data in WAL for the given segment at LSN. +# This should be called while the cluster is not running. +sub write_wal +{ + my $node = shift; + my $tli = shift; + my $lsn = shift; + my $data = shift; + + my ($segment, $offset) = lsn_to_segment_and_offset($lsn); + my $path = wal_segment_path($node, $tli, $segment); + + open my $fh, "+<:raw", $path or die; + seek($fh, $offset, SEEK_SET) or die; + print $fh $data; + close $fh; +} + +sub format_lsn +{ + my $lsn = shift; + return sprintf("%X/%X", $lsn >> 32, $lsn & 0xffffffff); +} + +# Emit a WAL record of arbitrary size. Returns the end LSN of the +# record inserted, in bytes. +sub emit_message +{ + my $node = shift; + my $size = shift; + return int( + $node->safe_psql( + 'postgres', + "SELECT pg_logical_emit_message(true, '', repeat('a', $size)) - '0/0'" + )); +} + +# Get the current insert LSN of a node, in bytes. +sub get_insert_lsn +{ + my $node = shift; + return int( + $node->safe_psql( + 'postgres', "SELECT pg_current_wal_insert_lsn() - '0/0'")); +} + +# Get GUC value, converted to an int. +sub get_int_setting +{ + my $node = shift; + my $name = shift; + return int( + $node->safe_psql( + 'postgres', + "SELECT setting FROM pg_settings WHERE name = '$name'")); +} + +sub start_of_page +{ + my $lsn = shift; + return $lsn & ~($WAL_BLOCK_SIZE - 1); +} + +sub start_of_next_page +{ + my $lsn = shift; + return start_of_page($lsn) + $WAL_BLOCK_SIZE; +} + +# Build a fake WAL record header based on the data given by the caller. +# This needs to follow the format of the C structure XLogRecord. To +# be inserted with write_wal(). +sub build_record_header +{ + my $xl_tot_len = shift; + my $xl_xid = shift || 0; + my $xl_prev = shift || 0; + my $xl_info = shift || 0; + my $xl_rmid = shift || 0; + my $xl_crc = shift || 0; + + # This needs to follow the structure XLogRecord: + # I for xl_tot_len + # I for xl_xid + # II for xl_prev + # C for xl_info + # C for xl_rmid + # BB for two bytes of padding + # I for xl_crc + return pack("IIIICCBBI", + $xl_tot_len, $xl_xid, + $BIG_ENDIAN ? 0 : $xl_prev, + $BIG_ENDIAN ? $xl_prev : 0, + $xl_info, $xl_rmid, 0, 0, $xl_crc); +} + +# Build a fake WAL page header, based on the data given by the caller +# This needs to follow the format of the C structure XLogPageHeaderData. +# To be inserted with write_wal(). +sub build_page_header +{ + my $xlp_magic = shift; + my $xlp_info = shift || 0; + my $xlp_tli = shift || 0; + my $xlp_pageaddr = shift || 0; + my $xlp_rem_len = shift || 0; + + # This needs to follow the structure XLogPageHeaderData: + # S for xlp_magic + # S for xlp_info + # I for xlp_tli + # II for xlp_pageaddr + # I for xlp_rem_len + return pack("SSIIII", + $xlp_magic, $xlp_info, $xlp_tli, + $BIG_ENDIAN ? 0 : $xlp_pageaddr, + $BIG_ENDIAN ? $xlp_pageaddr : 0, $xlp_rem_len); +} + +# Make sure we are far away enough from the end of a page that we could insert +# a couple of small records. This inserts a few records of a fixed size, until +# the threshold gets close enough to the end of the WAL page inserting records +# to. +sub advance_out_of_record_splitting_zone +{ + my $node = shift; + + my $page_threshold = 2000; + my $end_lsn = get_insert_lsn($node); + my $page_offset = $end_lsn % $WAL_BLOCK_SIZE; + while ($page_offset >= $WAL_BLOCK_SIZE - $page_threshold) + { + emit_message($node, $page_threshold); + $end_lsn = get_insert_lsn($node); + $page_offset = $end_lsn % $WAL_BLOCK_SIZE; + } + return $end_lsn; +} + +# Advance so close to the end of a page that an XLogRecordHeader would not +# fit on it. +sub advance_to_record_splitting_zone +{ + my $node = shift; + + my $end_lsn = get_insert_lsn($node); + my $page_offset = $end_lsn % $WAL_BLOCK_SIZE; + + # Get fairly close to the end of a page in big steps + while ($page_offset <= $WAL_BLOCK_SIZE - 512) + { + emit_message($node, $WAL_BLOCK_SIZE - $page_offset - 256); + $end_lsn = get_insert_lsn($node); + $page_offset = $end_lsn % $WAL_BLOCK_SIZE; + } + + # Calibrate our message size so that we can get closer 8 bytes at + # a time. + my $message_size = $WAL_BLOCK_SIZE - 80; + while ($page_offset <= $WAL_BLOCK_SIZE - $RECORD_HEADER_SIZE) + { + emit_message($node, $message_size); + $end_lsn = get_insert_lsn($node); + + my $old_offset = $page_offset; + $page_offset = $end_lsn % $WAL_BLOCK_SIZE; + + # Adjust the message size until it causes 8 bytes changes in + # offset, enough to be able to split a record header. + my $delta = $page_offset - $old_offset; + if ($delta > 8) + { + $message_size -= 8; + } + elsif ($delta <= 0) + { + $message_size += 8; + } + } + return $end_lsn; +} + +# Setup a new node. The configuration chosen here minimizes the number +# of arbitrary records that could get generated in a cluster. Enlarging +# checkpoint_timeout avoids noise with checkpoint activity. wal_level +# set to "minimal" avoids random standby snapshot records. Autovacuum +# could also trigger randomly, generating random WAL activity of its own. +my $node = PostgreSQL::Test::Cluster->new("node"); +$node->init; +$node->append_conf( + 'postgresql.conf', + q[wal_level = minimal + autovacuum = off + checkpoint_timeout = '30min' +]); +$node->start; +$node->safe_psql('postgres', "CREATE TABLE t AS SELECT 42"); + +$WAL_SEGMENT_SIZE = get_int_setting($node, 'wal_segment_size'); +$WAL_BLOCK_SIZE = get_int_setting($node, 'wal_block_size'); +$TLI = $node->safe_psql('postgres', + "SELECT timeline_id FROM pg_control_checkpoint();"); + +my $end_lsn; +my $prev_lsn; + +########################################################################### +note "Single-page end-of-WAL detection"; +########################################################################### + +# xl_tot_len is 0 (a common case, we hit trailing zeroes). +emit_message($node, 0); +$end_lsn = advance_out_of_record_splitting_zone($node); +$node->stop('immediate'); +my $log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "invalid record length at .*: wanted 24, got 0", $log_size + ), + "xl_tot_len zero"); + +# xl_tot_len is < 24 (presumably recycled garbage). +emit_message($node, 0); +$end_lsn = advance_out_of_record_splitting_zone($node); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, build_record_header(23)); +$log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "invalid record length at .*: wanted 24, got 23", + $log_size), + "xl_tot_len short"); + +# xl_tot_len in final position, not big enough to span into a new page but +# also not eligible for regular record header validation +emit_message($node, 0); +$end_lsn = advance_to_record_splitting_zone($node); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, build_record_header(1)); +$log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "invalid record length at .*: wanted 24, got 1", $log_size + ), + "xl_tot_len short at end-of-page"); + +# Need more pages, but xl_prev check fails first. +emit_message($node, 0); +$end_lsn = advance_out_of_record_splitting_zone($node); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef)); +$log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "record with incorrect prev-link 0/DEADBEEF at .*", $log_size), + "xl_prev bad"); + +# xl_crc check fails. +emit_message($node, 0); +advance_out_of_record_splitting_zone($node); +$end_lsn = emit_message($node, 10); +$node->stop('immediate'); +# Corrupt a byte in that record, breaking its CRC. +write_wal($node, $TLI, $end_lsn - 8, '!'); +$log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "incorrect resource manager data checksum in record at .*", $log_size + ), + "xl_crc bad"); + + +########################################################################### +note "Multi-page end-of-WAL detection, header is not split"; +########################################################################### + +# This series of tests requires a valid xl_prev set in the record header +# written to WAL. + +# Good xl_prev, we hit zero page next (zero magic). +emit_message($node, 0); +$prev_lsn = advance_out_of_record_splitting_zone($node); +$end_lsn = emit_message($node, 0); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn)); +$log_size = -s $node->logfile; +$node->start; +ok($node->log_contains("invalid magic number 0000 ", $log_size), + "xlp_magic zero"); + +# Good xl_prev, we hit garbage page next (bad magic). +emit_message($node, 0); +$prev_lsn = advance_out_of_record_splitting_zone($node); +$end_lsn = emit_message($node, 0); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header(0xcafe, 0, 1, 0)); +$log_size = -s $node->logfile; +$node->start; +ok($node->log_contains("invalid magic number CAFE ", $log_size), + "xlp_magic bad"); + +# Good xl_prev, we hit typical recycled page (good xlp_magic, bad +# xlp_pageaddr). +emit_message($node, 0); +$prev_lsn = advance_out_of_record_splitting_zone($node); +$end_lsn = emit_message($node, 0); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header($XLP_PAGE_MAGIC, 0, 1, 0xbaaaaaad)); +$log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "unexpected pageaddr 0/BAAAAAAD ", $log_size), + "xlp_pageaddr bad"); + +# Good xl_prev, xlp_magic, xlp_pageaddr, but bogus xlp_info. +emit_message($node, 0); +$prev_lsn = advance_out_of_record_splitting_zone($node); +$end_lsn = emit_message($node, 0); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header( + $XLP_PAGE_MAGIC, 0x1234, 1, start_of_next_page($end_lsn))); +$log_size = -s $node->logfile; +$node->start; +ok($node->log_contains("invalid info bits 1234 ", $log_size), + "xlp_info bad"); + +# Good xl_prev, xlp_magic, xlp_pageaddr, but xlp_info doesn't mention +# continuation record. +emit_message($node, 0); +$prev_lsn = advance_out_of_record_splitting_zone($node); +$end_lsn = emit_message($node, 0); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header($XLP_PAGE_MAGIC, 0, 1, start_of_next_page($end_lsn))); +$log_size = -s $node->logfile; +$node->start; +ok($node->log_contains("there is no contrecord flag at .*", $log_size), + "xlp_info lacks XLP_FIRST_IS_CONTRECORD"); + +# Good xl_prev, xlp_magic, xlp_pageaddr, xlp_info but xlp_rem_len doesn't add +# up. +emit_message($node, 0); +$prev_lsn = advance_out_of_record_splitting_zone($node); +$end_lsn = emit_message($node, 0); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header( + $XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD, + 1, start_of_next_page($end_lsn), + 123456)); +$log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "invalid contrecord length 123456 .* at .*", $log_size), + "xlp_rem_len bad"); + + +########################################################################### +note "Multi-page, but header is split, so page checks are done first"; +########################################################################### + +# xl_prev is bad and xl_tot_len is too big, but we'll check xlp_magic first. +emit_message($node, 0); +$end_lsn = advance_to_record_splitting_zone($node); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef)); +$log_size = -s $node->logfile; +$node->start; +# The bytes of the split record header that spill over to the new page +# overwrite xlp_magic. In upstream the spill-over is the xl_info/xl_rmid +# padding bytes, which are zero here, but Cloudberry writes an extra +# distributed-commit WAL record after every statement, so the calibrated +# insert LSN ends up 8 bytes further and the xl_prev bytes land on +# xlp_magic instead. Accept any magic number here; the point of the +# test is that the page header is validated first. +ok( $node->log_contains( + "invalid magic number [0-9A-F]{4} in log segment", + $log_size), + "xlp_magic zero (split record header)"); + +# And we'll also check xlp_pageaddr before any header checks. +emit_message($node, 0); +$end_lsn = advance_to_record_splitting_zone($node); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header( + $XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD, 1, 0xbaaaaaad)); +$log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "unexpected pageaddr 0/BAAAAAAD ", $log_size), + "xlp_pageaddr bad (split record header)"); + +# We'll also discover that xlp_rem_len doesn't add up before any +# header checks, +emit_message($node, 0); +$end_lsn = advance_to_record_splitting_zone($node); +$node->stop('immediate'); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header( + $XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD, + 1, start_of_next_page($end_lsn), + 123456)); +$log_size = -s $node->logfile; +$node->start; +ok( $node->log_contains( + "invalid contrecord length 123456 .* at .*", $log_size), + "xlp_rem_len bad (split record header)"); + +done_testing(); diff --git a/src/test/regress/expected/brin_multi.out b/src/test/regress/expected/brin_multi.out index 677fb45f1fd..6b312a73462 100644 --- a/src/test/regress/expected/brin_multi.out +++ b/src/test/regress/expected/brin_multi.out @@ -20,6 +20,8 @@ CREATE TABLE brintest_multi ( uuidcol uuid, lsncol pg_lsn ) WITH (fillfactor=10); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'int8col' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. INSERT INTO brintest_multi SELECT 142857 * tenthous, thousand, @@ -111,6 +113,8 @@ CREATE TABLE brinopers_multi (colname name, typ text, op text[], value text[], matches int[], check (cardinality(op) = cardinality(value)), check (cardinality(op) = cardinality(matches))); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'colname' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. INSERT INTO brinopers_multi VALUES ('int2col', 'int2', '{>, >=, =, <=, <}', @@ -355,16 +359,18 @@ insert into public.brintest_multi (float8col) values (real 'nan'); UPDATE brintest_multi SET int8col = int8col * int4col; -- Test handling of inet netmasks with inet_minmax_multi_ops CREATE TABLE brin_test_inet (a inet); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE INDEX ON brin_test_inet USING brin (a inet_minmax_multi_ops); INSERT INTO brin_test_inet VALUES ('127.0.0.1/0'); INSERT INTO brin_test_inet VALUES ('0.0.0.0/12'); DROP TABLE brin_test_inet; -- Tests for brin_summarize_new_values SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index -ERROR: "brintest_multi" is not an index +ERROR: "brintest_multi" is not an index (seg0 slice1 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=4083798) CONTEXT: SQL function "brin_summarize_new_values" statement 1 SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index -ERROR: "tenk1_unique1" is not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index (seg0 slice1 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=4083798) CONTEXT: SQL function "brin_summarize_new_values" statement 1 SELECT brin_summarize_new_values('brinidx_multi') > 0 AS result; -- ok, no change expected result @@ -395,6 +401,8 @@ SELECT brin_desummarize_range('brinidx_multi', 100000000); -- test building an index with many values, to force compaction of the buffer CREATE TABLE brin_large_range (a int4); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. INSERT INTO brin_large_range SELECT i FROM generate_series(1,10000) s(i); CREATE INDEX brin_large_range_idx ON brin_large_range USING brin (a int4_minmax_multi_ops); DROP TABLE brin_large_range; @@ -402,6 +410,8 @@ DROP TABLE brin_large_range; CREATE TABLE brin_summarize_multi ( value int ) WITH (fillfactor=10, autovacuum_enabled=false); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'value' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); -- Fill a few pages DO $$ @@ -443,21 +453,23 @@ SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); -- invalid block number values SELECT brin_summarize_range('brin_summarize_multi_idx', -1); -ERROR: block number out of range: -1 +ERROR: block number out of range: -1 (seg1 slice1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=4083799) +CONTEXT: SQL function "brin_summarize_range" statement 1 SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); -ERROR: block number out of range: 4294967296 +ERROR: block number out of range: 4294967296 (seg1 slice1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=4083799) +CONTEXT: SQL function "brin_summarize_range" statement 1 -- test brin cost estimates behave sanely based on correlation of values CREATE TABLE brin_test_multi (a INT, b INT); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); VACUUM ANALYZE brin_test_multi; -- Ensure brin index is used when columns are perfectly correlated +--GPDB_14_MERGE_FIXME: plan shapes differ between the normal and +--force-parallel (ic-cbdb-parallel) suites; mask via start_ignore. --start_ignore ---GPDB_14_MERGE_FIXME ---It should choose bitmap index scan, but seq scan here, which is caused by ---inaccurate index correlation calculation in compute_scalar_stats. ---end_ignore EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; QUERY PLAN ------------------------------------------ @@ -477,3 +489,163 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; Optimizer: Postgres query optimizer (4 rows) +--end_ignore +-- test overflows during CREATE INDEX with extreme timestamp values +CREATE TABLE brin_timestamp_test(a TIMESTAMPTZ); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +SET datestyle TO iso; +-- values close to timetamp minimum +INSERT INTO brin_timestamp_test +SELECT '4713-01-01 00:00:01 BC'::timestamptz + (i || ' seconds')::interval + FROM generate_series(1,30) s(i); +-- values close to timetamp maximum +INSERT INTO brin_timestamp_test +SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval + FROM generate_series(1,30) s(i); +CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1); +DROP TABLE brin_timestamp_test; +-- test overflows during CREATE INDEX with extreme date values +CREATE TABLE brin_date_test(a DATE); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +-- insert values close to date minimum +INSERT INTO brin_date_test SELECT '4713-01-01 BC'::date + i FROM generate_series(1, 30) s(i); +-- insert values close to date minimum +INSERT INTO brin_date_test SELECT '5874897-12-01'::date + i FROM generate_series(1, 30) s(i); +CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1); +SET enable_seqscan = off; +-- make sure the ranges were built correctly and 2023-01-01 eliminates all +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date; + QUERY PLAN +------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1) + Recheck Cond: (a = '2023-01-01'::date) + -> Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '2023-01-01'::date) + Optimizer: Postgres query optimizer +(6 rows) + +--end_ignore +DROP TABLE brin_date_test; +RESET enable_seqscan; +-- test handling of infinite timestamp values +CREATE TABLE brin_timestamp_test(a TIMESTAMP); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +INSERT INTO brin_timestamp_test VALUES ('-infinity'), ('infinity'); +INSERT INTO brin_timestamp_test +SELECT i FROM generate_series('2000-01-01'::timestamp, '2000-02-09'::timestamp, '1 day'::interval) s(i); +CREATE INDEX ON brin_timestamp_test USING brin (a timestamp_minmax_multi_ops) WITH (pages_per_range=1); +SET enable_seqscan = off; +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_timestamp_test WHERE a = '2023-01-01'::timestamp; + QUERY PLAN +------------------------------------------------------------------------------------ + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_timestamp_test (actual rows=0 loops=1) + Recheck Cond: (a = '2023-01-01 00:00:00'::timestamp without time zone) + -> Bitmap Index Scan on brin_timestamp_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '2023-01-01 00:00:00'::timestamp without time zone) + Optimizer: Postgres query optimizer +(6 rows) + +--end_ignore +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_timestamp_test WHERE a = '1900-01-01'::timestamp; + QUERY PLAN +------------------------------------------------------------------------------------ + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_timestamp_test (actual rows=0 loops=1) + Recheck Cond: (a = '1900-01-01 00:00:00'::timestamp without time zone) + -> Bitmap Index Scan on brin_timestamp_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '1900-01-01 00:00:00'::timestamp without time zone) + Optimizer: Postgres query optimizer +(6 rows) + +--end_ignore +DROP TABLE brin_timestamp_test; +RESET enable_seqscan; +-- test handling of infinite date values +CREATE TABLE brin_date_test(a DATE); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +INSERT INTO brin_date_test VALUES ('-infinity'), ('infinity'); +INSERT INTO brin_date_test SELECT '2000-01-01'::date + i FROM generate_series(1, 40) s(i); +CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1); +SET enable_seqscan = off; +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date; + QUERY PLAN +------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1) + Recheck Cond: (a = '2023-01-01'::date) + -> Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '2023-01-01'::date) + Optimizer: Postgres query optimizer +(6 rows) + +--end_ignore +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '1900-01-01'::date; + QUERY PLAN +------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1) + Recheck Cond: (a = '1900-01-01'::date) + -> Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '1900-01-01'::date) + Optimizer: Postgres query optimizer +(6 rows) + +--end_ignore +DROP TABLE brin_date_test; +RESET enable_seqscan; +RESET datestyle; +-- test handling of overflow for interval values +CREATE TABLE brin_interval_test(a INTERVAL); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM generate_series(-178000000, -177999980) s(i); +INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM generate_series( 177999980, 178000000) s(i); +CREATE INDEX ON brin_interval_test USING brin (a interval_minmax_multi_ops) WITH (pages_per_range=1); +SET enable_seqscan = off; +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_interval_test WHERE a = '-30 years'::interval; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1) + Recheck Cond: (a = '@ 30 years ago'::interval) + -> Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '@ 30 years ago'::interval) + Optimizer: Postgres query optimizer +(6 rows) + +--end_ignore +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_interval_test WHERE a = '30 years'::interval; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1) + Recheck Cond: (a = '@ 30 years'::interval) + -> Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '@ 30 years'::interval) + Optimizer: Postgres query optimizer +(6 rows) + +--end_ignore +DROP TABLE brin_interval_test; +RESET enable_seqscan; +RESET datestyle; diff --git a/src/test/regress/expected/brin_multi_optimizer_1.out b/src/test/regress/expected/brin_multi_optimizer_1.out index d56000ba8fb..86b7c681419 100644 --- a/src/test/regress/expected/brin_multi_optimizer_1.out +++ b/src/test/regress/expected/brin_multi_optimizer_1.out @@ -20,6 +20,8 @@ CREATE TABLE brintest_multi ( uuidcol uuid, lsncol pg_lsn ) WITH (fillfactor=10); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'int8col' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. INSERT INTO brintest_multi SELECT 142857 * tenthous, thousand, @@ -111,6 +113,8 @@ CREATE TABLE brinopers_multi (colname name, typ text, op text[], value text[], matches int[], check (cardinality(op) = cardinality(value)), check (cardinality(op) = cardinality(matches))); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'colname' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. INSERT INTO brinopers_multi VALUES ('int2col', 'int2', '{>, >=, =, <=, <}', @@ -505,16 +509,18 @@ insert into public.brintest_multi (float8col) values (real 'nan'); UPDATE brintest_multi SET int8col = int8col * int4col; -- Test handling of inet netmasks with inet_minmax_multi_ops CREATE TABLE brin_test_inet (a inet); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE INDEX ON brin_test_inet USING brin (a inet_minmax_multi_ops); INSERT INTO brin_test_inet VALUES ('127.0.0.1/0'); INSERT INTO brin_test_inet VALUES ('0.0.0.0/12'); DROP TABLE brin_test_inet; -- Tests for brin_summarize_new_values SELECT brin_summarize_new_values('brintest_multi'); -- error, not an index -ERROR: "brintest_multi" is not an index +ERROR: "brintest_multi" is not an index (seg0 slice1 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=4107940) CONTEXT: SQL function "brin_summarize_new_values" statement 1 SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index -ERROR: "tenk1_unique1" is not a BRIN index +ERROR: "tenk1_unique1" is not a BRIN index (seg0 slice1 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=4107940) CONTEXT: SQL function "brin_summarize_new_values" statement 1 SELECT brin_summarize_new_values('brinidx_multi') > 0 AS result; -- ok, no change expected result @@ -545,6 +551,8 @@ SELECT brin_desummarize_range('brinidx_multi', 100000000); -- test building an index with many values, to force compaction of the buffer CREATE TABLE brin_large_range (a int4); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. INSERT INTO brin_large_range SELECT i FROM generate_series(1,10000) s(i); CREATE INDEX brin_large_range_idx ON brin_large_range USING brin (a int4_minmax_multi_ops); DROP TABLE brin_large_range; @@ -552,6 +560,8 @@ DROP TABLE brin_large_range; CREATE TABLE brin_summarize_multi ( value int ) WITH (fillfactor=10, autovacuum_enabled=false); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'value' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE INDEX brin_summarize_multi_idx ON brin_summarize_multi USING brin (value) WITH (pages_per_range=2); -- Fill a few pages DO $$ @@ -593,21 +603,23 @@ SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967295); -- invalid block number values SELECT brin_summarize_range('brin_summarize_multi_idx', -1); -ERROR: block number out of range: -1 +ERROR: block number out of range: -1 (seg0 slice1 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=4107940) +CONTEXT: SQL function "brin_summarize_range" statement 1 SELECT brin_summarize_range('brin_summarize_multi_idx', 4294967296); -ERROR: block number out of range: 4294967296 +ERROR: block number out of range: 4294967296 (seg0 slice1 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=4107940) +CONTEXT: SQL function "brin_summarize_range" statement 1 -- test brin cost estimates behave sanely based on correlation of values CREATE TABLE brin_test_multi (a INT, b INT); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. INSERT INTO brin_test_multi SELECT x/100,x%100 FROM generate_series(1,10000) x(x); CREATE INDEX brin_test_multi_a_idx ON brin_test_multi USING brin (a) WITH (pages_per_range = 2); CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages_per_range = 2); VACUUM ANALYZE brin_test_multi; -- Ensure brin index is used when columns are perfectly correlated +--GPDB_14_MERGE_FIXME: plan shapes differ between the normal and +--force-parallel (ic-cbdb-parallel) suites; mask via start_ignore. --start_ignore ---GPDB_14_MERGE_FIXME ---It should choose bitmap index scan, but seq scan here, which is caused by ---inaccurate index correlation calculation in compute_scalar_stats. ---end_ignore EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; QUERY PLAN -------------------------------------------------------- @@ -616,7 +628,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; Recheck Cond: (a = 1) -> Bitmap Index Scan on brin_test_multi_a_idx Index Cond: (a = 1) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) -- Ensure brin index is not used when values are not correlated @@ -628,6 +640,166 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; Recheck Cond: (b = 1) -> Bitmap Index Scan on brin_test_multi_b_idx Index Cond: (b = 1) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA +(6 rows) + +--end_ignore +-- test overflows during CREATE INDEX with extreme timestamp values +CREATE TABLE brin_timestamp_test(a TIMESTAMPTZ); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +SET datestyle TO iso; +-- values close to timetamp minimum +INSERT INTO brin_timestamp_test +SELECT '4713-01-01 00:00:01 BC'::timestamptz + (i || ' seconds')::interval + FROM generate_series(1,30) s(i); +-- values close to timetamp maximum +INSERT INTO brin_timestamp_test +SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval + FROM generate_series(1,30) s(i); +CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1); +DROP TABLE brin_timestamp_test; +-- test overflows during CREATE INDEX with extreme date values +CREATE TABLE brin_date_test(a DATE); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +-- insert values close to date minimum +INSERT INTO brin_date_test SELECT '4713-01-01 BC'::date + i FROM generate_series(1, 30) s(i); +-- insert values close to date minimum +INSERT INTO brin_date_test SELECT '5874897-12-01'::date + i FROM generate_series(1, 30) s(i); +CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1); +SET enable_seqscan = off; +-- make sure the ranges were built correctly and 2023-01-01 eliminates all +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date; + QUERY PLAN +------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1) + Recheck Cond: (a = '2023-01-01'::date) + -> Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '2023-01-01'::date) + Optimizer: GPORCA +(6 rows) + +--end_ignore +DROP TABLE brin_date_test; +RESET enable_seqscan; +-- test handling of infinite timestamp values +CREATE TABLE brin_timestamp_test(a TIMESTAMP); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +INSERT INTO brin_timestamp_test VALUES ('-infinity'), ('infinity'); +INSERT INTO brin_timestamp_test +SELECT i FROM generate_series('2000-01-01'::timestamp, '2000-02-09'::timestamp, '1 day'::interval) s(i); +CREATE INDEX ON brin_timestamp_test USING brin (a timestamp_minmax_multi_ops) WITH (pages_per_range=1); +SET enable_seqscan = off; +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_timestamp_test WHERE a = '2023-01-01'::timestamp; + QUERY PLAN +------------------------------------------------------------------------------------ + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_timestamp_test (actual rows=0 loops=1) + Recheck Cond: (a = '2023-01-01 00:00:00'::timestamp without time zone) + -> Bitmap Index Scan on brin_timestamp_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '2023-01-01 00:00:00'::timestamp without time zone) + Optimizer: GPORCA (6 rows) +--end_ignore +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_timestamp_test WHERE a = '1900-01-01'::timestamp; + QUERY PLAN +------------------------------------------------------------------------------------ + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_timestamp_test (actual rows=0 loops=1) + Recheck Cond: (a = '1900-01-01 00:00:00'::timestamp without time zone) + -> Bitmap Index Scan on brin_timestamp_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '1900-01-01 00:00:00'::timestamp without time zone) + Optimizer: GPORCA +(6 rows) + +--end_ignore +DROP TABLE brin_timestamp_test; +RESET enable_seqscan; +-- test handling of infinite date values +CREATE TABLE brin_date_test(a DATE); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +INSERT INTO brin_date_test VALUES ('-infinity'), ('infinity'); +INSERT INTO brin_date_test SELECT '2000-01-01'::date + i FROM generate_series(1, 40) s(i); +CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1); +SET enable_seqscan = off; +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date; + QUERY PLAN +------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1) + Recheck Cond: (a = '2023-01-01'::date) + -> Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '2023-01-01'::date) + Optimizer: GPORCA +(6 rows) + +--end_ignore +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '1900-01-01'::date; + QUERY PLAN +------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1) + Recheck Cond: (a = '1900-01-01'::date) + -> Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '1900-01-01'::date) + Optimizer: GPORCA +(6 rows) + +--end_ignore +DROP TABLE brin_date_test; +RESET enable_seqscan; +RESET datestyle; +-- test handling of overflow for interval values +CREATE TABLE brin_interval_test(a INTERVAL); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM generate_series(-178000000, -177999980) s(i); +INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM generate_series( 177999980, 178000000) s(i); +CREATE INDEX ON brin_interval_test USING brin (a interval_minmax_multi_ops) WITH (pages_per_range=1); +SET enable_seqscan = off; +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_interval_test WHERE a = '-30 years'::interval; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1) + Recheck Cond: (a = '@ 30 years ago'::interval) + -> Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '@ 30 years ago'::interval) + Optimizer: GPORCA +(6 rows) + +--end_ignore +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_interval_test WHERE a = '30 years'::interval; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (actual rows=0 loops=1) + -> Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1) + Recheck Cond: (a = '@ 30 years'::interval) + -> Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1) + Index Cond: (a = '@ 30 years'::interval) + Optimizer: GPORCA +(6 rows) + +--end_ignore +DROP TABLE brin_interval_test; +RESET enable_seqscan; +RESET datestyle; diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 7d20cc2bdc4..042108476da 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -3,8 +3,11 @@ -- CREATE TABLE a (aa TEXT) distributed randomly; CREATE TABLE b (bb TEXT) INHERITS (a); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE c (cc TEXT) INHERITS (a); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE d (dd TEXT) INHERITS (b,c,a); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "aa" NOTICE: merging multiple inherited definitions of column "aa" INSERT INTO a(aa) VALUES('aaa'); @@ -34,119 +37,119 @@ INSERT INTO d(aa) VALUES('dddddddd'); SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+---------- - a | aaa a | aaaa - a | aaaaa - a | aaaaaa a | aaaaaaa + b | bbbb + b | bbbbbbb + c | cccc + c | ccccccc + d | ddddddd + d | dddddddd + a | aaa + a | aaaaaa a | aaaaaaaa b | bbb - b | bbbb b | bbbbb - b | bbbbbb - b | bbbbbbb b | bbbbbbbb c | ccc - c | cccc + d | dddd + d | ddddd + d | dddddd + a | aaaaa + b | bbbbbb c | ccccc c | cccccc - c | ccccccc c | cccccccc d | ddd - d | dddd - d | ddddd - d | dddddd - d | ddddddd - d | dddddddd (24 rows) SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+----------+---- - b | bbb | - b | bbbb | - b | bbbbb | b | bbbbbb | + d | ddd | + b | bbbb | b | bbbbbbb | + d | ddddddd | + d | dddddddd | + b | bbb | + b | bbbbb | b | bbbbbbbb | - d | ddd | d | dddd | d | ddddd | d | dddddd | - d | ddddddd | - d | dddddddd | (12 rows) SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- - c | ccc | - c | cccc | c | ccccc | c | cccccc | - c | ccccccc | c | cccccccc | d | ddd | + c | cccc | + c | ccccccc | + d | ddddddd | + d | dddddddd | + c | ccc | d | dddd | d | ddddd | d | dddddd | - d | ddddddd | - d | dddddddd | (12 rows) SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; relname | aa | bb | cc | dd ---------+----------+----+----+---- - d | ddd | | | d | dddd | | | d | ddddd | | | d | dddddd | | | d | ddddddd | | | d | dddddddd | | | + d | ddd | | | (6 rows) SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+---------- a | aaa - a | aaaa - a | aaaaa a | aaaaaa - a | aaaaaaa a | aaaaaaaa + a | aaaa + a | aaaaaaa + a | aaaaa (6 rows) SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+----------+---- + b | bbbbbb | b | bbb | - b | bbbb | b | bbbbb | - b | bbbbbb | - b | bbbbbbb | b | bbbbbbbb | + b | bbbb | + b | bbbbbbb | (6 rows) SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- - c | ccc | - c | cccc | c | ccccc | c | cccccc | - c | ccccccc | c | cccccccc | + c | ccc | + c | cccc | + c | ccccccc | (6 rows) SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; relname | aa | bb | cc | dd ---------+----------+----+----+---- - d | ddd | | | d | dddd | | | d | ddddd | | | d | dddddd | | | d | ddddddd | | | d | dddddddd | | | + d | ddd | | | (6 rows) UPDATE a SET aa='zzzz' WHERE aa='aaaa'; @@ -158,81 +161,81 @@ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+---------- a | zzzz - a | zzzzz a | zzzzzz + b | bbbb + b | bbbbbbb + c | cccc + c | ccccccc + d | ddddddd + d | dddddddd a | zzzzzz a | zzzzzz a | zzzzzz b | bbb - b | bbbb b | bbbbb - b | bbbbbb - b | bbbbbbb b | bbbbbbbb c | ccc - c | cccc + d | dddd + d | ddddd + d | dddddd + a | zzzzz + b | bbbbbb c | ccccc c | cccccc - c | ccccccc c | cccccccc d | ddd - d | dddd - d | ddddd - d | dddddd - d | ddddddd - d | dddddddd (24 rows) SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+----------+---- - b | bbb | b | bbbb | - b | bbbbb | - b | bbbbbb | b | bbbbbbb | + d | ddddddd | + d | dddddddd | + b | bbb | + b | bbbbb | b | bbbbbbbb | - d | ddd | d | dddd | d | ddddd | d | dddddd | - d | ddddddd | - d | dddddddd | + b | bbbbbb | + d | ddd | (12 rows) SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- - c | ccc | c | cccc | + c | ccccccc | + d | ddddddd | + d | dddddddd | c | ccccc | c | cccccc | - c | ccccccc | c | cccccccc | d | ddd | + c | ccc | d | dddd | d | ddddd | d | dddddd | - d | ddddddd | - d | dddddddd | (12 rows) SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; relname | aa | bb | cc | dd ---------+----------+----+----+---- - d | ddd | | | + d | ddddddd | | | + d | dddddddd | | | d | dddd | | | d | ddddd | | | d | dddddd | | | - d | ddddddd | | | - d | dddddddd | | | + d | ddd | | | (6 rows) SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+-------- - a | zzzz a | zzzzz + a | zzzz a | zzzzzz a | zzzzzz a | zzzzzz @@ -243,11 +246,11 @@ SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+----------+---- b | bbb | - b | bbbb | b | bbbbb | + b | bbbbbbbb | b | bbbbbb | + b | bbbb | b | bbbbbbb | - b | bbbbbbbb | (6 rows) SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; @@ -255,19 +258,19 @@ SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; ---------+----------+---- c | ccc | c | cccc | + c | ccccccc | c | ccccc | c | cccccc | - c | ccccccc | c | cccccccc | (6 rows) SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; relname | aa | bb | cc | dd ---------+----------+----+----+---- - d | ddd | | | d | dddd | | | d | ddddd | | | d | dddddd | | | + d | ddd | | | d | ddddddd | | | d | dddddddd | | | (6 rows) @@ -276,28 +279,28 @@ UPDATE b SET aa='new'; SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+---------- - a | zzzz - a | zzzzz a | zzzzzz a | zzzzzz a | zzzzzz - a | zzzzzz - b | new - b | new - b | new b | new b | new b | new c | ccc - c | cccc - c | ccccc - c | cccccc - c | ccccccc - c | cccccccc d | new d | new d | new + a | zzzzz + b | new + c | ccccc + c | cccccc + c | cccccccc d | new + a | zzzz + a | zzzzzz + b | new + b | new + c | cccc + c | ccccccc d | new d | new (24 rows) @@ -307,15 +310,15 @@ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; ---------+-----+---- b | new | b | new | + d | new | + d | new | b | new | b | new | b | new | - b | new | - d | new | - d | new | d | new | d | new | d | new | + b | new | d | new | (12 rows) @@ -323,15 +326,15 @@ SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- c | ccc | - c | cccc | - c | ccccc | - c | cccccc | - c | ccccccc | - c | cccccccc | d | new | d | new | d | new | + c | ccccc | + c | cccccc | + c | cccccccc | d | new | + c | cccc | + c | ccccccc | d | new | d | new | (12 rows) @@ -350,11 +353,11 @@ SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+-------- - a | zzzz - a | zzzzz a | zzzzzz a | zzzzzz a | zzzzzz + a | zzzzz + a | zzzz a | zzzzzz (6 rows) @@ -373,11 +376,11 @@ SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- c | ccc | - c | cccc | c | ccccc | c | cccccc | - c | ccccccc | c | cccccccc | + c | cccc | + c | ccccccc | (6 rows) SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; @@ -399,19 +402,19 @@ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; a | new a | new a | new - a | new - a | new - a | new - b | new - b | new - b | new b | new b | new b | new d | new d | new d | new + a | new + b | new d | new + a | new + a | new + b | new + b | new d | new d | new (18 rows) @@ -419,17 +422,17 @@ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+-----+---- - b | new | - b | new | - b | new | b | new | b | new | b | new | d | new | d | new | d | new | + b | new | + b | new | d | new | d | new | + b | new | d | new | (12 rows) @@ -537,11 +540,43 @@ SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; -- Confirm PRIMARY KEY adds NOT NULL constraint to child table CREATE TEMP TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a); INSERT INTO z VALUES (NULL, 'text'); -- should fail -ERROR: null value in column "aa" of relation "z" violates not-null constraint +ERROR: null value in column "aa" of relation "z" violates not-null constraint (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3236638) DETAIL: Failing row contains (null, text). +-- Check inherited UPDATE with first child excluded +create table some_tab (f1 int, f2 int, f3 int, check (f1 < 10) no inherit); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +create table some_tab_child () inherits(some_tab); +NOTICE: table has parent, setting distribution columns to match parent table +insert into some_tab_child select i, i+1, 0 from generate_series(1,1000) i; +create index on some_tab_child(f1, f2); +-- while at it, also check that statement-level triggers fire +create function some_tab_stmt_trig_func() returns trigger as +$$begin raise notice 'updating some_tab'; return NULL; end;$$ +language plpgsql; +create trigger some_tab_stmt_trig + before update on some_tab execute function some_tab_stmt_trig_func(); +ERROR: Triggers for statements are not yet supported +explain (costs off) +update some_tab set f3 = 11 where f1 = 12 and f2 = 13; + QUERY PLAN +------------------------------------------------------------------------------------ + Update on some_tab + Update on some_tab_child some_tab_1 + -> Result + -> Index Scan using some_tab_child_f1_f2_idx on some_tab_child some_tab_1 + Index Cond: ((f1 = 12) AND (f2 = 13)) + Optimizer: Postgres query optimizer +(6 rows) + +update some_tab set f3 = 11 where f1 = 12 and f2 = 13; +drop table some_tab cascade; +NOTICE: drop cascades to table some_tab_child +drop function some_tab_stmt_trig_func(); -- Check inherited UPDATE with all children excluded create table some_tab (a int, b int) distributed randomly; create table some_tab_child () inherits (some_tab); +NOTICE: table has parent, setting distribution columns to match parent table insert into some_tab_child values(1,2); explain (verbose, costs off) update some_tab set a = a + 1 where false; @@ -551,8 +586,9 @@ update some_tab set a = a + 1 where false; -> Result Output: (some_tab.a + 1), NULL::oid, NULL::tid, NULL::integer One-Time Filter: false + Settings: optimizer = 'off' Optimizer: Postgres query optimizer -(5 rows) +(6 rows) update some_tab set a = a + 1 where false; explain (verbose, costs off) @@ -566,8 +602,9 @@ update some_tab set a = a + 1 where false returning b, a; -> Result Output: (some_tab.a + 1), NULL::oid, NULL::tid, NULL::integer One-Time Filter: false + Settings: optimizer = 'off' Optimizer: Postgres query optimizer -(8 rows) +(9 rows) update some_tab set a = a + 1 where false returning b, a; b | a @@ -584,9 +621,15 @@ drop table some_tab cascade; NOTICE: drop cascades to table some_tab_child -- Check UPDATE with inherited target and an inherited source table create temp table foo(f1 int, f2 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table foo2(f3 int) inherits (foo); +NOTICE: table has parent, setting distribution columns to match parent table create temp table bar(f1 int, f2 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table bar2(f3 int) inherits (bar); +NOTICE: table has parent, setting distribution columns to match parent table insert into foo values(1,1); insert into foo values(3,3); insert into foo2 values(2,2,2); @@ -635,11 +678,17 @@ select tableoid::regclass::text as relname, bar.* from bar order by 1,2; create table some_tab (a int) distributed randomly; insert into some_tab values (0); create table some_tab_child () inherits (some_tab); +NOTICE: table has parent, setting distribution columns to match parent table insert into some_tab_child values (1); create table parted_tab (a int, b char) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table parted_tab_part1 partition of parted_tab for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table parted_tab_part2 partition of parted_tab for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table parted_tab_part3 partition of parted_tab for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table insert into parted_tab values (1, 'a'), (2, 'a'), (3, 'a'); update parted_tab set b = 'b' from @@ -671,19 +720,27 @@ select tableoid::regclass::text as relname, parted_tab.* from parted_tab order b explain update parted_tab set a = 2 where false; QUERY PLAN -------------------------------------------------------- - Update on parted_tab (cost=0.00..0.00 rows=0 width=0) - -> Result (cost=0.00..0.00 rows=0 width=10) + Update on parted_tab (cost=0.00..0.01 rows=0 width=0) + -> Result (cost=0.00..0.00 rows=0 width=22) One-Time Filter: false -(3 rows) + Optimizer: Postgres query optimizer +(4 rows) drop table parted_tab; -- Check UPDATE with multi-level partitioned inherited target create table mlparted_tab (a int, b char, c text) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mlparted_tab_part1 partition of mlparted_tab for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table mlparted_tab_part2 partition of mlparted_tab for values in (2) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table mlparted_tab_part3 partition of mlparted_tab for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table create table mlparted_tab_part2a partition of mlparted_tab_part2 for values in ('a'); +NOTICE: table has parent, setting distribution columns to match parent table create table mlparted_tab_part2b partition of mlparted_tab_part2 for values in ('b'); +NOTICE: table has parent, setting distribution columns to match parent table insert into mlparted_tab values (1, 'a'), (2, 'a'), (2, 'b'), (3, 'a'); update mlparted_tab mlp set c = 'xxx' from @@ -703,16 +760,25 @@ drop table some_tab cascade; NOTICE: drop cascades to table some_tab_child /* Test multiple inheritance of column defaults */ CREATE TABLE firstparent (tomorrow date default now()::date + 1); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'tomorrow' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE secondparent (tomorrow date default now() :: date + 1); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'tomorrow' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE jointchild () INHERITS (firstparent, secondparent); -- ok +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "tomorrow" CREATE TABLE thirdparent (tomorrow date default now()::date - 1); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'tomorrow' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE otherchild () INHERITS (firstparent, thirdparent); -- not ok +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "tomorrow" ERROR: column "tomorrow" inherits conflicting default values HINT: To resolve the conflict, specify a default explicitly. CREATE TABLE otherchild (tomorrow date default now()) INHERITS (firstparent, thirdparent); -- ok, child resolves ambiguous default +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "tomorrow" NOTICE: merging column "tomorrow" with inherited definition DROP TABLE firstparent, secondparent, jointchild, thirdparent, otherchild; @@ -730,14 +796,21 @@ select * from d; -- column; but we should reject that if any definition was inherited from -- an unrelated parent. create temp table parent1(f1 int, f2 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table parent2(f1 int, f3 bigint); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table childtab(f4 int) inherits(parent1, parent2); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "f1" alter table parent1 alter column f1 type bigint; -- fail, conflict w/parent2 ERROR: cannot alter inherited column "f1" of relation "childtab" alter table parent1 alter column f2 type bigint; -- ok -- Test non-inheritable parent constraints create table p1(ff1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'ff1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. alter table p1 add constraint p1chk check (ff1 > 0) no inherit; alter table p1 add constraint p2chk check (ff1 > 10); -- connoinherit should be true for NO INHERIT constraint @@ -750,6 +823,7 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg -- Test that child does not inherit NO INHERIT constraints create table c1 () inherits (p1); +NOTICE: table has parent, setting distribution columns to match parent table \d p1 Table "public.p1" Column | Type | Collation | Nullable | Default @@ -773,14 +847,19 @@ Distributed by: (ff1) -- Test that child does not override inheritable constraints of the parent create table c2 (constraint p2chk check (ff1 > 10) no inherit) inherits (p1); --fails +NOTICE: table has parent, setting distribution columns to match parent table ERROR: constraint "p2chk" conflicts with inherited constraint on relation "c2" drop table p1 cascade; NOTICE: drop cascades to table c1 -- Tests for casting between the rowtypes of parent and child -- tables. See the pgsql-hackers thread beginning Dec. 4/04 create table base (i integer); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'i' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table derived () inherits (base); +NOTICE: table has parent, setting distribution columns to match parent table create table more_derived (like derived, b int) inherits (derived); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging column "i" with inherited definition insert into derived (i) values (0); select derived::base from derived; @@ -803,26 +882,31 @@ explain (verbose on, costs off) select row(i, b)::more_derived::derived::base fr Output: ((ROW(i, b)::more_derived)::base) -> Seq Scan on public.more_derived Output: (ROW(i, b)::more_derived)::base + Settings: optimizer = 'off' Optimizer: Postgres query optimizer - Settings: optimizer=off (6 rows) explain (verbose on, costs off) select (1, 2)::more_derived::derived::base; - QUERY PLAN ------------------------ + QUERY PLAN +------------------------------------- Result Output: '(1)'::base -(2 rows) + Settings: optimizer = 'off' + Optimizer: Postgres query optimizer +(4 rows) drop table more_derived; drop table derived; drop table base; create table p1(ff1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'ff1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table p2(f1 text); NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create function p2text(p2) returns text as 'select $1.f1' language sql; create table c1(f3 int) inherits(p1,p2); +NOTICE: table has parent, setting distribution columns to match parent table insert into c1 values(123456789, 'hi', 42); select p2text(c1.*) from c1; p2text @@ -835,8 +919,11 @@ drop table c1; drop table p2; drop table p1; CREATE TABLE ac (aa TEXT); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'aa' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. alter table ac add constraint ac_check check (aa is not null); CREATE TABLE bc (bb TEXT) INHERITS (ac); +NOTICE: table has parent, setting distribution columns to match parent table select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; relname | conname | contype | conislocal | coninhcount | consrc ---------+----------+---------+------------+-------------+------------------ @@ -845,10 +932,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg (2 rows) insert into ac (aa) values (NULL); -ERROR: new row for relation "ac" violates check constraint "ac_check" +ERROR: new row for relation "ac" violates check constraint "ac_check" (seg0 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=3236630) DETAIL: Failing row contains (null). insert into bc (aa) values (NULL); -ERROR: new row for relation "bc" violates check constraint "ac_check" +ERROR: new row for relation "bc" violates check constraint "ac_check" (seg0 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=3236630) DETAIL: Failing row contains (null, null). alter table bc drop constraint ac_check; -- fail, disallowed ERROR: cannot drop inherited constraint "ac_check" of relation "bc" @@ -868,10 +955,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg (2 rows) insert into ac (aa) values (NULL); -ERROR: new row for relation "ac" violates check constraint "ac_aa_check" +ERROR: new row for relation "ac" violates check constraint "ac_aa_check" (seg0 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=3236630) DETAIL: Failing row contains (null). insert into bc (aa) values (NULL); -ERROR: new row for relation "bc" violates check constraint "ac_aa_check" +ERROR: new row for relation "bc" violates check constraint "ac_aa_check" (seg0 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=3236630) DETAIL: Failing row contains (null, null). alter table bc drop constraint ac_aa_check; -- fail, disallowed ERROR: cannot drop inherited constraint "ac_aa_check" of relation "bc" @@ -906,7 +993,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg drop table bc; drop table ac; create table ac (a int constraint check_a check (a <> 0)); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging column "a" with inherited definition NOTICE: merging constraint "check_a" with inherited definition select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; @@ -920,8 +1010,13 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg drop table bc; drop table ac; create table ac (a int constraint check_a check (a <> 0)); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table bc (b int constraint check_b check (b <> 0)); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table cc (c int constraint check_c check (c <> 0)) inherits (ac, bc); +NOTICE: table has parent, setting distribution columns to match parent table select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc', 'cc') order by 1,2; relname | conname | contype | conislocal | coninhcount | consrc ---------+---------+---------+------------+-------------+---------- @@ -947,20 +1042,26 @@ drop table cc; drop table bc; drop table ac; create table p1(f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table p2(f2 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f2' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table c1(f3 int) inherits(p1,p2); +NOTICE: table has parent, setting distribution columns to match parent table insert into c1 values(1,-1,2); alter table p2 add constraint cc check (f2>0); -- fail -ERROR: check constraint "cc" of relation "c1" is violated by some row +ERROR: check constraint "cc" of relation "c1" is violated by some row (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) alter table p2 add check (f2>0); -- check it without a name, too -ERROR: check constraint "p2_f2_check" of relation "c1" is violated by some row +ERROR: check constraint "p2_f2_check" of relation "c1" is violated by some row (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) delete from c1; insert into c1 values(1,1,2); alter table p2 add check (f2>0); insert into c1 values(1,-1,2); -- fail -ERROR: new row for relation "c1" violates check constraint "p2_f2_check" +ERROR: new row for relation "c1" violates check constraint "p2_f2_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (1, -1, 2). create table c2(f3 int) inherits(p1,p2); +NOTICE: table has parent, setting distribution columns to match parent table \d c2 Table "public.c2" Column | Type | Collation | Nullable | Default @@ -975,6 +1076,7 @@ Inherits: p1, Distributed by: (f1) create table c3 (f4 int) inherits(c1,c2); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "f1" NOTICE: merging multiple inherited definitions of column "f2" NOTICE: merging multiple inherited definitions of column "f3" @@ -999,7 +1101,10 @@ drop cascades to table c2 drop cascades to table c3 drop table p2 cascade; create table pp1 (f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table cc1 (f2 text, f3 int) inherits (pp1); +NOTICE: table has parent, setting distribution columns to match parent table alter table pp1 add column a1 int check (a1 > 0); \d cc1 Table "public.cc1" @@ -1015,6 +1120,7 @@ Inherits: pp1 Distributed by: (f1) create table cc2(f4 float) inherits(pp1,cc1); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "f1" NOTICE: merging multiple inherited definitions of column "a1" \d cc2 @@ -1058,8 +1164,13 @@ DETAIL: drop cascades to table cc1 drop cascades to table cc2 -- Test for renaming in simple multiple inheritance CREATE TABLE inht1 (a int, b int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE inhs1 (b int, c int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE inhts (d int) INHERITS (inht1, inhs1); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "b" ALTER TABLE inht1 RENAME a TO aa; ALTER TABLE inht1 RENAME b TO bb; -- to be failed @@ -1082,8 +1193,11 @@ Distributed by: (aa) DROP TABLE inhts; -- Test for renaming in diamond inheritance CREATE TABLE inht2 (x int) INHERITS (inht1); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE inht3 (y int) INHERITS (inht1); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE inht4 (z int) INHERITS (inht2, inht3); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "aa" NOTICE: merging multiple inherited definitions of column "b" ALTER TABLE inht1 RENAME aa TO aaa; @@ -1101,6 +1215,7 @@ Inherits: inht2, Distributed by: (aaa) CREATE TABLE inhts (d int) INHERITS (inht2, inhs1); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "b" ALTER TABLE inht1 RENAME aaa TO aaaa; ALTER TABLE inht1 RENAME b TO bb; -- to be failed @@ -1153,6 +1268,7 @@ drop cascades to table inht4 -- Test non-inheritable indices [UNIQUE, EXCLUDE] constraints CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2)); CREATE TABLE test_constraints_inh () INHERITS (test_constraints); +NOTICE: table has parent, setting distribution columns to match parent table \d+ test_constraints Table "public.test_constraints" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -1193,7 +1309,10 @@ CREATE TABLE test_ex_constraints ( dkey inet, EXCLUDE USING gist (dkey inet_ops WITH =, c WITH &&) ); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'dkey' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints); +NOTICE: table has parent, setting distribution columns to match parent table \d+ test_ex_constraints Table "public.test_ex_constraints" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -1229,7 +1348,11 @@ DROP TABLE test_ex_constraints; -- Test non-inheritable foreign key constraints CREATE TABLE test_primary_constraints(id int PRIMARY KEY); CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id)); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'id1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +WARNING: referential integrity (FOREIGN KEY) constraints are not supported in Apache Cloudberry, will not be enforced CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints); +NOTICE: table has parent, setting distribution columns to match parent table \d+ test_primary_constraints Table "public.test_primary_constraints" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -1275,8 +1398,10 @@ DROP TABLE test_primary_constraints; create table inh_fk_1 (a int primary key); insert into inh_fk_1 values (1), (2), (3); create table inh_fk_2 (x int primary key, y int references inh_fk_1 on delete cascade); +WARNING: referential integrity (FOREIGN KEY) constraints are not supported in Apache Cloudberry, will not be enforced insert into inh_fk_2 values (11, 1), (22, 2), (33, 3); create table inh_fk_2_child () inherits (inh_fk_2); +NOTICE: table has parent, setting distribution columns to match parent table insert into inh_fk_2_child values (111, 1), (222, 2); -- The cascading deletion doesn't work on GPDB, because foreign keys are not -- enforced in general. So this produces different result than on upstream. @@ -1301,7 +1426,10 @@ select * from inh_fk_2 order by 1, 2; drop table inh_fk_1, inh_fk_2, inh_fk_2_child; -- Test that parent and child CHECK constraints can be created in either order create table p1(f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table p1_c1() inherits(p1); +NOTICE: table has parent, setting distribution columns to match parent table alter table p1 add constraint inh_check_constraint1 check (f1 > 0); alter table p1_c1 add constraint inh_check_constraint1 check (f1 > 0); NOTICE: merging constraint "inh_check_constraint1" with inherited definition @@ -1323,7 +1451,10 @@ drop table p1 cascade; NOTICE: drop cascades to table p1_c1 -- Test that a valid child can have not-valid parent, but not vice versa create table invalid_check_con(f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table invalid_check_con_child() inherits(invalid_check_con); +NOTICE: table has parent, setting distribution columns to match parent table alter table invalid_check_con_child add constraint inh_check_constraint check(f1 > 0) not valid; alter table invalid_check_con add constraint inh_check_constraint check(f1 > 0); -- fail ERROR: constraint "inh_check_constraint" conflicts with NOT VALID constraint on relation "invalid_check_con_child" @@ -1333,10 +1464,10 @@ alter table invalid_check_con_child add constraint inh_check_constraint check(f1 alter table invalid_check_con add constraint inh_check_constraint check(f1 > 0) not valid; NOTICE: merging constraint "inh_check_constraint" with inherited definition insert into invalid_check_con values(0); -- fail -ERROR: new row for relation "invalid_check_con" violates check constraint "inh_check_constraint" +ERROR: new row for relation "invalid_check_con" violates check constraint "inh_check_constraint" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (0). insert into invalid_check_con_child values(0); -- fail -ERROR: new row for relation "invalid_check_con_child" violates check constraint "inh_check_constraint" +ERROR: new row for relation "invalid_check_con_child" violates check constraint "inh_check_constraint" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (0). select conrelid::regclass::text as relname, conname, convalidated, conislocal, coninhcount, connoinherit @@ -1356,9 +1487,11 @@ create temp table patest0 (id, x) as select x, x from generate_series(0,1000) x distributed by (id); create temp table patest1() inherits (patest0); +NOTICE: table has parent, setting distribution columns to match parent table insert into patest1 select x, x from generate_series(0,1000) x; create temp table patest2() inherits (patest0); +NOTICE: table has parent, setting distribution columns to match parent table insert into patest2 select x, x from generate_series(0,1000) x; create index patest0i on patest0(id); @@ -1371,8 +1504,8 @@ set enable_seqscan=off; set enable_bitmapscan=off; explain (costs off) select * from patest0 join (select f1 from int4_tbl where f1 < 10 and f1 > -10 limit 1) ss on id = f1; - QUERY PLAN ----------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Hash Join Hash Cond: (patest0.id = int4_tbl.f1) @@ -1477,8 +1610,8 @@ explain (verbose, costs off) select * from matest0 order by 1-id; Output: matest0_3.id, matest0_3.name -> Seq Scan on public.matest3 matest0_4 Output: matest0_4.id, matest0_4.name + Settings: enable_indexscan = 'off', optimizer = 'off' Optimizer: Postgres query optimizer - Settings: enable_indexscan=off (19 rows) select * from matest0 order by 1-id; @@ -1493,8 +1626,8 @@ select * from matest0 order by 1-id; (6 rows) explain (verbose, costs off) select min(1-id) from matest0; - QUERY PLAN ------------------------------------------------------ + QUERY PLAN +-------------------------------------------------------------- Finalize Aggregate Output: min((1 - matest0.id)) -> Gather Motion 3:1 (slice1; segments: 3) @@ -1510,8 +1643,8 @@ explain (verbose, costs off) select min(1-id) from matest0; Output: matest0_3.id -> Seq Scan on public.matest3 matest0_4 Output: matest0_4.id + Settings: enable_indexscan = 'off', optimizer = 'off' Optimizer: Postgres query optimizer - Settings: enable_indexscan=off (17 rows) select min(1-id) from matest0; @@ -1528,8 +1661,8 @@ set enable_parallel_append = off; -- Don't let parallel-append interfere -- of append with bitmapscan + sort set enable_bitmapscan = off; explain (verbose, costs off) select * from matest0 order by 1-id; - QUERY PLAN ------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Output: matest0.id, matest0.name, ((1 - matest0.id)) Merge Key: ((1 - matest0.id)) @@ -1546,8 +1679,8 @@ explain (verbose, costs off) select * from matest0 order by 1-id; Output: matest0_3.id, matest0_3.name, (1 - matest0_3.id) -> Index Scan using matest3i on public.matest3 matest0_4 Output: matest0_4.id, matest0_4.name, (1 - matest0_4.id) + Settings: enable_bitmapscan = 'off', enable_parallel_append = 'off', enable_seqscan = 'off', optimizer = 'off' Optimizer: Postgres query optimizer - Settings: enable_bitmapscan=off, enable_parallel_append=off, enable_seqscan=off (18 rows) select * from matest0 order by 1-id; @@ -1562,8 +1695,8 @@ select * from matest0 order by 1-id; (6 rows) explain (verbose, costs off) select min(1-id) from matest0; - QUERY PLAN --------------------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------------- Result Output: $0 InitPlan 1 (returns $0) (slice1) @@ -1591,8 +1724,8 @@ explain (verbose, costs off) select min(1-id) from matest0; -> Index Scan using matest3i on public.matest3 matest0_4 Output: matest0_4.id, (1 - matest0_4.id) Index Cond: ((1 - matest0_4.id) IS NOT NULL) + Settings: enable_bitmapscan = 'off', enable_parallel_append = 'off', enable_seqscan = 'off', optimizer = 'off' Optimizer: Postgres query optimizer - Settings: enable_bitmapscan=off, enable_parallel_append=off, enable_seqscan=off (29 rows) select min(1-id) from matest0; @@ -1614,7 +1747,10 @@ drop cascades to table matest3 -- a plan with extraneous sorting -- create table matest0 (a int, b int, c int, d int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table matest1 () inherits(matest0); +NOTICE: table has parent, setting distribution columns to match parent table create index matest0i on matest0 (b, c); create index matest1i on matest1 (b, c); set enable_nestloop = off; -- we want a plan with two MergeAppends @@ -1830,16 +1966,19 @@ rollback; -- Check handling of a constant-null CHECK constraint -- create table cnullparent (f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table cnullchild (check (f1 = 1 or f1 = null)) inherits(cnullparent); +NOTICE: table has parent, setting distribution columns to match parent table insert into cnullchild values(1); insert into cnullchild values(2); insert into cnullchild values(null); select * from cnullparent; f1 ---- - 1 2 + 1 (3 rows) select * from cnullparent where f1 = 2; @@ -1854,11 +1993,18 @@ NOTICE: drop cascades to table cnullchild -- Check use of temporary tables with inheritance trees -- create table inh_perm_parent (a1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table inh_temp_parent (a1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table inh_temp_child () inherits (inh_perm_parent); -- ok +NOTICE: table has parent, setting distribution columns to match parent table create table inh_perm_child () inherits (inh_temp_parent); -- error +NOTICE: table has parent, setting distribution columns to match parent table ERROR: cannot inherit from temporary relation "inh_temp_parent" create temp table inh_temp_child_2 () inherits (inh_temp_parent); -- ok +NOTICE: table has parent, setting distribution columns to match parent table insert into inh_perm_parent values (1); insert into inh_temp_parent values (2); insert into inh_temp_child values (3); @@ -1866,8 +2012,8 @@ insert into inh_temp_child_2 values (4); select tableoid::regclass, a1 from inh_perm_parent; tableoid | a1 -----------------+---- - inh_perm_parent | 1 inh_temp_child | 3 + inh_perm_parent | 1 (2 rows) select tableoid::regclass, a1 from inh_temp_parent; @@ -1888,9 +2034,14 @@ NOTICE: drop cascades to table inh_temp_child_2 create table list_parted ( a varchar ) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_ab_cd partition of list_parted for values in ('ab', 'cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_ef_gh partition of list_parted for values in ('ef', 'gh'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_null_xy partition of list_parted for values in (null, 'xy'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from list_parted; QUERY PLAN ---------------------------------------------------- @@ -1959,22 +2110,37 @@ create table range_list_parted ( a int, b char(2) ) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_1_10 partition of range_list_parted for values from (1) to (10) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table part_1_10_ab partition of part_1_10 for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_1_10_cd partition of part_1_10 for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_10_20 partition of range_list_parted for values from (10) to (20) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table part_10_20_ab partition of part_10_20 for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_10_20_cd partition of part_10_20 for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_21_30 partition of range_list_parted for values from (21) to (30) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table part_21_30_ab partition of part_21_30 for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_21_30_cd partition of part_21_30 for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_40_inf partition of range_list_parted for values from (40) to (maxvalue) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table part_40_inf_ab partition of part_40_inf for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_40_inf_cd partition of part_40_inf for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_40_inf_null partition of part_40_inf for values in (null); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from range_list_parted; - QUERY PLAN ------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Append -> Seq Scan on part_1_10_ab range_list_parted_1 @@ -2033,8 +2199,8 @@ explain (costs off) select * from range_list_parted where a between 3 and 23 and /* Should select no rows because range partition key cannot be null */ explain (costs off) select * from range_list_parted where a is null; - QUERY PLAN --------------------------- + QUERY PLAN +------------------------------------- Result One-Time Filter: false Optimizer: Postgres query optimizer @@ -2042,8 +2208,8 @@ explain (costs off) select * from range_list_parted where a is null; /* Should only select rows from the null-accepting partition */ explain (costs off) select * from range_list_parted where b is null; - QUERY PLAN ------------------------------------------- + QUERY PLAN +------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) -> Seq Scan on part_40_inf_null range_list_parted Filter: (b IS NULL) @@ -2051,8 +2217,8 @@ explain (costs off) select * from range_list_parted where b is null; (4 rows) explain (costs off) select * from range_list_parted where a is not null and a < 67; - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Append -> Seq Scan on part_1_10_ab range_list_parted_1 @@ -2077,8 +2243,8 @@ explain (costs off) select * from range_list_parted where a is not null and a < (21 rows) explain (costs off) select * from range_list_parted where a >= 30; - QUERY PLAN ------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Append -> Seq Scan on part_40_inf_ab range_list_parted_1 @@ -2095,16 +2261,25 @@ drop table range_list_parted; -- check that constraint exclusion is able to cope with the partition -- constraint emitted for multi-column range partitioned tables create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mcrparted_def partition of mcrparted default; +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted0 partition of mcrparted for values from (minvalue, minvalue, minvalue) to (1, 1, 1); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted2 partition of mcrparted for values from (10, 5, 10) to (10, 10, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted4 partition of mcrparted for values from (20, 10, 10) to (20, 20, 20); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0, mcrparted_def - QUERY PLAN ------------------------------------------- + QUERY PLAN +--------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Append -> Seq Scan on mcrparted0 mcrparted_1 @@ -2141,8 +2316,8 @@ explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scan (9 rows) explain (costs off) select * from mcrparted where abs(b) = 5; -- scans all partitions - QUERY PLAN ------------------------------------------- + QUERY PLAN +--------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Append -> Seq Scan on mcrparted0 mcrparted_1 @@ -2163,8 +2338,8 @@ explain (costs off) select * from mcrparted where abs(b) = 5; -- scans all parti (17 rows) explain (costs off) select * from mcrparted where a > -1; -- scans all partitions - QUERY PLAN -------------------------------------------- + QUERY PLAN +--------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Append -> Seq Scan on mcrparted0 mcrparted_1 @@ -2194,8 +2369,8 @@ explain (costs off) select * from mcrparted where a = 20 and abs(b) = 10 and c > (4 rows) explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mcrparted3, mcrparte4, mcrparte5, mcrparted_def - QUERY PLAN ------------------------------------------------ + QUERY PLAN +--------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Append -> Seq Scan on mcrparted3 mcrparted_1 @@ -2212,12 +2387,15 @@ explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mc -- check that partitioned table Appends cope with being referenced in -- subplans create table parted_minmax (a int, b varchar(16)) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table parted_minmax1 partition of parted_minmax for values from (1) to (10); +NOTICE: table has parent, setting distribution columns to match parent table create index parted_minmax1i on parted_minmax1 (a, b); insert into parted_minmax values (1,'12345'); explain (costs off) select min(a), max(a) from parted_minmax where b = '12345'; - QUERY PLAN -------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------ Result InitPlan 1 (returns $0) (slice1) -> Limit @@ -2246,8 +2424,8 @@ drop table parted_minmax; create index mcrparted_a_abs_c_idx on mcrparted (a, abs(b), c); -- MergeAppend must be used when a default partition exists explain (costs off) select * from mcrparted order by a, abs(b), c; - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c -> Merge Append @@ -2300,11 +2478,14 @@ explain (costs off) select * from mcrparted order by a desc, abs(b) desc, c desc -- that are unordered. drop table mcrparted5; create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue) partition by list (a); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted5a partition of mcrparted5 for values in(20); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted5_def partition of mcrparted5 default; +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mcrparted order by a, abs(b), c; - QUERY PLAN ---------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c -> Append @@ -2325,8 +2506,8 @@ drop table mcrparted5_def; -- into the main Append when the sub-partition is unordered but contains -- just a single sub-partition. explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c; - QUERY PLAN ---------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c -> Append @@ -2342,8 +2523,8 @@ explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c; -- check that Append is used when the sub-partitioned tables are pruned -- during planning. explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c -> Append @@ -2359,13 +2540,17 @@ explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c; (12 rows) create table mclparted (a int) partition by list(a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mclparted1 partition of mclparted for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table mclparted2 partition of mclparted for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table create index on mclparted (a); -- Ensure an Append is used for a list partition with an order by. explain (costs off) select * from mclparted order by a; - QUERY PLAN ------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) Merge Key: mclparted.a -> Append @@ -2377,10 +2562,12 @@ explain (costs off) select * from mclparted order by a; -- Ensure a MergeAppend is used when a partition exists with interleaved -- datums in the partition bound. create table mclparted3_5 partition of mclparted for values in(3,5); +NOTICE: table has parent, setting distribution columns to match parent table create table mclparted4 partition of mclparted for values in(4); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mclparted order by a; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mclparted.a -> Merge Append @@ -2401,8 +2588,8 @@ create index on mcrparted2 (a, abs(b), c); create index on mcrparted3 (a, abs(b), c); create index on mcrparted4 (a, abs(b), c); explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c limit 1; - QUERY PLAN -------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------- Limit -> Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c @@ -2425,8 +2612,8 @@ set enable_bitmapscan = 0; -- Ensure Append node can be used when the partition is ordered by some -- pathkeys which were deemed redundant. explain (costs off) select * from mcrparted where a = 10 order by a, abs(b), c; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) Merge Key: (abs(mcrparted.b)), mcrparted.c -> Append @@ -2441,12 +2628,16 @@ reset enable_bitmapscan; drop table mcrparted; -- Ensure LIST partitions allow an Append to be used instead of a MergeAppend create table bool_lp (b bool) partition by list(b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table bool_lp_true partition of bool_lp for values in(true); +NOTICE: table has parent, setting distribution columns to match parent table create table bool_lp_false partition of bool_lp for values in(false); +NOTICE: table has parent, setting distribution columns to match parent table create index on bool_lp (b); explain (costs off) select * from bool_lp order by b; - QUERY PLAN ------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: bool_lp.b -> Append @@ -2458,14 +2649,20 @@ explain (costs off) select * from bool_lp order by b; drop table bool_lp; -- Ensure const bool quals can be properly detected as redundant create table bool_rp (b bool, a int) partition by range(b,a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table bool_rp_false_1k partition of bool_rp for values from (false,0) to (false,1000); +NOTICE: table has parent, setting distribution columns to match parent table create table bool_rp_true_1k partition of bool_rp for values from (true,0) to (true,1000); +NOTICE: table has parent, setting distribution columns to match parent table create table bool_rp_false_2k partition of bool_rp for values from (false,1000) to (false,2000); +NOTICE: table has parent, setting distribution columns to match parent table create table bool_rp_true_2k partition of bool_rp for values from (true,1000) to (true,2000); +NOTICE: table has parent, setting distribution columns to match parent table create index on bool_rp (b,a); explain (costs off) select * from bool_rp where b = true order by b,a; - QUERY PLAN ------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) Merge Key: bool_rp.b, bool_rp.a -> Append @@ -2477,8 +2674,8 @@ explain (costs off) select * from bool_rp where b = true order by b,a; (8 rows) explain (costs off) select * from bool_rp where b = false order by b,a; - QUERY PLAN --------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------ Gather Motion 1:1 (slice1; segments: 1) Merge Key: bool_rp.b, bool_rp.a -> Append @@ -2493,8 +2690,8 @@ explain (costs off) select * from bool_rp where b = false order by b,a; set enable_seqscan=off; set enable_bitmapscan=off; explain (costs off) select * from bool_rp where b = true order by a; - QUERY PLAN ------------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) Merge Key: bool_rp.a -> Sort @@ -2508,8 +2705,8 @@ explain (costs off) select * from bool_rp where b = true order by a; (10 rows) explain (costs off) select * from bool_rp where b = false order by a; - QUERY PLAN --------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------ Gather Motion 1:1 (slice1; segments: 1) Merge Key: bool_rp.a -> Sort @@ -2528,12 +2725,16 @@ drop table bool_rp; -- Ensure an Append scan is chosen when the partition order is a subset of -- the required order. create table range_parted (a int, b int, c int) partition by range(a, b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table range_parted1 partition of range_parted for values from (0,0) to (10,10); +NOTICE: table has parent, setting distribution columns to match parent table create table range_parted2 partition of range_parted for values from (10,10) to (20,20); +NOTICE: table has parent, setting distribution columns to match parent table create index on range_parted (a,b,c); explain (costs off) select * from range_parted order by a,b,c; - QUERY PLAN ----------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: range_parted.a, range_parted.b, range_parted.c -> Append @@ -2543,8 +2744,8 @@ explain (costs off) select * from range_parted order by a,b,c; (6 rows) explain (costs off) select * from range_parted order by a desc,b desc,c desc; - QUERY PLAN -------------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: range_parted.a, range_parted.b, range_parted.c -> Append @@ -2557,6 +2758,8 @@ drop table range_parted; -- Check that we allow access to a child table's statistics when the user -- has permissions only for the parent table. create table permtest_parent (a int, b text, c text) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table permtest_child (b text, c text, a int) partition by list (b) distributed by (a); create table permtest_grandchild (c text, b text, a int) distributed by (a); alter table permtest_child attach partition permtest_grandchild for values in ('a'); @@ -2566,6 +2769,7 @@ insert into permtest_parent select 1, 'a', left(md5(i::text), 5) from generate_series(0, 100) i; analyze permtest_parent; create role regress_no_child_access; +NOTICE: resource queue required -- using default resource queue "pg_default" revoke all on permtest_grandchild from regress_no_child_access; NOTICE: no privileges could be revoked grant select on permtest_parent to regress_no_child_access; @@ -2574,8 +2778,8 @@ set session authorization regress_no_child_access; explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c ~ 'a1$'; - QUERY PLAN ------------------------------------------- + QUERY PLAN +------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) -> Hash Join Hash Cond: (p2.a = p1.a) @@ -2590,8 +2794,8 @@ explain (costs off) explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and left(p1.c, 3) ~ 'a1$'; - QUERY PLAN ----------------------------------------------- + QUERY PLAN +---------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Hash Join Hash Cond: (p2.a = p1.a) @@ -2610,8 +2814,8 @@ set session authorization regress_no_child_access; explain (costs off) select p2.a, p1.c from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c ~ 'a1$'; - QUERY PLAN ------------------------------------------- + QUERY PLAN +------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) -> Hash Join Hash Cond: (p2.a = p1.a) @@ -2652,6 +2856,8 @@ CREATE TABLE errtst_parent ( data int NOT NULL DEFAULT 0, CONSTRAINT shdata_small CHECK(shdata < 3) ) PARTITION BY RANGE (partid); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'partid' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- fast defaults lead to attribute mapping being used in one -- direction, but not the other CREATE TABLE errtst_child_fastdef ( @@ -2659,6 +2865,8 @@ CREATE TABLE errtst_child_fastdef ( shdata int not null, CONSTRAINT shdata_small CHECK(shdata < 3) ); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'partid' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- no remapping in either direction necessary CREATE TABLE errtst_child_plaindef ( partid int not null, @@ -2667,6 +2875,8 @@ CREATE TABLE errtst_child_plaindef ( CONSTRAINT shdata_small CHECK(shdata < 3), CHECK(data < 10) ); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'partid' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- remapping in both direction CREATE TABLE errtst_child_reorder ( data int NOT NULL DEFAULT 0, @@ -2686,33 +2896,33 @@ INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', '5'); INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '5'); -- insert with child check constraint error INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', '10'); -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" +ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (0, 1, 10). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', '10'); -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" +ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3236638) DETAIL: Failing row contains (10, 1, 10). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '10'); -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" +ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (20, 1, 10). -- insert with child not null constraint error INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_fastdef" violates not-null constraint +ERROR: null value in column "data" of relation "errtst_child_fastdef" violates not-null constraint (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (0, 1, null). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_plaindef" violates not-null constraint +ERROR: null value in column "data" of relation "errtst_child_plaindef" violates not-null constraint (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3236638) DETAIL: Failing row contains (10, 1, null). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_reorder" violates not-null constraint +ERROR: null value in column "data" of relation "errtst_child_reorder" violates not-null constraint (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (20, 1, null). -- insert with shared check constraint error INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '5', '5'); -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "shdata_small" +ERROR: new row for relation "errtst_child_fastdef" violates check constraint "shdata_small" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (0, 5, 5). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '5', '5'); -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "shdata_small" +ERROR: new row for relation "errtst_child_plaindef" violates check constraint "shdata_small" (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3236638) DETAIL: Failing row contains (10, 5, 5). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '5', '5'); -ERROR: new row for relation "errtst_child_reorder" violates check constraint "shdata_small" +ERROR: new row for relation "errtst_child_reorder" violates check constraint "shdata_small" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (20, 5, 5). -- within partition update without child check constraint violation BEGIN; @@ -2722,13 +2932,13 @@ UPDATE errtst_parent SET data = data + 1 WHERE partid = 20; ROLLBACK; -- within partition update with child check constraint violation UPDATE errtst_parent SET data = data + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" +ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (0, 1, 15). UPDATE errtst_parent SET data = data + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" +ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3236638) DETAIL: Failing row contains (10, 1, 15). UPDATE errtst_parent SET data = data + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" +ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (20, 1, 15). -- direct leaf partition update, without partition id violation BEGIN; @@ -2738,13 +2948,13 @@ UPDATE errtst_child_reorder SET partid = 21 WHERE partid = 20; ROLLBACK; -- direct leaf partition update, with partition id violation UPDATE errtst_child_fastdef SET partid = partid + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_fastdef" violates partition constraint +ERROR: new row for relation "errtst_child_fastdef" violates partition constraint (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3236638) DETAIL: Failing row contains (10, 1, 5). UPDATE errtst_child_plaindef SET partid = partid + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_plaindef" violates partition constraint +ERROR: new row for relation "errtst_child_plaindef" violates partition constraint (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (20, 1, 5). UPDATE errtst_child_reorder SET partid = partid + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_reorder" violates partition constraint +ERROR: new row for relation "errtst_child_reorder" violates partition constraint (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (5, 1, 30). -- partition move, without child check constraint violation BEGIN; @@ -2754,16 +2964,16 @@ UPDATE errtst_parent SET partid = 0, data = data + 1 WHERE partid = 20; ROLLBACK; -- partition move, with child check constraint violation UPDATE errtst_parent SET partid = 10, data = data + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" +ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3236638) DETAIL: Failing row contains (10, 1, 15). UPDATE errtst_parent SET partid = 20, data = data + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" +ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (20, 1, 15). UPDATE errtst_parent SET partid = 0, data = data + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" +ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Failing row contains (0, 1, 15). -- partition move, without target partition UPDATE errtst_parent SET partid = 30, data = data + 10 WHERE partid = 20; -ERROR: no partition of relation "errtst_parent" found for row +ERROR: no partition of relation "errtst_parent" found for row (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3236632) DETAIL: Partition key of the failing row contains (partid) = (30). DROP TABLE errtst_parent; diff --git a/src/test/regress/expected/inherit_optimizer.out b/src/test/regress/expected/inherit_optimizer.out index ef4a44eac33..3ce9bfa269e 100644 --- a/src/test/regress/expected/inherit_optimizer.out +++ b/src/test/regress/expected/inherit_optimizer.out @@ -3,8 +3,11 @@ -- CREATE TABLE a (aa TEXT) distributed randomly; CREATE TABLE b (bb TEXT) INHERITS (a); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE c (cc TEXT) INHERITS (a); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE d (dd TEXT) INHERITS (b,c,a); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "aa" NOTICE: merging multiple inherited definitions of column "aa" INSERT INTO a(aa) VALUES('aaa'); @@ -35,45 +38,45 @@ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+---------- a | aaa + a | aaaaaaaa + b | bbbbbbb + c | cccccccc + d | ddddd + d | dddddddd a | aaaa a | aaaaa a | aaaaaa a | aaaaaaa - a | aaaaaaaa b | bbb b | bbbb - b | bbbbb - b | bbbbbb - b | bbbbbbb b | bbbbbbbb - c | ccc c | cccc c | ccccc + d | ddddddd + b | bbbbb + b | bbbbbb + c | ccc c | cccccc c | ccccccc - c | cccccccc d | ddd d | dddd - d | ddddd d | dddddd - d | ddddddd - d | dddddddd (24 rows) SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+----------+---- - b | bbb | - b | bbbb | b | bbbbb | b | bbbbbb | - b | bbbbbbb | - b | bbbbbbbb | d | ddd | d | dddd | - d | ddddd | d | dddddd | + b | bbb | + b | bbbb | + b | bbbbbbbb | d | ddddddd | + b | bbbbbbb | + d | ddddd | d | dddddddd | (12 rows) @@ -81,39 +84,39 @@ SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- c | ccc | - c | cccc | - c | ccccc | c | cccccc | c | ccccccc | - c | cccccccc | d | ddd | d | dddd | - d | ddddd | d | dddddd | + c | cccc | + c | ccccc | d | ddddddd | + c | cccccccc | + d | ddddd | d | dddddddd | (12 rows) SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; relname | aa | bb | cc | dd ---------+----------+----+----+---- + d | ddddd | | | + d | dddddddd | | | + d | ddddddd | | | d | ddd | | | d | dddd | | | - d | ddddd | | | d | dddddd | | | - d | ddddddd | | | - d | dddddddd | | | (6 rows) SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+---------- a | aaa + a | aaaaaaaa a | aaaa a | aaaaa a | aaaaaa a | aaaaaaa - a | aaaaaaaa (6 rows) SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; @@ -121,32 +124,32 @@ SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; ---------+----------+---- b | bbb | b | bbbb | + b | bbbbbbbb | + b | bbbbbbb | b | bbbbb | b | bbbbbb | - b | bbbbbbb | - b | bbbbbbbb | (6 rows) SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- c | ccc | - c | cccc | - c | ccccc | c | cccccc | c | ccccccc | c | cccccccc | + c | cccc | + c | ccccc | (6 rows) SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; relname | aa | bb | cc | dd ---------+----------+----+----+---- + d | ddddd | | | + d | dddddddd | | | + d | ddddddd | | | d | ddd | | | d | dddd | | | - d | ddddd | | | d | dddddd | | | - d | ddddddd | | | - d | dddddddd | | | (6 rows) UPDATE a SET aa='zzzz' WHERE aa='aaaa'; @@ -157,64 +160,64 @@ UPDATE a SET aa='zzzzzz' WHERE aa LIKE 'aaa%'; SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+---------- - a | zzzz - a | zzzzz a | zzzzzz a | zzzzzz + b | bbbbbbb + c | cccccccc + d | ddddd + d | dddddddd + a | zzzz + a | zzzzz a | zzzzzz a | zzzzzz b | bbb b | bbbb - b | bbbbb - b | bbbbbb - b | bbbbbbb b | bbbbbbbb - c | ccc c | cccc c | ccccc + d | ddddddd + b | bbbbb + b | bbbbbb + c | ccc c | cccccc c | ccccccc - c | cccccccc d | ddd d | dddd - d | ddddd d | dddddd - d | ddddddd - d | dddddddd (24 rows) SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+----------+---- - b | bbb | - b | bbbb | + b | bbbbbbb | + d | ddddd | + d | dddddddd | b | bbbbb | b | bbbbbb | - b | bbbbbbb | - b | bbbbbbbb | d | ddd | d | dddd | - d | ddddd | d | dddddd | + b | bbb | + b | bbbb | + b | bbbbbbbb | d | ddddddd | - d | dddddddd | (12 rows) SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- - c | ccc | c | cccc | c | ccccc | + d | ddddddd | + c | cccccccc | + d | ddddd | + d | dddddddd | + c | ccc | c | cccccc | c | ccccccc | - c | cccccccc | d | ddd | d | dddd | - d | ddddd | d | dddddd | - d | ddddddd | - d | dddddddd | (12 rows) SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; @@ -222,10 +225,10 @@ SELECT relname, d.* FROM d, pg_class where d.tableoid = pg_class.oid; ---------+----------+----+----+---- d | ddd | | | d | dddd | | | - d | ddddd | | | d | dddddd | | | - d | ddddddd | | | + d | ddddd | | | d | dddddddd | | | + d | ddddddd | | | (6 rows) SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; @@ -242,61 +245,61 @@ SELECT relname, a.* FROM ONLY a, pg_class where a.tableoid = pg_class.oid; SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+----------+---- - b | bbb | - b | bbbb | + b | bbbbbbb | b | bbbbb | b | bbbbbb | - b | bbbbbbb | + b | bbb | + b | bbbb | b | bbbbbbbb | (6 rows) SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- + c | cccccccc | c | ccc | - c | cccc | - c | ccccc | c | cccccc | c | ccccccc | - c | cccccccc | + c | cccc | + c | ccccc | (6 rows) SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; relname | aa | bb | cc | dd ---------+----------+----+----+---- + d | ddddd | | | + d | dddddddd | | | d | ddd | | | d | dddd | | | - d | ddddd | | | d | dddddd | | | d | ddddddd | | | - d | dddddddd | | | (6 rows) UPDATE b SET aa='new'; SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+---------- - a | zzzz - a | zzzzz a | zzzzzz a | zzzzzz + b | new + c | cccccccc + d | new + d | new + a | zzzz + a | zzzzz a | zzzzzz a | zzzzzz b | new b | new b | new - b | new + c | cccc + c | ccccc + d | new b | new b | new c | ccc - c | cccc - c | ccccc c | cccccc c | ccccccc - c | cccccccc - d | new - d | new - d | new d | new d | new d | new @@ -308,13 +311,13 @@ SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; b | new | b | new | b | new | + d | new | b | new | b | new | - b | new | - d | new | d | new | d | new | d | new | + b | new | d | new | d | new | (12 rows) @@ -323,16 +326,16 @@ SELECT relname, c.* FROM c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- c | ccc | - c | cccc | - c | ccccc | c | cccccc | c | ccccccc | - c | cccccccc | d | new | d | new | d | new | + c | cccccccc | d | new | d | new | + c | cccc | + c | ccccc | d | new | (12 rows) @@ -372,12 +375,12 @@ SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; relname | aa | cc ---------+----------+---- - c | ccc | + c | cccccccc | c | cccc | c | ccccc | + c | ccc | c | cccccc | c | ccccccc | - c | cccccccc | (6 rows) SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; @@ -396,8 +399,16 @@ DELETE FROM ONLY c WHERE aa='new'; SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; relname | aa ---------+----- + b | new + b | new + d | new + d | new + d | new a | new a | new + b | new + d | new + d | new a | new a | new a | new @@ -405,31 +416,23 @@ SELECT relname, a.* FROM a, pg_class where a.tableoid = pg_class.oid; b | new b | new b | new - b | new - b | new - b | new - d | new - d | new - d | new - d | new - d | new d | new (18 rows) SELECT relname, b.* FROM b, pg_class where b.tableoid = pg_class.oid; relname | aa | bb ---------+-----+---- - b | new | - b | new | - b | new | - b | new | - b | new | b | new | d | new | d | new | + b | new | + b | new | d | new | d | new | d | new | + b | new | + b | new | + b | new | d | new | (12 rows) @@ -537,27 +540,60 @@ SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; -- Confirm PRIMARY KEY adds NOT NULL constraint to child table CREATE TEMP TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a); INSERT INTO z VALUES (NULL, 'text'); -- should fail -ERROR: null value in column "aa" of relation "z" violates not-null constraint +ERROR: null value in column "aa" of relation "z" violates not-null constraint (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3130282) DETAIL: Failing row contains (null, text). +-- Check inherited UPDATE with first child excluded +create table some_tab (f1 int, f2 int, f3 int, check (f1 < 10) no inherit); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +create table some_tab_child () inherits(some_tab); +NOTICE: table has parent, setting distribution columns to match parent table +insert into some_tab_child select i, i+1, 0 from generate_series(1,1000) i; +create index on some_tab_child(f1, f2); +-- while at it, also check that statement-level triggers fire +create function some_tab_stmt_trig_func() returns trigger as +$$begin raise notice 'updating some_tab'; return NULL; end;$$ +language plpgsql; +create trigger some_tab_stmt_trig + before update on some_tab execute function some_tab_stmt_trig_func(); +ERROR: Triggers for statements are not yet supported +explain (costs off) +update some_tab set f3 = 11 where f1 = 12 and f2 = 13; + QUERY PLAN +------------------------------------------------------------------------------------ + Update on some_tab + Update on some_tab_child some_tab_1 + -> Result + -> Index Scan using some_tab_child_f1_f2_idx on some_tab_child some_tab_1 + Index Cond: ((f1 = 12) AND (f2 = 13)) + Optimizer: Postgres query optimizer +(6 rows) + +update some_tab set f3 = 11 where f1 = 12 and f2 = 13; +drop table some_tab cascade; +NOTICE: drop cascades to table some_tab_child +drop function some_tab_stmt_trig_func(); -- Check inherited UPDATE with all children excluded create table some_tab (a int, b int) distributed randomly; create table some_tab_child () inherits (some_tab); +NOTICE: table has parent, setting distribution columns to match parent table insert into some_tab_child values(1,2); explain (verbose, costs off) update some_tab set a = a + 1 where false; - QUERY PLAN + QUERY PLAN ----------------------------------------------------------------------- Update on public.some_tab -> Result Output: (some_tab.a + 1), NULL::oid, NULL::tid, NULL::integer One-Time Filter: false + Settings: optimizer = 'on' Optimizer: Postgres query optimizer -(5 rows) +(6 rows) update some_tab set a = a + 1 where false; explain (verbose, costs off) update some_tab set a = a + 1 where false returning b, a; - QUERY PLAN + QUERY PLAN ----------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Output: some_tab.b, some_tab.a @@ -566,8 +602,9 @@ update some_tab set a = a + 1 where false returning b, a; -> Result Output: (some_tab.a + 1), NULL::oid, NULL::tid, NULL::integer One-Time Filter: false + Settings: optimizer = 'on' Optimizer: Postgres query optimizer -(8 rows) +(9 rows) update some_tab set a = a + 1 where false returning b, a; b | a @@ -584,9 +621,15 @@ drop table some_tab cascade; NOTICE: drop cascades to table some_tab_child -- Check UPDATE with inherited target and an inherited source table create temp table foo(f1 int, f2 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table foo2(f3 int) inherits (foo); +NOTICE: table has parent, setting distribution columns to match parent table create temp table bar(f1 int, f2 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table bar2(f3 int) inherits (bar); +NOTICE: table has parent, setting distribution columns to match parent table insert into foo values(1,1); insert into foo values(3,3); insert into foo2 values(2,2,2); @@ -635,17 +678,25 @@ select tableoid::regclass::text as relname, bar.* from bar order by 1,2; create table some_tab (a int) distributed randomly; insert into some_tab values (0); create table some_tab_child () inherits (some_tab); +NOTICE: table has parent, setting distribution columns to match parent table insert into some_tab_child values (1); create table parted_tab (a int, b char) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table parted_tab_part1 partition of parted_tab for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table parted_tab_part2 partition of parted_tab for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table parted_tab_part3 partition of parted_tab for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table insert into parted_tab values (1, 'a'), (2, 'a'), (3, 'a'); update parted_tab set b = 'b' from (select a from some_tab union all select a+1 from some_tab) ss (a) where parted_tab.a = ss.a; select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2; +NOTICE: One or more columns in the following table(s) do not have statistics: parted_tab +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. relname | a | b ------------------+---+--- parted_tab_part1 | 1 | b @@ -660,6 +711,8 @@ from (select 0 from parted_tab union all select 1 from parted_tab) ss (a) where parted_tab.a = ss.a; select tableoid::regclass::text as relname, parted_tab.* from parted_tab order by 1,2; +NOTICE: One or more columns in the following table(s) do not have statistics: parted_tab +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. relname | a | b ------------------+---+--- parted_tab_part1 | 1 | b @@ -671,8 +724,8 @@ select tableoid::regclass::text as relname, parted_tab.* from parted_tab order b explain update parted_tab set a = 2 where false; QUERY PLAN -------------------------------------------------------- - Update on parted_tab (cost=0.00..0.00 rows=0 width=0) - -> Result (cost=0.00..0.00 rows=0 width=0) + Update on parted_tab (cost=0.00..0.01 rows=0 width=0) + -> Result (cost=0.00..0.00 rows=0 width=22) One-Time Filter: false Optimizer: Postgres query optimizer (4 rows) @@ -680,11 +733,18 @@ explain update parted_tab set a = 2 where false; drop table parted_tab; -- Check UPDATE with multi-level partitioned inherited target create table mlparted_tab (a int, b char, c text) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mlparted_tab_part1 partition of mlparted_tab for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table mlparted_tab_part2 partition of mlparted_tab for values in (2) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table mlparted_tab_part3 partition of mlparted_tab for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table create table mlparted_tab_part2a partition of mlparted_tab_part2 for values in ('a'); +NOTICE: table has parent, setting distribution columns to match parent table create table mlparted_tab_part2b partition of mlparted_tab_part2 for values in ('b'); +NOTICE: table has parent, setting distribution columns to match parent table insert into mlparted_tab values (1, 'a'), (2, 'a'), (2, 'b'), (3, 'a'); update mlparted_tab mlp set c = 'xxx' from @@ -704,16 +764,25 @@ drop table some_tab cascade; NOTICE: drop cascades to table some_tab_child /* Test multiple inheritance of column defaults */ CREATE TABLE firstparent (tomorrow date default now()::date + 1); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'tomorrow' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE secondparent (tomorrow date default now() :: date + 1); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'tomorrow' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE jointchild () INHERITS (firstparent, secondparent); -- ok +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "tomorrow" CREATE TABLE thirdparent (tomorrow date default now()::date - 1); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'tomorrow' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE otherchild () INHERITS (firstparent, thirdparent); -- not ok +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "tomorrow" ERROR: column "tomorrow" inherits conflicting default values HINT: To resolve the conflict, specify a default explicitly. CREATE TABLE otherchild (tomorrow date default now()) INHERITS (firstparent, thirdparent); -- ok, child resolves ambiguous default +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "tomorrow" NOTICE: merging column "tomorrow" with inherited definition DROP TABLE firstparent, secondparent, jointchild, thirdparent, otherchild; @@ -731,14 +800,21 @@ select * from d; -- column; but we should reject that if any definition was inherited from -- an unrelated parent. create temp table parent1(f1 int, f2 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table parent2(f1 int, f3 bigint); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table childtab(f4 int) inherits(parent1, parent2); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "f1" alter table parent1 alter column f1 type bigint; -- fail, conflict w/parent2 ERROR: cannot alter inherited column "f1" of relation "childtab" alter table parent1 alter column f2 type bigint; -- ok -- Test non-inheritable parent constraints create table p1(ff1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'ff1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. alter table p1 add constraint p1chk check (ff1 > 0) no inherit; alter table p1 add constraint p2chk check (ff1 > 10); -- connoinherit should be true for NO INHERIT constraint @@ -751,6 +827,7 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg -- Test that child does not inherit NO INHERIT constraints create table c1 () inherits (p1); +NOTICE: table has parent, setting distribution columns to match parent table \d p1 Table "public.p1" Column | Type | Collation | Nullable | Default @@ -774,14 +851,19 @@ Distributed by: (ff1) -- Test that child does not override inheritable constraints of the parent create table c2 (constraint p2chk check (ff1 > 10) no inherit) inherits (p1); --fails +NOTICE: table has parent, setting distribution columns to match parent table ERROR: constraint "p2chk" conflicts with inherited constraint on relation "c2" drop table p1 cascade; NOTICE: drop cascades to table c1 -- Tests for casting between the rowtypes of parent and child -- tables. See the pgsql-hackers thread beginning Dec. 4/04 create table base (i integer); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'i' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table derived () inherits (base); +NOTICE: table has parent, setting distribution columns to match parent table create table more_derived (like derived, b int) inherits (derived); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging column "i" with inherited definition insert into derived (i) values (0); select derived::base from derived; @@ -804,25 +886,31 @@ explain (verbose on, costs off) select row(i, b)::more_derived::derived::base fr Output: ((ROW(i, b)::more_derived)::base) -> Seq Scan on public.more_derived Output: (ROW(i, b)::more_derived)::base + Settings: optimizer = 'on' Optimizer: Postgres query optimizer -(5 rows) +(6 rows) explain (verbose on, costs off) select (1, 2)::more_derived::derived::base; - QUERY PLAN ------------------------ + QUERY PLAN +---------------------------- Result Output: '(1)'::base -(2 rows) + Settings: optimizer = 'on' + Optimizer: GPORCA +(4 rows) drop table more_derived; drop table derived; drop table base; create table p1(ff1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'ff1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table p2(f1 text); NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create function p2text(p2) returns text as 'select $1.f1' language sql; create table c1(f3 int) inherits(p1,p2); +NOTICE: table has parent, setting distribution columns to match parent table insert into c1 values(123456789, 'hi', 42); select p2text(c1.*) from c1; p2text @@ -835,8 +923,11 @@ drop table c1; drop table p2; drop table p1; CREATE TABLE ac (aa TEXT); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'aa' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. alter table ac add constraint ac_check check (aa is not null); CREATE TABLE bc (bb TEXT) INHERITS (ac); +NOTICE: table has parent, setting distribution columns to match parent table select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; relname | conname | contype | conislocal | coninhcount | consrc ---------+----------+---------+------------+-------------+------------------ @@ -845,10 +936,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg (2 rows) insert into ac (aa) values (NULL); -ERROR: new row for relation "ac" violates check constraint "ac_check" +ERROR: new row for relation "ac" violates check constraint "ac_check" (seg0 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=3130283) DETAIL: Failing row contains (null). insert into bc (aa) values (NULL); -ERROR: new row for relation "bc" violates check constraint "ac_check" +ERROR: new row for relation "bc" violates check constraint "ac_check" (seg0 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=3130283) DETAIL: Failing row contains (null, null). alter table bc drop constraint ac_check; -- fail, disallowed ERROR: cannot drop inherited constraint "ac_check" of relation "bc" @@ -868,10 +959,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg (2 rows) insert into ac (aa) values (NULL); -ERROR: new row for relation "ac" violates check constraint "ac_aa_check" +ERROR: new row for relation "ac" violates check constraint "ac_aa_check" (seg0 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=3130283) DETAIL: Failing row contains (null). insert into bc (aa) values (NULL); -ERROR: new row for relation "bc" violates check constraint "ac_aa_check" +ERROR: new row for relation "bc" violates check constraint "ac_aa_check" (seg0 2a02:6b8:c37:834b:0:5644:602c:0:7002 pid=3130283) DETAIL: Failing row contains (null, null). alter table bc drop constraint ac_aa_check; -- fail, disallowed ERROR: cannot drop inherited constraint "ac_aa_check" of relation "bc" @@ -906,7 +997,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg drop table bc; drop table ac; create table ac (a int constraint check_a check (a <> 0)); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table bc (a int constraint check_a check (a <> 0), b int constraint check_b check (b <> 0)) inherits (ac); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging column "a" with inherited definition NOTICE: merging constraint "check_a" with inherited definition select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc') order by 1,2; @@ -920,8 +1014,13 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg drop table bc; drop table ac; create table ac (a int constraint check_a check (a <> 0)); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table bc (b int constraint check_b check (b <> 0)); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table cc (c int constraint check_c check (c <> 0)) inherits (ac, bc); +NOTICE: table has parent, setting distribution columns to match parent table select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg_get_expr(pgc.conbin, pc.oid) as consrc from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname in ('ac', 'bc', 'cc') order by 1,2; relname | conname | contype | conislocal | coninhcount | consrc ---------+---------+---------+------------+-------------+---------- @@ -947,20 +1046,26 @@ drop table cc; drop table bc; drop table ac; create table p1(f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table p2(f2 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f2' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table c1(f3 int) inherits(p1,p2); +NOTICE: table has parent, setting distribution columns to match parent table insert into c1 values(1,-1,2); alter table p2 add constraint cc check (f2>0); -- fail -ERROR: check constraint "cc" of relation "c1" is violated by some row +ERROR: check constraint "cc" of relation "c1" is violated by some row (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) alter table p2 add check (f2>0); -- check it without a name, too -ERROR: check constraint "p2_f2_check" of relation "c1" is violated by some row +ERROR: check constraint "p2_f2_check" of relation "c1" is violated by some row (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) delete from c1; insert into c1 values(1,1,2); alter table p2 add check (f2>0); insert into c1 values(1,-1,2); -- fail -ERROR: new row for relation "c1" violates check constraint "p2_f2_check" +ERROR: new row for relation "c1" violates check constraint "p2_f2_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (1, -1, 2). create table c2(f3 int) inherits(p1,p2); +NOTICE: table has parent, setting distribution columns to match parent table \d c2 Table "public.c2" Column | Type | Collation | Nullable | Default @@ -975,6 +1080,7 @@ Inherits: p1, Distributed by: (f1) create table c3 (f4 int) inherits(c1,c2); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "f1" NOTICE: merging multiple inherited definitions of column "f2" NOTICE: merging multiple inherited definitions of column "f3" @@ -999,7 +1105,10 @@ drop cascades to table c2 drop cascades to table c3 drop table p2 cascade; create table pp1 (f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table cc1 (f2 text, f3 int) inherits (pp1); +NOTICE: table has parent, setting distribution columns to match parent table alter table pp1 add column a1 int check (a1 > 0); \d cc1 Table "public.cc1" @@ -1015,6 +1124,7 @@ Inherits: pp1 Distributed by: (f1) create table cc2(f4 float) inherits(pp1,cc1); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "f1" NOTICE: merging multiple inherited definitions of column "a1" \d cc2 @@ -1058,8 +1168,13 @@ DETAIL: drop cascades to table cc1 drop cascades to table cc2 -- Test for renaming in simple multiple inheritance CREATE TABLE inht1 (a int, b int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE inhs1 (b int, c int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE inhts (d int) INHERITS (inht1, inhs1); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "b" ALTER TABLE inht1 RENAME a TO aa; ALTER TABLE inht1 RENAME b TO bb; -- to be failed @@ -1082,8 +1197,11 @@ Distributed by: (aa) DROP TABLE inhts; -- Test for renaming in diamond inheritance CREATE TABLE inht2 (x int) INHERITS (inht1); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE inht3 (y int) INHERITS (inht1); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE inht4 (z int) INHERITS (inht2, inht3); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "aa" NOTICE: merging multiple inherited definitions of column "b" ALTER TABLE inht1 RENAME aa TO aaa; @@ -1101,6 +1219,7 @@ Inherits: inht2, Distributed by: (aaa) CREATE TABLE inhts (d int) INHERITS (inht2, inhs1); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging multiple inherited definitions of column "b" ALTER TABLE inht1 RENAME aaa TO aaaa; ALTER TABLE inht1 RENAME b TO bb; -- to be failed @@ -1153,6 +1272,7 @@ drop cascades to table inht4 -- Test non-inheritable indices [UNIQUE, EXCLUDE] constraints CREATE TABLE test_constraints (id int, val1 varchar, val2 int, UNIQUE(val1, val2)); CREATE TABLE test_constraints_inh () INHERITS (test_constraints); +NOTICE: table has parent, setting distribution columns to match parent table \d+ test_constraints Table "public.test_constraints" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -1193,7 +1313,10 @@ CREATE TABLE test_ex_constraints ( dkey inet, EXCLUDE USING gist (dkey inet_ops WITH =, c WITH &&) ); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'dkey' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE test_ex_constraints_inh () INHERITS (test_ex_constraints); +NOTICE: table has parent, setting distribution columns to match parent table \d+ test_ex_constraints Table "public.test_ex_constraints" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -1229,7 +1352,11 @@ DROP TABLE test_ex_constraints; -- Test non-inheritable foreign key constraints CREATE TABLE test_primary_constraints(id int PRIMARY KEY); CREATE TABLE test_foreign_constraints(id1 int REFERENCES test_primary_constraints(id)); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'id1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +WARNING: referential integrity (FOREIGN KEY) constraints are not supported in Apache Cloudberry, will not be enforced CREATE TABLE test_foreign_constraints_inh () INHERITS (test_foreign_constraints); +NOTICE: table has parent, setting distribution columns to match parent table \d+ test_primary_constraints Table "public.test_primary_constraints" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -1275,8 +1402,10 @@ DROP TABLE test_primary_constraints; create table inh_fk_1 (a int primary key); insert into inh_fk_1 values (1), (2), (3); create table inh_fk_2 (x int primary key, y int references inh_fk_1 on delete cascade); +WARNING: referential integrity (FOREIGN KEY) constraints are not supported in Apache Cloudberry, will not be enforced insert into inh_fk_2 values (11, 1), (22, 2), (33, 3); create table inh_fk_2_child () inherits (inh_fk_2); +NOTICE: table has parent, setting distribution columns to match parent table insert into inh_fk_2_child values (111, 1), (222, 2); -- The cascading deletion doesn't work on GPDB, because foreign keys are not -- enforced in general. So this produces different result than on upstream. @@ -1301,7 +1430,10 @@ select * from inh_fk_2 order by 1, 2; drop table inh_fk_1, inh_fk_2, inh_fk_2_child; -- Test that parent and child CHECK constraints can be created in either order create table p1(f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table p1_c1() inherits(p1); +NOTICE: table has parent, setting distribution columns to match parent table alter table p1 add constraint inh_check_constraint1 check (f1 > 0); alter table p1_c1 add constraint inh_check_constraint1 check (f1 > 0); NOTICE: merging constraint "inh_check_constraint1" with inherited definition @@ -1323,7 +1455,10 @@ drop table p1 cascade; NOTICE: drop cascades to table p1_c1 -- Test that a valid child can have not-valid parent, but not vice versa create table invalid_check_con(f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table invalid_check_con_child() inherits(invalid_check_con); +NOTICE: table has parent, setting distribution columns to match parent table alter table invalid_check_con_child add constraint inh_check_constraint check(f1 > 0) not valid; alter table invalid_check_con add constraint inh_check_constraint check(f1 > 0); -- fail ERROR: constraint "inh_check_constraint" conflicts with NOT VALID constraint on relation "invalid_check_con_child" @@ -1333,10 +1468,10 @@ alter table invalid_check_con_child add constraint inh_check_constraint check(f1 alter table invalid_check_con add constraint inh_check_constraint check(f1 > 0) not valid; NOTICE: merging constraint "inh_check_constraint" with inherited definition insert into invalid_check_con values(0); -- fail -ERROR: new row for relation "invalid_check_con" violates check constraint "inh_check_constraint" +ERROR: new row for relation "invalid_check_con" violates check constraint "inh_check_constraint" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (0). insert into invalid_check_con_child values(0); -- fail -ERROR: new row for relation "invalid_check_con_child" violates check constraint "inh_check_constraint" +ERROR: new row for relation "invalid_check_con_child" violates check constraint "inh_check_constraint" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (0). select conrelid::regclass::text as relname, conname, convalidated, conislocal, coninhcount, connoinherit @@ -1356,9 +1491,11 @@ create temp table patest0 (id, x) as select x, x from generate_series(0,1000) x distributed by (id); create temp table patest1() inherits (patest0); +NOTICE: table has parent, setting distribution columns to match parent table insert into patest1 select x, x from generate_series(0,1000) x; create temp table patest2() inherits (patest0); +NOTICE: table has parent, setting distribution columns to match parent table insert into patest2 select x, x from generate_series(0,1000) x; create index patest0i on patest0(id); @@ -1371,8 +1508,8 @@ set enable_seqscan=off; set enable_bitmapscan=off; explain (costs off) select * from patest0 join (select f1 from int4_tbl where f1 < 10 and f1 > -10 limit 1) ss on id = f1; - QUERY PLAN ----------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Hash Join Hash Cond: (patest0.id = int4_tbl.f1) @@ -1493,8 +1630,8 @@ select * from matest0 order by 1-id; (6 rows) explain (verbose, costs off) select min(1-id) from matest0; - QUERY PLAN ------------------------------------------------------ + QUERY PLAN +-------------------------------------------------------------- Finalize Aggregate Output: min((1 - matest0.id)) -> Gather Motion 3:1 (slice1; segments: 3) @@ -1528,8 +1665,8 @@ set enable_parallel_append = off; -- Don't let parallel-append interfere -- of append with bitmapscan + sort set enable_bitmapscan = off; explain (verbose, costs off) select * from matest0 order by 1-id; - QUERY PLAN ------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Output: matest0.id, matest0.name, ((1 - matest0.id)) Merge Key: ((1 - matest0.id)) @@ -1562,8 +1699,8 @@ select * from matest0 order by 1-id; (6 rows) explain (verbose, costs off) select min(1-id) from matest0; - QUERY PLAN --------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- Result Output: $0 InitPlan 1 (returns $0) (slice1) @@ -1614,7 +1751,10 @@ drop cascades to table matest3 -- a plan with extraneous sorting -- create table matest0 (a int, b int, c int, d int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table matest1 () inherits(matest0); +NOTICE: table has parent, setting distribution columns to match parent table create index matest0i on matest0 (b, c); create index matest1i on matest1 (b, c); set enable_nestloop = off; -- we want a plan with two MergeAppends @@ -1678,7 +1818,7 @@ ORDER BY thousand, tenthous; -> Append -> Seq Scan on tenk1 -> Seq Scan on tenk1 tenk1_1 - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (8 rows) explain (costs off) @@ -1686,8 +1826,8 @@ SELECT thousand, tenthous, thousand+tenthous AS x FROM tenk1 UNION ALL SELECT 42, 42, hundred FROM tenk1 ORDER BY thousand, tenthous; - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +-------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: tenk1.thousand, tenk1.tenthous -> Sort @@ -1695,7 +1835,7 @@ ORDER BY thousand, tenthous; -> Append -> Seq Scan on tenk1 -> Seq Scan on tenk1 tenk1_1 - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (8 rows) explain (costs off) @@ -1712,7 +1852,7 @@ ORDER BY thousand, tenthous; -> Append -> Seq Scan on tenk1 -> Seq Scan on tenk1 tenk1_1 - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (8 rows) -- Check min/max aggregate optimization @@ -1721,15 +1861,15 @@ SELECT min(x) FROM (SELECT unique1 AS x FROM tenk1 a UNION ALL SELECT unique2 AS x FROM tenk1 b) s; - QUERY PLAN ---------------------------------------------------- + QUERY PLAN +------------------------------------------------ Finalize Aggregate -> Gather Motion 3:1 (slice1; segments: 3) -> Partial Aggregate -> Append -> Seq Scan on tenk1 a -> Seq Scan on tenk1 b - Optimizer: Pivotal Optimizer (GPORCA) version 2.74.0 + Optimizer: GPORCA (7 rows) explain (costs off) @@ -1737,15 +1877,15 @@ SELECT min(y) FROM (SELECT unique1 AS x, unique1 AS y FROM tenk1 a UNION ALL SELECT unique2 AS x, unique2 AS y FROM tenk1 b) s; - QUERY PLAN ---------------------------------------------------- + QUERY PLAN +------------------------------------------------ Finalize Aggregate -> Gather Motion 3:1 (slice1; segments: 3) -> Partial Aggregate -> Append -> Seq Scan on tenk1 a -> Seq Scan on tenk1 b - Optimizer: Pivotal Optimizer (GPORCA) version 3.83.0 + Optimizer: GPORCA (7 rows) -- XXX planner doesn't recognize that index on unique2 is sufficiently sorted @@ -1755,8 +1895,8 @@ SELECT x, y FROM UNION ALL SELECT unique2 AS x, unique2 AS y FROM tenk1 b) s ORDER BY x, y; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) Merge Key: a.thousand, a.tenthous -> Sort @@ -1764,7 +1904,7 @@ ORDER BY x, y; -> Append -> Seq Scan on tenk1 a -> Seq Scan on tenk1 b - Optimizer: Pivotal Optimizer (GPORCA) version 2.74.0 + Optimizer: GPORCA (8 rows) -- exercise rescan code path via a repeatedly-evaluated subquery @@ -1816,7 +1956,10 @@ rollback; -- Check handling of a constant-null CHECK constraint -- create table cnullparent (f1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'f1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table cnullchild (check (f1 = 1 or f1 = null)) inherits(cnullparent); +NOTICE: table has parent, setting distribution columns to match parent table insert into cnullchild values(1); insert into cnullchild values(2); insert into cnullchild values(null); @@ -1840,11 +1983,18 @@ NOTICE: drop cascades to table cnullchild -- Check use of temporary tables with inheritance trees -- create table inh_perm_parent (a1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table inh_temp_parent (a1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table inh_temp_child () inherits (inh_perm_parent); -- ok +NOTICE: table has parent, setting distribution columns to match parent table create table inh_perm_child () inherits (inh_temp_parent); -- error +NOTICE: table has parent, setting distribution columns to match parent table ERROR: cannot inherit from temporary relation "inh_temp_parent" create temp table inh_temp_child_2 () inherits (inh_temp_parent); -- ok +NOTICE: table has parent, setting distribution columns to match parent table insert into inh_perm_parent values (1); insert into inh_temp_parent values (2); insert into inh_temp_child values (3); @@ -1852,8 +2002,8 @@ insert into inh_temp_child_2 values (4); select tableoid::regclass, a1 from inh_perm_parent; tableoid | a1 -----------------+---- - inh_perm_parent | 1 inh_temp_child | 3 + inh_perm_parent | 1 (2 rows) select tableoid::regclass, a1 from inh_temp_parent; @@ -1874,85 +2024,117 @@ NOTICE: drop cascades to table inh_temp_child_2 create table list_parted ( a varchar ) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_ab_cd partition of list_parted for values in ('ab', 'cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_ef_gh partition of list_parted for values in ('ef', 'gh'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_null_xy partition of list_parted for values in (null, 'xy'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from list_parted; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: list_parted +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on list_parted Number of partitions to scan: 3 (out of 3) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (4 rows) explain (costs off) select * from list_parted where a is null; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: list_parted +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on list_parted Number of partitions to scan: 1 (out of 3) Filter: (a IS NULL) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from list_parted where a is not null; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: list_parted +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on list_parted Number of partitions to scan: 3 (out of 3) Filter: (NOT (a IS NULL)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from list_parted where a in ('ab', 'cd', 'ef'); - QUERY PLAN +NOTICE: One or more columns in the following table(s) do not have statistics: list_parted +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN ---------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on list_parted Number of partitions to scan: 2 (out of 3) Filter: ((a)::text = ANY ('{ab,cd,ef}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from list_parted where a = 'ab' or a in (null, 'cd'); - QUERY PLAN +NOTICE: One or more columns in the following table(s) do not have statistics: list_parted +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN --------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on list_parted Number of partitions to scan: 1 (out of 3) Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[]))) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from list_parted where a = 'ab'; +NOTICE: One or more columns in the following table(s) do not have statistics: list_parted +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on list_parted Number of partitions to scan: 1 (out of 3) Filter: ((a)::text = 'ab'::text) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) create table range_list_parted ( a int, b char(2) ) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_1_10 partition of range_list_parted for values from (1) to (10) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table part_1_10_ab partition of part_1_10 for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_1_10_cd partition of part_1_10 for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_10_20 partition of range_list_parted for values from (10) to (20) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table part_10_20_ab partition of part_10_20 for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_10_20_cd partition of part_10_20 for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_21_30 partition of range_list_parted for values from (21) to (30) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table part_21_30_ab partition of part_21_30 for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_21_30_cd partition of part_21_30 for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_40_inf partition of range_list_parted for values from (40) to (maxvalue) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table part_40_inf_ab partition of part_40_inf for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_40_inf_cd partition of part_40_inf for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table part_40_inf_null partition of part_40_inf for values in (null); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from range_list_parted; QUERY PLAN -------------------------------------------------------------- @@ -2014,16 +2196,17 @@ explain (costs off) select * from range_list_parted where a between 3 and 23 and /* Should select no rows because range partition key cannot be null */ explain (costs off) select * from range_list_parted where a is null; - QUERY PLAN --------------------------- + QUERY PLAN +------------------------------------- Result One-Time Filter: false -(2 rows) + Optimizer: Postgres query optimizer +(3 rows) /* Should only select rows from the null-accepting partition */ explain (costs off) select * from range_list_parted where b is null; - QUERY PLAN ------------------------------------------- + QUERY PLAN +------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) -> Seq Scan on part_40_inf_null range_list_parted Filter: (b IS NULL) @@ -2031,8 +2214,8 @@ explain (costs off) select * from range_list_parted where b is null; (4 rows) explain (costs off) select * from range_list_parted where a is not null and a < 67; - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Append -> Seq Scan on part_1_10_ab range_list_parted_1 @@ -2075,16 +2258,25 @@ drop table range_list_parted; -- check that constraint exclusion is able to cope with the partition -- constraint emitted for multi-column range partitioned tables create table mcrparted (a int, b int, c int) partition by range (a, abs(b), c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mcrparted_def partition of mcrparted default; +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted0 partition of mcrparted for values from (minvalue, minvalue, minvalue) to (1, 1, 1); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted1 partition of mcrparted for values from (1, 1, 1) to (10, 5, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted2 partition of mcrparted for values from (10, 5, 10) to (10, 10, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted3 partition of mcrparted for values from (11, 1, 1) to (20, 10, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted4 partition of mcrparted for values from (20, 10, 10) to (20, 20, 20); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mcrparted where a = 0; -- scans mcrparted0, mcrparted_def - QUERY PLAN ------------------------------------------- + QUERY PLAN +--------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Append -> Seq Scan on mcrparted0 mcrparted_1 @@ -2121,8 +2313,8 @@ explain (costs off) select * from mcrparted where a = 10 and abs(b) = 5; -- scan (9 rows) explain (costs off) select * from mcrparted where abs(b) = 5; -- scans all partitions - QUERY PLAN ------------------------------------------- + QUERY PLAN +--------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Append -> Seq Scan on mcrparted0 mcrparted_1 @@ -2143,8 +2335,8 @@ explain (costs off) select * from mcrparted where abs(b) = 5; -- scans all parti (17 rows) explain (costs off) select * from mcrparted where a > -1; -- scans all partitions - QUERY PLAN -------------------------------------------- + QUERY PLAN +--------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Append -> Seq Scan on mcrparted0 mcrparted_1 @@ -2174,8 +2366,8 @@ explain (costs off) select * from mcrparted where a = 20 and abs(b) = 10 and c > (4 rows) explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mcrparted3, mcrparte4, mcrparte5, mcrparted_def - QUERY PLAN ------------------------------------------------ + QUERY PLAN +--------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Append -> Seq Scan on mcrparted3 mcrparted_1 @@ -2192,22 +2384,29 @@ explain (costs off) select * from mcrparted where a = 20 and c > 20; -- scans mc -- check that partitioned table Appends cope with being referenced in -- subplans create table parted_minmax (a int, b varchar(16)) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table parted_minmax1 partition of parted_minmax for values from (1) to (10); +NOTICE: table has parent, setting distribution columns to match parent table create index parted_minmax1i on parted_minmax1 (a, b); insert into parted_minmax values (1,'12345'); explain (costs off) select min(a), max(a) from parted_minmax where b = '12345'; - QUERY PLAN ---------------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: parted_minmax +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------------------- Finalize Aggregate -> Gather Motion 3:1 (slice1; segments: 3) -> Partial Aggregate -> Dynamic Seq Scan on parted_minmax Number of partitions to scan: 1 (out of 1) Filter: ((b)::text = '12345'::text) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (7 rows) select min(a), max(a) from parted_minmax where b = '12345'; +NOTICE: One or more columns in the following table(s) do not have statistics: parted_minmax +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. min | max -----+----- 1 | 1 @@ -2219,8 +2418,8 @@ drop table parted_minmax; create index mcrparted_a_abs_c_idx on mcrparted (a, abs(b), c); -- MergeAppend must be used when a default partition exists explain (costs off) select * from mcrparted order by a, abs(b), c; - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c -> Merge Append @@ -2273,11 +2472,14 @@ explain (costs off) select * from mcrparted order by a desc, abs(b) desc, c desc -- that are unordered. drop table mcrparted5; create table mcrparted5 partition of mcrparted for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue) partition by list (a); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted5a partition of mcrparted5 for values in(20); +NOTICE: table has parent, setting distribution columns to match parent table create table mcrparted5_def partition of mcrparted5 default; +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mcrparted order by a, abs(b), c; - QUERY PLAN ---------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c -> Append @@ -2298,8 +2500,8 @@ drop table mcrparted5_def; -- into the main Append when the sub-partition is unordered but contains -- just a single sub-partition. explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c; - QUERY PLAN ---------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c -> Append @@ -2315,8 +2517,8 @@ explain (costs off) select a, abs(b) from mcrparted order by a, abs(b), c; -- check that Append is used when the sub-partitioned tables are pruned -- during planning. explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c -> Append @@ -2332,36 +2534,46 @@ explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c; (12 rows) create table mclparted (a int) partition by list(a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mclparted1 partition of mclparted for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table mclparted2 partition of mclparted for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table create index on mclparted (a); -- Ensure an Append is used for a list partition with an order by. explain (costs off) select * from mclparted order by a; - QUERY PLAN ------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: mclparted +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: a -> Sort Sort Key: a -> Dynamic Seq Scan on mclparted Number of partitions to scan: 2 (out of 2) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (7 rows) -- Ensure a MergeAppend is used when a partition exists with interleaved -- datums in the partition bound. create table mclparted3_5 partition of mclparted for values in(3,5); +NOTICE: table has parent, setting distribution columns to match parent table create table mclparted4 partition of mclparted for values in(4); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mclparted order by a; - QUERY PLAN ------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: mclparted +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: a -> Sort Sort Key: a -> Dynamic Seq Scan on mclparted Number of partitions to scan: 4 (out of 4) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (7 rows) drop table mclparted; @@ -2373,8 +2585,8 @@ create index on mcrparted2 (a, abs(b), c); create index on mcrparted3 (a, abs(b), c); create index on mcrparted4 (a, abs(b), c); explain (costs off) select * from mcrparted where a < 20 order by a, abs(b), c limit 1; - QUERY PLAN -------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------- Limit -> Gather Motion 3:1 (slice1; segments: 3) Merge Key: mcrparted.a, (abs(mcrparted.b)), mcrparted.c @@ -2397,8 +2609,8 @@ set enable_bitmapscan = 0; -- Ensure Append node can be used when the partition is ordered by some -- pathkeys which were deemed redundant. explain (costs off) select * from mcrparted where a = 10 order by a, abs(b), c; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) Merge Key: (abs(mcrparted.b)), mcrparted.c -> Append @@ -2413,32 +2625,44 @@ reset enable_bitmapscan; drop table mcrparted; -- Ensure LIST partitions allow an Append to be used instead of a MergeAppend create table bool_lp (b bool) partition by list(b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table bool_lp_true partition of bool_lp for values in(true); +NOTICE: table has parent, setting distribution columns to match parent table create table bool_lp_false partition of bool_lp for values in(false); +NOTICE: table has parent, setting distribution columns to match parent table create index on bool_lp (b); explain (costs off) select * from bool_lp order by b; - QUERY PLAN ------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: bool_lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: b -> Sort Sort Key: b -> Dynamic Seq Scan on bool_lp Number of partitions to scan: 2 (out of 2) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (7 rows) drop table bool_lp; -- Ensure const bool quals can be properly detected as redundant create table bool_rp (b bool, a int) partition by range(b,a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table bool_rp_false_1k partition of bool_rp for values from (false,0) to (false,1000); +NOTICE: table has parent, setting distribution columns to match parent table create table bool_rp_true_1k partition of bool_rp for values from (true,0) to (true,1000); +NOTICE: table has parent, setting distribution columns to match parent table create table bool_rp_false_2k partition of bool_rp for values from (false,1000) to (false,2000); +NOTICE: table has parent, setting distribution columns to match parent table create table bool_rp_true_2k partition of bool_rp for values from (true,1000) to (true,2000); +NOTICE: table has parent, setting distribution columns to match parent table create index on bool_rp (b,a); explain (costs off) select * from bool_rp where b = true order by b,a; - QUERY PLAN ------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) Merge Key: bool_rp.b, bool_rp.a -> Append @@ -2450,8 +2674,8 @@ explain (costs off) select * from bool_rp where b = true order by b,a; (8 rows) explain (costs off) select * from bool_rp where b = false order by b,a; - QUERY PLAN --------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------ Gather Motion 1:1 (slice1; segments: 1) Merge Key: bool_rp.b, bool_rp.a -> Append @@ -2466,8 +2690,8 @@ explain (costs off) select * from bool_rp where b = false order by b,a; set enable_seqscan=off; set enable_bitmapscan=off; explain (costs off) select * from bool_rp where b = true order by a; - QUERY PLAN ------------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) Merge Key: bool_rp.a -> Sort @@ -2481,8 +2705,8 @@ explain (costs off) select * from bool_rp where b = true order by a; (10 rows) explain (costs off) select * from bool_rp where b = false order by a; - QUERY PLAN --------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------ Gather Motion 1:1 (slice1; segments: 1) Merge Key: bool_rp.a -> Sort @@ -2501,12 +2725,16 @@ drop table bool_rp; -- Ensure an Append scan is chosen when the partition order is a subset of -- the required order. create table range_parted (a int, b int, c int) partition by range(a, b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table range_parted1 partition of range_parted for values from (0,0) to (10,10); +NOTICE: table has parent, setting distribution columns to match parent table create table range_parted2 partition of range_parted for values from (10,10) to (20,20); +NOTICE: table has parent, setting distribution columns to match parent table create index on range_parted (a,b,c); explain (costs off) select * from range_parted order by a,b,c; - QUERY PLAN ----------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: range_parted.a, range_parted.b, range_parted.c -> Append @@ -2516,8 +2744,8 @@ explain (costs off) select * from range_parted order by a,b,c; (6 rows) explain (costs off) select * from range_parted order by a desc,b desc,c desc; - QUERY PLAN -------------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Merge Key: range_parted.a, range_parted.b, range_parted.c -> Append @@ -2530,6 +2758,8 @@ drop table range_parted; -- Check that we allow access to a child table's statistics when the user -- has permissions only for the parent table. create table permtest_parent (a int, b text, c text) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table permtest_child (b text, c text, a int) partition by list (b) distributed by (a); create table permtest_grandchild (c text, b text, a int) distributed by (a); alter table permtest_child attach partition permtest_grandchild for values in ('a'); @@ -2539,6 +2769,7 @@ insert into permtest_parent select 1, 'a', left(md5(i::text), 5) from generate_series(0, 100) i; analyze permtest_parent; create role regress_no_child_access; +NOTICE: resource queue required -- using default resource queue "pg_default" revoke all on permtest_grandchild from regress_no_child_access; NOTICE: no privileges could be revoked grant select on permtest_parent to regress_no_child_access; @@ -2547,8 +2778,8 @@ set session authorization regress_no_child_access; explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c ~ 'a1$'; - QUERY PLAN ------------------------------------------- + QUERY PLAN +------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) -> Hash Join Hash Cond: (p2.a = p1.a) @@ -2563,8 +2794,8 @@ explain (costs off) explain (costs off) select * from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and left(p1.c, 3) ~ 'a1$'; - QUERY PLAN ----------------------------------------------- + QUERY PLAN +---------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Hash Join Hash Cond: (p2.a = p1.a) @@ -2583,8 +2814,8 @@ set session authorization regress_no_child_access; explain (costs off) select p2.a, p1.c from permtest_parent p1 inner join permtest_parent p2 on p1.a = p2.a and p1.c ~ 'a1$'; - QUERY PLAN ------------------------------------------- + QUERY PLAN +------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) -> Hash Join Hash Cond: (p2.a = p1.a) @@ -2625,6 +2856,8 @@ CREATE TABLE errtst_parent ( data int NOT NULL DEFAULT 0, CONSTRAINT shdata_small CHECK(shdata < 3) ) PARTITION BY RANGE (partid); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'partid' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- fast defaults lead to attribute mapping being used in one -- direction, but not the other CREATE TABLE errtst_child_fastdef ( @@ -2632,6 +2865,8 @@ CREATE TABLE errtst_child_fastdef ( shdata int not null, CONSTRAINT shdata_small CHECK(shdata < 3) ); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'partid' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- no remapping in either direction necessary CREATE TABLE errtst_child_plaindef ( partid int not null, @@ -2640,6 +2875,8 @@ CREATE TABLE errtst_child_plaindef ( CONSTRAINT shdata_small CHECK(shdata < 3), CHECK(data < 10) ); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'partid' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- remapping in both direction CREATE TABLE errtst_child_reorder ( data int NOT NULL DEFAULT 0, @@ -2659,33 +2896,33 @@ INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', '5'); INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '5'); -- insert with child check constraint error INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', '10'); -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" +ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (0, 1, 10). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', '10'); -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" +ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3130282) DETAIL: Failing row contains (10, 1, 10). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', '10'); -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" +ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (20, 1, 10). -- insert with child not null constraint error INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_fastdef" violates not-null constraint +ERROR: null value in column "data" of relation "errtst_child_fastdef" violates not-null constraint (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (0, 1, null). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_plaindef" violates not-null constraint +ERROR: null value in column "data" of relation "errtst_child_plaindef" violates not-null constraint (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3130282) DETAIL: Failing row contains (10, 1, null). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '1', NULL); -ERROR: null value in column "data" of relation "errtst_child_reorder" violates not-null constraint +ERROR: null value in column "data" of relation "errtst_child_reorder" violates not-null constraint (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (20, 1, null). -- insert with shared check constraint error INSERT INTO errtst_parent(partid, shdata, data) VALUES ( '0', '5', '5'); -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "shdata_small" +ERROR: new row for relation "errtst_child_fastdef" violates check constraint "shdata_small" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (0, 5, 5). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('10', '5', '5'); -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "shdata_small" +ERROR: new row for relation "errtst_child_plaindef" violates check constraint "shdata_small" (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3130282) DETAIL: Failing row contains (10, 5, 5). INSERT INTO errtst_parent(partid, shdata, data) VALUES ('20', '5', '5'); -ERROR: new row for relation "errtst_child_reorder" violates check constraint "shdata_small" +ERROR: new row for relation "errtst_child_reorder" violates check constraint "shdata_small" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (20, 5, 5). -- within partition update without child check constraint violation BEGIN; @@ -2695,13 +2932,13 @@ UPDATE errtst_parent SET data = data + 1 WHERE partid = 20; ROLLBACK; -- within partition update with child check constraint violation UPDATE errtst_parent SET data = data + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" +ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (0, 1, 15). UPDATE errtst_parent SET data = data + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" +ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3130282) DETAIL: Failing row contains (10, 1, 15). UPDATE errtst_parent SET data = data + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" +ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (20, 1, 15). -- direct leaf partition update, without partition id violation BEGIN; @@ -2711,13 +2948,13 @@ UPDATE errtst_child_reorder SET partid = 21 WHERE partid = 20; ROLLBACK; -- direct leaf partition update, with partition id violation UPDATE errtst_child_fastdef SET partid = partid + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_fastdef" violates partition constraint +ERROR: new row for relation "errtst_child_fastdef" violates partition constraint (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3130282) DETAIL: Failing row contains (10, 1, 5). UPDATE errtst_child_plaindef SET partid = partid + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_plaindef" violates partition constraint +ERROR: new row for relation "errtst_child_plaindef" violates partition constraint (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (20, 1, 5). UPDATE errtst_child_reorder SET partid = partid + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_reorder" violates partition constraint +ERROR: new row for relation "errtst_child_reorder" violates partition constraint (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (5, 1, 30). -- partition move, without child check constraint violation BEGIN; @@ -2727,16 +2964,16 @@ UPDATE errtst_parent SET partid = 0, data = data + 1 WHERE partid = 20; ROLLBACK; -- partition move, with child check constraint violation UPDATE errtst_parent SET partid = 10, data = data + 10 WHERE partid = 0; -ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" +ERROR: new row for relation "errtst_child_plaindef" violates check constraint "errtst_child_plaindef_data_check" (seg2 2a02:6b8:c37:834b:0:5644:602c:0:7004 pid=3130282) DETAIL: Failing row contains (10, 1, 15). UPDATE errtst_parent SET partid = 20, data = data + 10 WHERE partid = 10; -ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" +ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (20, 1, 15). UPDATE errtst_parent SET partid = 0, data = data + 10 WHERE partid = 20; -ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" +ERROR: new row for relation "errtst_child_fastdef" violates check constraint "errtest_child_fastdef_data_check" (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Failing row contains (0, 1, 15). -- partition move, without target partition UPDATE errtst_parent SET partid = 30, data = data + 10 WHERE partid = 20; -ERROR: no partition of relation "errtst_parent" found for row +ERROR: no partition of relation "errtst_parent" found for row (seg1 2a02:6b8:c37:834b:0:5644:602c:0:7003 pid=3130276) DETAIL: Partition key of the failing row contains (partid) = (30). DROP TABLE errtst_parent; diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 2fb7ba06584..be4221fac9f 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1531,10 +1531,10 @@ WHERE a.aggfnoid = p.oid AND ); aggfnoid | proname | oid | proname ----------+--------------------+------+--------------------- - 9189 | gp_percentile_cont | 9188 | gp_percentile_final - 9190 | gp_percentile_cont | 9188 | gp_percentile_final - 9191 | gp_percentile_cont | 9188 | gp_percentile_final 9192 | gp_percentile_cont | 9188 | gp_percentile_final + 9191 | gp_percentile_cont | 9188 | gp_percentile_final + 9190 | gp_percentile_cont | 9188 | gp_percentile_final + 9189 | gp_percentile_cont | 9188 | gp_percentile_final (4 rows) -- If transfn is strict then either initval should be non-NULL, or @@ -2231,6 +2231,7 @@ ORDER BY 1, 2, 3; | complex_ops | complex_ops | complex | float_ops | float4_ops | real | float_ops | float8_ops | double precision + | interval_ops | interval_ops | interval | jsonb_ops | jsonb_ops | jsonb | multirange_ops | multirange_ops | anymultirange | numeric_ops | numeric_ops | numeric @@ -2239,7 +2240,7 @@ ORDER BY 1, 2, 3; | record_ops | record_ops | record | tsquery_ops | tsquery_ops | tsquery | tsvector_ops | tsvector_ops | tsvector -(16 rows) +(17 rows) -- **************** pg_index **************** -- Look for illegal values in pg_index fields. diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 75e646374be..abb76904d53 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -27,12 +27,20 @@ -- Force generic plans to be used for all prepared statements in this file. set plan_cache_mode = force_generic_plan; create table lp (a char) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table lp_default partition of lp default; +NOTICE: table has parent, setting distribution columns to match parent table create table lp_ef partition of lp for values in ('e', 'f'); +NOTICE: table has parent, setting distribution columns to match parent table create table lp_ad partition of lp for values in ('a', 'd'); +NOTICE: table has parent, setting distribution columns to match parent table create table lp_bc partition of lp for values in ('b', 'c'); +NOTICE: table has parent, setting distribution columns to match parent table create table lp_g partition of lp for values in ('g'); +NOTICE: table has parent, setting distribution columns to match parent table create table lp_null partition of lp for values in (null); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from lp; QUERY PLAN ------------------------------------------ @@ -192,9 +200,14 @@ explain (costs off) select * from lp where a not in ('a', 'd'); -- collation matches the partitioning collation, pruning works create table coll_pruning (a text collate "C") partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table coll_pruning_a partition of coll_pruning for values in ('a'); +NOTICE: table has parent, setting distribution columns to match parent table create table coll_pruning_b partition of coll_pruning for values in ('b'); +NOTICE: table has parent, setting distribution columns to match parent table create table coll_pruning_def partition of coll_pruning default; +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from coll_pruning where a collate "C" = 'a' collate "C"; QUERY PLAN ----------------------------------------------- @@ -220,28 +233,50 @@ explain (costs off) select * from coll_pruning where a collate "POSIX" = 'a' col (9 rows) create table rlp (a int, b varchar) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rlp_default partition of rlp default partition by list (a); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp_default_default partition of rlp_default default; +NOTICE: table has parent, setting distribution columns to match parent table create table rlp_default_10 partition of rlp_default for values in (10); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp_default_30 partition of rlp_default for values in (30); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp_default_null partition of rlp_default for values in (null); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp1 partition of rlp for values from (minvalue) to (1); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp2 partition of rlp for values from (1) to (10); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp3 (b varchar, a int) partition by list (b varchar_ops); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- GPDB: distribution policy must match the parent table. alter table rlp3 set distributed by (a); create table rlp3_default partition of rlp3 default; +NOTICE: table has parent, setting distribution columns to match parent table create table rlp3abcd partition of rlp3 for values in ('ab', 'cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp3efgh partition of rlp3 for values in ('ef', 'gh'); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp3nullxy partition of rlp3 for values in (null, 'xy'); +NOTICE: table has parent, setting distribution columns to match parent table alter table rlp attach partition rlp3 for values from (15) to (20); create table rlp4 partition of rlp for values from (20) to (30) partition by range (a); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp4_default partition of rlp4 default; +NOTICE: table has parent, setting distribution columns to match parent table create table rlp4_1 partition of rlp4 for values from (20) to (25); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp4_2 partition of rlp4 for values from (25) to (29); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp5 partition of rlp for values from (31) to (maxvalue) partition by range (a); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp5_default partition of rlp5 default; +NOTICE: table has parent, setting distribution columns to match parent table create table rlp5_1 partition of rlp5 for values from (31) to (40); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from rlp where a < 1; QUERY PLAN ------------------------------------------ @@ -792,15 +827,26 @@ explain (costs off) select * from rlp where (a = 1 and a = 3) or (a > 1 and a = -- multi-column keys create table mc3p (a int, b int, c int) partition by range (a, abs(b), c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mc3p_default partition of mc3p default; +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p0 partition of mc3p for values from (minvalue, minvalue, minvalue) to (1, 1, 1); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p1 partition of mc3p for values from (1, 1, 1) to (10, 5, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p2 partition of mc3p for values from (10, 5, 10) to (10, 10, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p3 partition of mc3p for values from (10, 10, 10) to (10, 10, 20); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p4 partition of mc3p for values from (10, 10, 20) to (10, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p5 partition of mc3p for values from (11, 1, 1) to (20, 10, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p6 partition of mc3p for values from (20, 10, 10) to (20, 20, 20); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p7 partition of mc3p for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mc3p where a = 1; QUERY PLAN --------------------------------------------- @@ -1103,13 +1149,22 @@ explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 a -- a simpler multi-column keys case create table mc2p (a int, b int) partition by range (a, b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mc2p_default partition of mc2p default; +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p0 partition of mc2p for values from (minvalue, minvalue) to (1, minvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p1 partition of mc2p for values from (1, minvalue) to (1, 1); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p2 partition of mc2p for values from (1, 1) to (2, minvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p3 partition of mc2p for values from (2, minvalue) to (2, 1); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p4 partition of mc2p for values from (2, 1) to (2, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p5 partition of mc2p for values from (2, maxvalue) to (maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mc2p where a < 2; QUERY PLAN --------------------------------------------- @@ -1210,9 +1265,14 @@ explain (costs off) select * from mc2p where b is null; -- boolean partitioning create table boolpart (a bool) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table boolpart_default partition of boolpart default; +NOTICE: table has parent, setting distribution columns to match parent table create table boolpart_t partition of boolpart for values in ('true'); +NOTICE: table has parent, setting distribution columns to match parent table create table boolpart_f partition of boolpart for values in ('false'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from boolpart where a in (true, false); QUERY PLAN ------------------------------------------------------ @@ -1301,10 +1361,16 @@ explain (costs off) select * from boolpart where a is not unknown; (9 rows) create table boolrangep (a bool, b bool, c int) partition by range (a,b,c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table boolrangep_tf partition of boolrangep for values from ('true', 'false', 0) to ('true', 'false', 100); +NOTICE: table has parent, setting distribution columns to match parent table create table boolrangep_ft partition of boolrangep for values from ('false', 'true', 0) to ('false', 'true', 100); +NOTICE: table has parent, setting distribution columns to match parent table create table boolrangep_ff1 partition of boolrangep for values from ('false', 'false', 0) to ('false', 'false', 50); +NOTICE: table has parent, setting distribution columns to match parent table create table boolrangep_ff2 partition of boolrangep for values from ('false', 'false', 50) to ('false', 'false', 100); +NOTICE: table has parent, setting distribution columns to match parent table -- try a more complex case that's been known to trip up pruning in the past explain (costs off) select * from boolrangep where not a and not b and c = 25; QUERY PLAN @@ -1317,9 +1383,14 @@ explain (costs off) select * from boolrangep where not a and not b and c = 25; -- test scalar-to-array operators create table coercepart (a varchar) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table coercepart_ab partition of coercepart for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table coercepart_bc partition of coercepart for values in ('bc'); +NOTICE: table has parent, setting distribution columns to match parent table create table coercepart_cd partition of coercepart for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from coercepart where a in ('ab', to_char(125, '999')); QUERY PLAN ----------------------------------------------------------- @@ -1449,10 +1520,17 @@ explain (costs off) select * from coercepart where a = all (null::text[]); drop table coercepart; CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT; +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE part_rev (b INT, c INT, a INT); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- The distribution key must be the same for partition tables. ALTER TABLE part_rev SET DISTRIBUTED BY(a); ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- fail @@ -1474,8 +1552,8 @@ EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS (7 rows) EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM part p(x) ORDER BY x; - QUERY PLAN ------------------------------------------------------ + QUERY PLAN +--------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) Output: p.x, p.b Merge Key: p.x @@ -1489,8 +1567,8 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM part p(x) ORDER BY x; Output: p_2.x, p_2.b -> Seq Scan on public.part_p2_p1 p_3 Output: p_3.x, p_3.b + Settings: optimizer = 'off', plan_cache_mode = 'force_generic_plan' Optimizer: Postgres query optimizer - Settings: plan_cache_mode = 'force_generic_plan' (15 rows) -- @@ -1597,9 +1675,14 @@ explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 -- -- doesn't prune range partitions create table rp (a int) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rp0 partition of rp for values from (minvalue) to (1); +NOTICE: table has parent, setting distribution columns to match parent table create table rp1 partition of rp for values from (1) to (2); +NOTICE: table has parent, setting distribution columns to match parent table create table rp2 partition of rp for values from (2) to (maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from rp where a <> 1; QUERY PLAN ------------------------------------------ @@ -1693,9 +1776,14 @@ explain (costs off) select * from rlp where a = 15 and b <> 'ab' and b <> 'cd' a -- different collations for different keys with same expression -- create table coll_pruning_multi (a text) partition by range (substr(a, 1) collate "POSIX", substr(a, 1) collate "C"); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table coll_pruning_multi1 partition of coll_pruning_multi for values from ('a', 'a') to ('a', 'e'); +NOTICE: table has parent, setting distribution columns to match parent table create table coll_pruning_multi2 partition of coll_pruning_multi for values from ('a', 'e') to ('a', 'z'); +NOTICE: table has parent, setting distribution columns to match parent table create table coll_pruning_multi3 partition of coll_pruning_multi for values from ('b', 'a') to ('b', 'e'); +NOTICE: table has parent, setting distribution columns to match parent table -- no pruning, because no value for the leading key explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' collate "C"; QUERY PLAN @@ -1738,8 +1826,12 @@ explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' co -- LIKE operators don't prune -- create table like_op_noprune (a text) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table like_op_noprune1 partition of like_op_noprune for values in ('ABC'); +NOTICE: table has parent, setting distribution columns to match parent table create table like_op_noprune2 partition of like_op_noprune for values in ('BCD'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from like_op_noprune where a like '%BC'; QUERY PLAN ------------------------------------------------------------ @@ -1756,8 +1848,12 @@ explain (costs off) select * from like_op_noprune where a like '%BC'; -- tests wherein clause value requires a cross-type comparison function -- create table lparted_by_int2 (a smallint) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table lparted_by_int2_1 partition of lparted_by_int2 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table lparted_by_int2_16384 partition of lparted_by_int2 for values in (16384); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from lparted_by_int2 where a = 100000000000000; QUERY PLAN ------------------------------------- @@ -1767,8 +1863,12 @@ explain (costs off) select * from lparted_by_int2 where a = 100000000000000; (3 rows) create table rparted_by_int2 (a smallint) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rparted_by_int2_1 partition of rparted_by_int2 for values from (1) to (10); +NOTICE: table has parent, setting distribution columns to match parent table create table rparted_by_int2_16384 partition of rparted_by_int2 for values from (10) to (16384); +NOTICE: table has parent, setting distribution columns to match parent table -- all partitions pruned explain (costs off) select * from rparted_by_int2 where a > 100000000000000; QUERY PLAN @@ -1779,6 +1879,7 @@ explain (costs off) select * from rparted_by_int2 where a > 100000000000000; (3 rows) create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values from (16384) to (maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table -- all partitions but rparted_by_int2_maxvalue pruned explain (costs off) select * from rparted_by_int2 where a > 100000000000000; QUERY PLAN @@ -1799,10 +1900,16 @@ drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pru -- create table hp (a int, b text, c int) partition by hash (a part_test_int4_ops, b part_test_text_ops); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table hp0 partition of hp for values with (modulus 4, remainder 0); +NOTICE: table has parent, setting distribution columns to match parent table create table hp3 partition of hp for values with (modulus 4, remainder 3); +NOTICE: table has parent, setting distribution columns to match parent table create table hp1 partition of hp for values with (modulus 4, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table create table hp2 partition of hp for values with (modulus 4, remainder 2); +NOTICE: table has parent, setting distribution columns to match parent table insert into hp values (null, null, 0); insert into hp values (1, null, 1); insert into hp values (1, 'xxx', 2); @@ -2028,29 +2135,43 @@ explain (costs off) select * from hp where a = 1 and b = 'abcde' and drop table hp2; explain (costs off) select * from hp where a = 1 and b = 'abcde' and (c = 2 or c = 3); - QUERY PLAN --------------------------- + QUERY PLAN +------------------------------------- Result One-Time Filter: false -(2 rows) + Optimizer: Postgres query optimizer +(3 rows) -drop table hp; -- -- Test runtime partition pruning -- create table ab (a int not null, b int not null) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table ab_a2 partition of ab for values in(2) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a2_b1 partition of ab_a2 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a2_b2 partition of ab_a2 for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a2_b3 partition of ab_a2 for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a1 partition of ab for values in(1) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a1_b1 partition of ab_a1 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a1_b2 partition of ab_a1 for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a1_b3 partition of ab_a1 for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a3 partition of ab for values in(3) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a3_b1 partition of ab_a3 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a3_b2 partition of ab_a3 for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a3_b3 partition of ab_a3 for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table -- Disallow index only scans as concurrent transactions may stop visibility -- bits being set causing "Heap Fetches" to be unstable in the EXPLAIN ANALYZE -- output. @@ -2168,12 +2289,44 @@ explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); Optimizer: Postgres query optimizer (12 rows) +-- +-- Test runtime pruning with hash partitioned tables +-- +-- recreate partitions dropped above +create table hp1 partition of hp for values with (modulus 4, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp2 partition of hp for values with (modulus 4, remainder 2); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp3 partition of hp for values with (modulus 4, remainder 3); +NOTICE: table has parent, setting distribution columns to match parent table +-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual +prepare hp_q1 (text) as +select * from hp where a is null and b = $1; +explain (costs off) execute hp_q1('xxx'); + QUERY PLAN +-------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + -> Append + Subplans Removed: 3 + -> Seq Scan on hp2 hp_1 + Filter: ((a IS NULL) AND (b = $1)) + Optimizer: Postgres query optimizer +(6 rows) + +deallocate hp_q1; +drop table hp; -- Test a backwards Append scan create table list_part (a int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table list_part1 partition of list_part for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table list_part2 partition of list_part for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table list_part3 partition of list_part for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table create table list_part4 partition of list_part for values in (4); +NOTICE: table has parent, setting distribution columns to match parent table insert into list_part select generate_series(1,4); begin; -- Don't select an actual value out of the table as the order of the Append's @@ -2367,6 +2520,8 @@ select explain_parallel_append('select count(*) from ab where (a = (select 1) or -- Test pruning during parallel nested loop query create table lprt_a (a int not null); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- Insert some values we won't find in ab insert into lprt_a select 0 from generate_series(1,100); -- and insert some values that we should find. @@ -2411,7 +2566,7 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on Index Cond: (a = ANY ('{0,0,1}'::integer[])) -> Sort (actual rows=N loops=N) Sort Key: a.a - Sort Method: quicksort Memory: 60kB + Sort Method: quicksort Memory: 29kB -> Partition Selector (selector id: $0) (actual rows=N loops=N) -> Seq Scan on lprt_a a (actual rows=N loops=N) Filter: (a = ANY ('{0,0,1}'::integer[])) @@ -2493,7 +2648,7 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on Index Cond: (a = ANY ('{1,0,3}'::integer[])) -> Sort (actual rows=N loops=N) Sort Key: a.a - Sort Method: quicksort Memory: 110kB + Sort Method: quicksort Memory: 54kB -> Partition Selector (selector id: $0) (actual rows=N loops=N) -> Seq Scan on lprt_a a (actual rows=N loops=N) Filter: (a = ANY ('{1,0,3}'::integer[])) @@ -2527,7 +2682,7 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on Index Cond: (a = ANY ('{1,0,0}'::integer[])) -> Sort (actual rows=N loops=N) Sort Key: a.a - Sort Method: quicksort Memory: 60kB + Sort Method: quicksort Memory: 29kB -> Partition Selector (selector id: $0) (actual rows=N loops=N) -> Seq Scan on lprt_a a (actual rows=N loops=N) Filter: (a = ANY ('{1,0,0}'::integer[])) @@ -2562,7 +2717,7 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on Index Cond: (a = ANY ('{1,0,0}'::integer[])) -> Sort (actual rows=N loops=N) Sort Key: a.a - Sort Method: quicksort Memory: 60kB + Sort Method: quicksort Memory: 29kB -> Partition Selector (selector id: $0) (actual rows=N loops=N) -> Seq Scan on lprt_a a (actual rows=N loops=N) Filter: (a = ANY ('{1,0,0}'::integer[])) @@ -2739,6 +2894,8 @@ select * from (select * from ab where a = 1 union all (values(10,5)) union all s -- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning. create table xy_1 (x int, y int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'x' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. insert into xy_1 values(100,-10); set enable_bitmapscan = 0; set enable_indexscan = 0; @@ -2871,25 +3028,35 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1); select tableoid::regclass, * from ab; tableoid | a | b ----------+---+--- + ab_a2_b1 | 2 | 1 ab_a1_b3 | 1 | 3 ab_a1_b3 | 1 | 3 ab_a1_b3 | 1 | 3 - ab_a2_b1 | 2 | 1 (4 rows) drop table ab, lprt_a; -- Join create table tbl1(col1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'col1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. insert into tbl1 values (501), (505); analyze tbl1; -- Basic table create table tprt (col1 int) partition by range (col1); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'col1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table tprt_1 partition of tprt for values from (1) to (501); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_2 partition of tprt for values from (501) to (1001); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_3 partition of tprt for values from (1001) to (2001); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_4 partition of tprt for values from (2001) to (3001); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_5 partition of tprt for values from (3001) to (4001); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_6 partition of tprt for values from (4001) to (5001); +NOTICE: table has parent, setting distribution columns to match parent table create index tprt1_idx on tprt_1 (col1); create index tprt2_idx on tprt_2 (col1); create index tprt3_idx on tprt_3 (col1); @@ -3202,9 +3369,17 @@ order by tbl1.col1, tprt.col1; drop table tbl1, tprt; -- Test with columns defined in varying orders between each level create table part_abc (a int not null, b int not null, c int not null) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_bac (b int not null, a int not null, c int not null) partition by list (b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_cab (c int not null, a int not null, b int not null) partition by list (c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'c' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_abc_p1 (a int not null, b int not null, c int not null); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- GPDB: the distribution keys must be the same in all parts of partition -- hierarchy. alter table part_bac set distributed by (a); @@ -3229,10 +3404,16 @@ drop table part_abc; -- Ensure that an Append node properly handles a sub-partitioned table -- matching without any of its leaf partitions matching the clause. create table listp (a int, b int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table listp_1 partition of listp for values in(1) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_1_1 partition of listp_1 for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_2 partition of listp for values in(2) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_2_1 partition of listp_2 for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table select * from listp where b = 1; a | b ---+--- @@ -3324,10 +3505,13 @@ drop table listp; create table stable_qual_pruning (a timestamp) distributed randomly partition by range (a); create table stable_qual_pruning1 partition of stable_qual_pruning for values from ('2000-01-01') to ('2000-02-01'); +NOTICE: table has parent, setting distribution columns to match parent table create table stable_qual_pruning2 partition of stable_qual_pruning for values from ('2000-02-01') to ('2000-03-01'); +NOTICE: table has parent, setting distribution columns to match parent table create table stable_qual_pruning3 partition of stable_qual_pruning for values from ('3000-02-01') to ('3000-03-01'); +NOTICE: table has parent, setting distribution columns to match parent table -- comparison against a stable value requires run-time pruning explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a < localtimestamp; @@ -3336,9 +3520,9 @@ select * from stable_qual_pruning where a < localtimestamp; Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) -> Append (actual rows=0 loops=1) -> Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (actual rows=0 loops=1) - Filter: (a < 'Mon Oct 14 23:09:32.667368 2024'::timestamp without time zone) + Filter: (a < 'Tue Sep 22 05:21:45.987024 2026'::timestamp without time zone) -> Seq Scan on stable_qual_pruning2 stable_qual_pruning_2 (actual rows=0 loops=1) - Filter: (a < 'Mon Oct 14 23:09:32.667368 2024'::timestamp without time zone) + Filter: (a < 'Tue Sep 22 05:21:45.987024 2026'::timestamp without time zone) Optimizer: Postgres query optimizer (7 rows) @@ -3384,7 +3568,7 @@ select * from stable_qual_pruning ----------------------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) -> Seq Scan on stable_qual_pruning2 stable_qual_pruning (actual rows=0 loops=1) - Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000","Mon Oct 14 23:09:32.675018 2024"}'::timestamp without time zone[])) + Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000","Tue Sep 22 05:21:45.990163 2026"}'::timestamp without time zone[])) Optimizer: Postgres query optimizer (4 rows) @@ -3435,12 +3619,17 @@ drop table stable_qual_pruning; -- non-inclusive operator for an earlier key -- create table mc3p (a int, b int, c int) partition by range (a, abs(b), c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mc3p0 partition of mc3p for values from (0, 0, 0) to (0, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p1 partition of mc3p for values from (1, 1, 1) to (2, minvalue, minvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p2 partition of mc3p for values from (2, minvalue, minvalue) to (3, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table insert into mc3p values (0, 1, 1), (1, 1, 1), (2, 1, 1); explain (analyze, costs off, summary off, timing off) select * from mc3p where a < 3 and abs(b) = 1; @@ -3501,10 +3690,16 @@ deallocate ps2; drop table mc3p; -- Ensure runtime pruning works with initplans params with boolean types create table boolvalues (value bool not null); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'value' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. insert into boolvalues values('t'),('f'); create table boolp (a bool) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table boolp_t partition of boolp for values in('t'); +NOTICE: table has parent, setting distribution columns to match parent table create table boolp_f partition of boolp for values in('f'); +NOTICE: table has parent, setting distribution columns to match parent table explain (analyze, costs off, summary off, timing off) select * from boolp where a = (select value from boolvalues where value); QUERY PLAN @@ -3548,9 +3743,14 @@ drop table boolp; set enable_seqscan = off; set enable_sort = off; create table ma_test (a int, b int) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table ma_test_p1 partition of ma_test for values from (0) to (10); +NOTICE: table has parent, setting distribution columns to match parent table create table ma_test_p2 partition of ma_test for values from (10) to (20); +NOTICE: table has parent, setting distribution columns to match parent table create table ma_test_p3 partition of ma_test for values from (20) to (30); +NOTICE: table has parent, setting distribution columns to match parent table insert into ma_test select x,x from generate_series(0,29) t(x); create index on ma_test (b); analyze ma_test; @@ -3620,8 +3820,8 @@ deallocate mt_q1; prepare mt_q2 (int) as select * from ma_test where a >= $1 order by b limit 1; -- Ensure output list looks sane when the MergeAppend has no subplans. explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35); - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Limit (actual rows=0 loops=1) Output: ma_test.a, ma_test.b -> Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -3632,8 +3832,8 @@ explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35 -> Merge Append (actual rows=0 loops=1) Sort Key: ma_test.b Subplans Removed: 3 + Settings: enable_hashjoin = 'off', enable_indexonlyscan = 'off', enable_mergejoin = 'off', enable_seqscan = 'off', enable_sort = 'off', optimizer = 'off', plan_cache_mode = 'force_generic_plan' Optimizer: Postgres query optimizer - Settings: enable_hashjoin = 'off', enable_indexonlyscan = 'off', enable_mergejoin = 'off', enable_seqscan = 'off', enable_sort = 'off', plan_cache_mode = 'force_generic_plan' (12 rows) deallocate mt_q2; @@ -3672,8 +3872,12 @@ reset enable_indexonlyscan; -- -- array type list partition key create table pp_arrpart (a int[]) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_arrpart1 partition of pp_arrpart for values in ('{1}'); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_arrpart2 partition of pp_arrpart for values in ('{2, 3}', '{4, 5}'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_arrpart where a = '{1}'; QUERY PLAN ------------------------------------------ @@ -3726,8 +3930,12 @@ explain (costs off) delete from pp_arrpart where a = '{1}'; drop table pp_arrpart; -- array type hash partition key create table pph_arrpart (a int[]) partition by hash (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pph_arrpart1 partition of pph_arrpart for values with (modulus 2, remainder 0); +NOTICE: table has parent, setting distribution columns to match parent table create table pph_arrpart2 partition of pph_arrpart for values with (modulus 2, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table insert into pph_arrpart values ('{1}'), ('{1, 2}'), ('{4, 5}'); select tableoid::regclass, * from pph_arrpart order by 1; tableoid | a @@ -3771,11 +3979,15 @@ drop table pph_arrpart; -- enum type list partition key create type pp_colors as enum ('green', 'blue', 'black'); create table pp_enumpart (a pp_colors) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_enumpart_green partition of pp_enumpart for values in ('green'); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_enumpart_blue partition of pp_enumpart for values in ('blue'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_enumpart where a = 'blue'; - QUERY PLAN ------------------------------------------- + QUERY PLAN +------------------------------------------------ Gather Motion 1:1 (slice1; segments: 1) -> Seq Scan on pp_enumpart_blue pp_enumpart Filter: (a = 'blue'::pp_colors) @@ -3795,8 +4007,12 @@ drop type pp_colors; -- record type as partition key create type pp_rectype as (a int, b int); create table pp_recpart (a pp_rectype) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_recpart_11 partition of pp_recpart for values in ('(1,1)'); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_recpart_23 partition of pp_recpart for values in ('(2,3)'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_recpart where a = '(1,1)'::pp_rectype; QUERY PLAN -------------------------------------------- @@ -3818,8 +4034,12 @@ drop table pp_recpart; drop type pp_rectype; -- range type partition key create table pp_intrangepart (a int4range) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_intrangepart12 partition of pp_intrangepart for values in ('[1,2]'); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_intrangepart2inf partition of pp_intrangepart for values in ('[2,)'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_intrangepart where a = '[1,2]'::int4range; QUERY PLAN ----------------------------------------------------- @@ -3842,8 +4062,12 @@ drop table pp_intrangepart; -- Ensure the enable_partition_prune GUC properly disables partition pruning. -- create table pp_lp (a int, value int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_lp1 partition of pp_lp for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_lp2 partition of pp_lp for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_lp where a = 1; QUERY PLAN ------------------------------------------ @@ -3898,7 +4122,8 @@ explain (costs off) update pp_lp set value = 10 where a = 1; Filter: (a = 1) -> Seq Scan on pp_lp2 pp_lp_2 Filter: (a = 1) -(8 rows) + Optimizer: Postgres query optimizer +(9 rows) explain (costs off) delete from pp_lp where a = 1; QUERY PLAN @@ -3911,7 +4136,8 @@ explain (costs off) delete from pp_lp where a = 1; Filter: (a = 1) -> Seq Scan on pp_lp2 pp_lp_2 Filter: (a = 1) -(8 rows) + Optimizer: Postgres query optimizer +(9 rows) set constraint_exclusion = 'off'; -- this should not affect the result. explain (costs off) select * from pp_lp where a = 1; @@ -3937,7 +4163,8 @@ explain (costs off) update pp_lp set value = 10 where a = 1; Filter: (a = 1) -> Seq Scan on pp_lp2 pp_lp_2 Filter: (a = 1) -(8 rows) + Optimizer: Postgres query optimizer +(9 rows) explain (costs off) delete from pp_lp where a = 1; QUERY PLAN @@ -3950,15 +4177,20 @@ explain (costs off) delete from pp_lp where a = 1; Filter: (a = 1) -> Seq Scan on pp_lp2 pp_lp_2 Filter: (a = 1) -(8 rows) + Optimizer: Postgres query optimizer +(9 rows) drop table pp_lp; -- Ensure enable_partition_prune does not affect non-partitioned tables. create table inh_lp (a int, value int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table inh_lp1 (a int, value int, check(a = 1)) inherits (inh_lp); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging column "a" with inherited definition NOTICE: merging column "value" with inherited definition create table inh_lp2 (a int, value int, check(a = 2)) inherits (inh_lp); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging column "a" with inherited definition NOTICE: merging column "value" with inherited definition set constraint_exclusion = 'partition'; @@ -3987,7 +4219,8 @@ explain (costs off) update inh_lp set value = 10 where a = 1; Filter: (a = 1) -> Seq Scan on inh_lp1 inh_lp_2 Filter: (a = 1) -(9 rows) + Optimizer: Postgres query optimizer +(10 rows) explain (costs off) delete from inh_lp where a = 1; QUERY PLAN @@ -4000,7 +4233,8 @@ explain (costs off) delete from inh_lp where a = 1; Filter: (a = 1) -> Seq Scan on inh_lp1 inh_lp_2 Filter: (a = 1) -(8 rows) + Optimizer: Postgres query optimizer +(9 rows) -- Ensure we don't exclude normal relations when we only expect to exclude -- inheritance children @@ -4021,8 +4255,12 @@ reset enable_partition_pruning; reset constraint_exclusion; -- Check pruning for a partition tree containing only temporary relations create temp table pp_temp_parent (a int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table pp_temp_part_1 partition of pp_temp_parent for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create temp table pp_temp_part_def partition of pp_temp_parent default; +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_temp_parent where true; QUERY PLAN ----------------------------------------------------------- @@ -4045,15 +4283,27 @@ explain (costs off) select * from pp_temp_parent where a = 2; drop table pp_temp_parent; -- Stress run-time partition pruning a bit more, per bug reports create temp table p (a int, b int, c int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table p1 partition of p for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create temp table p2 partition of p for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q (a int, b int, c int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table q1 partition of q for values in (1) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q11 partition of q1 for values in (1) partition by list (c); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q111 partition of q11 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q2 partition of q for values in (2) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q21 partition of q2 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q22 partition of q2 for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table insert into q22 values (2, 2, 3); -- GPDB: This is the query that needs the "matchsubs" rule at the top of the file -- The constant third branch of the UNION is executed at random segment. If the @@ -4072,7 +4322,7 @@ from ( where s.a = 1 and s.b = 1 and s.c = (select 1); QUERY PLAN ------------------------------------------------------------- - Gather Motion 1:1 (slice1; segments: 1) + Gather Motion 2:1 (slice1; segments: 2) InitPlan 1 (returns $0) (slice2) -> Result -> Append @@ -4081,7 +4331,7 @@ where s.a = 1 and s.b = 1 and s.c = (select 1); -> Seq Scan on q111 q1 Filter: ((a = 1) AND (b = 1) AND (c = $0)) -> Result - One-Time Filter: (gp_execution_segment() = 1) + One-Time Filter: (gp_execution_segment() = 2) -> Result One-Time Filter: (1 = $0) Optimizer: Postgres query optimizer @@ -4124,7 +4374,7 @@ explain (costs off) execute q (1, 1); -> Seq Scan on q111 q1 Filter: ((a = $1) AND (b = $2) AND (c = $0)) -> Result - One-Time Filter: (gp_execution_segment() = 1) + One-Time Filter: (gp_execution_segment() = 2) -> Result One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = $0)) Optimizer: Postgres query optimizer @@ -4140,9 +4390,14 @@ drop table p, q; -- Ensure run-time pruning works correctly when we match a partitioned table -- on the first level but find no matching partitions on the second level. create table listp (a int, b int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table listp1 partition of listp for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table listp2 partition of listp for values in(2) partition by list(b); +NOTICE: table has parent, setting distribution columns to match parent table create table listp2_10 partition of listp2 for values in (10); +NOTICE: table has parent, setting distribution columns to match parent table explain (analyze, costs off, summary off, timing off) select * from listp where a = (select 2) and b <> 10; QUERY PLAN @@ -4209,9 +4464,14 @@ drop table listp; set parallel_setup_cost to 0; set parallel_tuple_cost to 0; create table listp (a int) partition by list(a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table listp_12 partition of listp for values in(1,2) partition by list(a); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_12_1 partition of listp_12 for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_12_2 partition of listp_12 for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table -- Force the 2nd subnode of the Append to be non-parallel. This results in -- a nested Append node because the mixed parallel / non-parallel paths cannot -- be pulled into the top-level Append. @@ -4266,13 +4526,20 @@ reset parallel_setup_cost; -- Test case for run-time pruning with a nested Merge Append set enable_sort to 0; create table rangep (a int, b int) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rangep_0_to_100 partition of rangep for values from (0) to (100) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table -- We need 3 sub-partitions. 1 to validate pruning worked and another two -- because a single remaining partition would be pulled up to the main Append. create table rangep_0_to_100_1 partition of rangep_0_to_100 for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table rangep_0_to_100_2 partition of rangep_0_to_100 for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table create table rangep_0_to_100_3 partition of rangep_0_to_100 for values in(3); +NOTICE: table has parent, setting distribution columns to match parent table create table rangep_100_to_200 partition of rangep for values from (100) to (200); +NOTICE: table has parent, setting distribution columns to match parent table create index on rangep (a); -- Ensure run-time pruning works on the nested Merge Append explain (analyze on, costs off, timing off, summary off) @@ -4306,8 +4573,12 @@ drop table rangep; -- clauses for different partition keys -- create table rp_prefix_test1 (a int, b varchar) partition by range(a, b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rp_prefix_test1_p1 partition of rp_prefix_test1 for values from (1, 'a') to (1, 'b'); +NOTICE: table has parent, setting distribution columns to match parent table create table rp_prefix_test1_p2 partition of rp_prefix_test1 for values from (2, 'a') to (2, 'b'); +NOTICE: table has parent, setting distribution columns to match parent table -- Don't call get_steps_using_prefix() with the last partition key b plus -- an empty prefix explain (costs off) select * from rp_prefix_test1 where a <= 1 and b = 'a'; @@ -4320,8 +4591,12 @@ explain (costs off) select * from rp_prefix_test1 where a <= 1 and b = 'a'; (4 rows) create table rp_prefix_test2 (a int, b int, c int) partition by range(a, b, c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rp_prefix_test2_p1 partition of rp_prefix_test2 for values from (1, 1, 0) to (1, 1, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table rp_prefix_test2_p2 partition of rp_prefix_test2 for values from (2, 2, 0) to (2, 2, 10); +NOTICE: table has parent, setting distribution columns to match parent table -- Don't call get_steps_using_prefix() with the last partition key c plus -- an invalid prefix (ie, b = 1) explain (costs off) select * from rp_prefix_test2 where a <= 1 and b = 1 and c >= 0; @@ -4334,8 +4609,12 @@ explain (costs off) select * from rp_prefix_test2 where a <= 1 and b = 1 and c > (4 rows) create table rp_prefix_test3 (a int, b int, c int, d int) partition by range(a, b, c, d); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rp_prefix_test3_p1 partition of rp_prefix_test3 for values from (1, 1, 1, 0) to (1, 1, 1, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table rp_prefix_test3_p2 partition of rp_prefix_test3 for values from (2, 2, 2, 0) to (2, 2, 2, 10); +NOTICE: table has parent, setting distribution columns to match parent table -- Test that get_steps_using_prefix() handles a prefix that contains multiple -- clauses for the partition key b (ie, b >= 1 and b >= 2) explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b >= 2 and c >= 2 and d >= 0; @@ -4359,22 +4638,265 @@ explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b Optimizer: Postgres query optimizer (4 rows) -create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); -create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0); -create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1); --- Test that get_steps_using_prefix() handles non-NULL step_nullkeys -explain (costs off) select * from hp_prefix_test where a = 1 and b is null and c = 1 and d = 1; - QUERY PLAN -------------------------------------------------------------------- +drop table rp_prefix_test1; +drop table rp_prefix_test2; +drop table rp_prefix_test3; +-- +-- Test that get_steps_using_prefix() handles IS NULL clauses correctly +-- +create table hp_prefix_test (a int, b int, c int, d int) + partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +-- create 8 partitions +select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 8, remainder ' || x::text || ');' +from generate_Series(0,7) x; + ?column? +------------------------------------------------------------------------------------------------------ + create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0); + create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1); + create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2); + create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3); + create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4); + create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5); + create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6); + create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7); +(8 rows) + +\gexec +create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7); +NOTICE: table has parent, setting distribution columns to match parent table +-- insert 16 rows, one row for each test to perform. +insert into hp_prefix_test +select + case a when 0 then null else 1 end, + case b when 0 then null else 2 end, + case c when 0 then null else 3 end, + case d when 0 then null else 4 end +from + generate_series(0,1) a, + generate_series(0,1) b, + generate_Series(0,1) c, + generate_Series(0,1) d; +-- Ensure partition pruning works correctly for each combination of IS NULL +-- and equality quals. This may seem a little excessive, but there have been +-- a number of bugs in this area over the years. We make use of row only +-- output to reduce the size of the expected results. +--GPDB_14_MERGE_FIXME: with the force-parallel (ic-cbdb-parallel) suite these +--headerless explain outputs cannot be treated as plan blocks by the result +--comparison machinery, and the plan text differs (Parallel Seq Scan). Mask +--the whole explain section via start_ignore. +--start_ignore +\t on +select + 'explain (costs off) select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + +\gexec +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p0 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d IS NULL)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null Gather Motion 1:1 (slice1; segments: 1) -> Seq Scan on hp_prefix_test_p1 hp_prefix_test - Filter: ((b IS NULL) AND (a = 1) AND (c = 1) AND (d = 1)) + Filter: ((b IS NULL) AND (c IS NULL) AND (d IS NULL) AND (a = 1)) Optimizer: Postgres query optimizer -(4 rows) -drop table rp_prefix_test1; -drop table rp_prefix_test2; -drop table rp_prefix_test3; +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p2 hp_prefix_test + Filter: ((a IS NULL) AND (c IS NULL) AND (d IS NULL) AND (b = 2)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((c IS NULL) AND (d IS NULL) AND (a = 1) AND (b = 2)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p3 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (d IS NULL) AND (c = 3)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p7 hp_prefix_test + Filter: ((b IS NULL) AND (d IS NULL) AND (a = 1) AND (c = 3)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a IS NULL) AND (d IS NULL) AND (b = 2) AND (c = 3)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p5 hp_prefix_test + Filter: ((d IS NULL) AND (a = 1) AND (b = 2) AND (c = 3)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p6 hp_prefix_test + Filter: ((b IS NULL) AND (c IS NULL) AND (a = 1) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p5 hp_prefix_test + Filter: ((a IS NULL) AND (c IS NULL) AND (b = 2) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p6 hp_prefix_test + Filter: ((c IS NULL) AND (a = 1) AND (b = 2) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c = 3) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p5 hp_prefix_test + Filter: ((b IS NULL) AND (a = 1) AND (c = 3) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p6 hp_prefix_test + Filter: ((a IS NULL) AND (b = 2) AND (c = 3) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a = 1) AND (b = 2) AND (c = 3) AND (d = 4)) + Optimizer: Postgres query optimizer + +--end_ignore +-- And ensure we get exactly 1 row from each. Again, all 16 possible combinations. +select + 'select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + +\gexec +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + hp_prefix_test_p0 | | | | + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + hp_prefix_test_p1 | 1 | | | + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + hp_prefix_test_p2 | | 2 | | + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + hp_prefix_test_p4 | 1 | 2 | | + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + hp_prefix_test_p3 | | | 3 | + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + hp_prefix_test_p7 | 1 | | 3 | + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + hp_prefix_test_p4 | | 2 | 3 | + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + hp_prefix_test_p5 | 1 | 2 | 3 | + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + hp_prefix_test_p4 | | | | 4 + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + hp_prefix_test_p6 | 1 | | | 4 + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + hp_prefix_test_p5 | | 2 | | 4 + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + hp_prefix_test_p6 | 1 | 2 | | 4 + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + hp_prefix_test_p4 | | | 3 | 4 + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + hp_prefix_test_p5 | 1 | | 3 | 4 + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + hp_prefix_test_p6 | | 2 | 3 | 4 + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + hp_prefix_test_p4 | 1 | 2 | 3 | 4 + +\t off drop table hp_prefix_test; -- -- Check that gen_partprune_steps() detects self-contradiction from clauses @@ -4394,21 +4916,27 @@ using hash as operator 1 ===, function 2 part_hashint4_noop(int4, int8); create table hp_contradict_test (a int, b int) partition by hash (a part_test_int4_ops2, b part_test_int4_ops2); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table hp_contradict_test_p1 partition of hp_contradict_test for values with (modulus 2, remainder 0); +NOTICE: table has parent, setting distribution columns to match parent table create table hp_contradict_test_p2 partition of hp_contradict_test for values with (modulus 2, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from hp_contradict_test where a is null and a === 1 and b === 1; - QUERY PLAN --------------------------- + QUERY PLAN +------------------------------------- Result One-Time Filter: false -(2 rows) + Optimizer: Postgres query optimizer +(3 rows) explain (costs off) select * from hp_contradict_test where a === 1 and b === 1 and a is null; - QUERY PLAN --------------------------- + QUERY PLAN +------------------------------------- Result One-Time Filter: false -(2 rows) + Optimizer: Postgres query optimizer +(3 rows) drop table hp_contradict_test; drop operator class part_test_int4_ops2 using hash; diff --git a/src/test/regress/expected/partition_prune_optimizer.out b/src/test/regress/expected/partition_prune_optimizer.out index 72e87807c8a..7f87df1e30a 100644 --- a/src/test/regress/expected/partition_prune_optimizer.out +++ b/src/test/regress/expected/partition_prune_optimizer.out @@ -27,139 +27,176 @@ -- Force generic plans to be used for all prepared statements in this file. set plan_cache_mode = force_generic_plan; create table lp (a char) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table lp_default partition of lp default; +NOTICE: table has parent, setting distribution columns to match parent table create table lp_ef partition of lp for values in ('e', 'f'); +NOTICE: table has parent, setting distribution columns to match parent table create table lp_ad partition of lp for values in ('a', 'd'); +NOTICE: table has parent, setting distribution columns to match parent table create table lp_bc partition of lp for values in ('b', 'c'); +NOTICE: table has parent, setting distribution columns to match parent table create table lp_g partition of lp for values in ('g'); +NOTICE: table has parent, setting distribution columns to match parent table create table lp_null partition of lp for values in (null); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from lp; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 6 (out of 6) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (4 rows) explain (costs off) select * from lp where a > 'a' and a < 'd'; +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ----------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 2 (out of 6) Filter: ((a > 'a'::bpchar) AND (a < 'd'::bpchar)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a > 'a' and a <= 'd'; +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 3 (out of 6) Filter: ((a > 'a'::bpchar) AND (a <= 'd'::bpchar)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a = 'a'; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on lp Number of partitions to scan: 1 (out of 6) Filter: (a = 'a'::bpchar) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where 'a' = a; /* commuted */ - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on lp Number of partitions to scan: 1 (out of 6) Filter: (a = 'a'::bpchar) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a is not null; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 5 (out of 6) Filter: (NOT (a IS NULL)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a is null; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on lp Number of partitions to scan: 1 (out of 6) Filter: (a IS NULL) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a = 'a' or a = 'c'; +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ---------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on lp Number of partitions to scan: 2 (out of 6) Filter: ((a = 'a'::bpchar) OR (a = 'c'::bpchar)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a is not null and (a = 'a' or a = 'c'); +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ---------------------------------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on lp Number of partitions to scan: 2 (out of 6) Filter: ((NOT (a IS NULL)) AND ((a = 'a'::bpchar) OR (a = 'c'::bpchar))) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a <> 'g'; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 4 (out of 6) Filter: (a <> 'g'::bpchar) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a <> 'a' and a <> 'd'; +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 4 (out of 6) Filter: ((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from lp where a not in ('a', 'd'); - QUERY PLAN ------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 4 (out of 6) Filter: (a <> ALL ('{a,d}'::bpchar[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) -- collation matches the partitioning collation, pruning works create table coll_pruning (a text collate "C") partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table coll_pruning_a partition of coll_pruning for values in ('a'); +NOTICE: table has parent, setting distribution columns to match parent table create table coll_pruning_b partition of coll_pruning for values in ('b'); +NOTICE: table has parent, setting distribution columns to match parent table create table coll_pruning_def partition of coll_pruning default; +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from coll_pruning where a collate "C" = 'a' collate "C"; - QUERY PLAN ---------------------------------------------- + QUERY PLAN +----------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Seq Scan on coll_pruning_a coll_pruning Filter: (a = 'a'::text COLLATE "C") @@ -182,28 +219,50 @@ explain (costs off) select * from coll_pruning where a collate "POSIX" = 'a' col (9 rows) create table rlp (a int, b varchar) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rlp_default partition of rlp default partition by list (a); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp_default_default partition of rlp_default default; +NOTICE: table has parent, setting distribution columns to match parent table create table rlp_default_10 partition of rlp_default for values in (10); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp_default_30 partition of rlp_default for values in (30); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp_default_null partition of rlp_default for values in (null); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp1 partition of rlp for values from (minvalue) to (1); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp2 partition of rlp for values from (1) to (10); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp3 (b varchar, a int) partition by list (b varchar_ops); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- GPDB: distribution policy must match the parent table. alter table rlp3 set distributed by (a); create table rlp3_default partition of rlp3 default; +NOTICE: table has parent, setting distribution columns to match parent table create table rlp3abcd partition of rlp3 for values in ('ab', 'cd'); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp3efgh partition of rlp3 for values in ('ef', 'gh'); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp3nullxy partition of rlp3 for values in (null, 'xy'); +NOTICE: table has parent, setting distribution columns to match parent table alter table rlp attach partition rlp3 for values from (15) to (20); create table rlp4 partition of rlp for values from (20) to (30) partition by range (a); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp4_default partition of rlp4 default; +NOTICE: table has parent, setting distribution columns to match parent table create table rlp4_1 partition of rlp4 for values from (20) to (25); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp4_2 partition of rlp4 for values from (25) to (29); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp5 partition of rlp for values from (31) to (maxvalue) partition by range (a); +NOTICE: table has parent, setting distribution columns to match parent table create table rlp5_default partition of rlp5 default; +NOTICE: table has parent, setting distribution columns to match parent table create table rlp5_1 partition of rlp5 for values from (31) to (40); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from rlp where a < 1; QUERY PLAN ------------------------------------------ @@ -679,11 +738,11 @@ explain (costs off) select * from rlp where a = 20 or a = 40; (7 rows) explain (costs off) select * from rlp3 where a = 20; /* empty */ - QUERY PLAN ---------------------------------------- + QUERY PLAN +-------------------------- Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) -- redundant clauses are eliminated @@ -754,15 +813,26 @@ explain (costs off) select * from rlp where (a = 1 and a = 3) or (a > 1 and a = -- multi-column keys create table mc3p (a int, b int, c int) partition by range (a, abs(b), c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mc3p_default partition of mc3p default; +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p0 partition of mc3p for values from (minvalue, minvalue, minvalue) to (1, 1, 1); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p1 partition of mc3p for values from (1, 1, 1) to (10, 5, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p2 partition of mc3p for values from (10, 5, 10) to (10, 10, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p3 partition of mc3p for values from (10, 10, 10) to (10, 10, 20); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p4 partition of mc3p for values from (10, 10, 20) to (10, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p5 partition of mc3p for values from (11, 1, 1) to (20, 10, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p6 partition of mc3p for values from (20, 10, 10) to (20, 20, 20); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p7 partition of mc3p for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mc3p where a = 1; QUERY PLAN --------------------------------------------- @@ -1065,13 +1135,22 @@ explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 a -- a simpler multi-column keys case create table mc2p (a int, b int) partition by range (a, b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mc2p_default partition of mc2p default; +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p0 partition of mc2p for values from (minvalue, minvalue) to (1, minvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p1 partition of mc2p for values from (1, minvalue) to (1, 1); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p2 partition of mc2p for values from (1, 1) to (2, minvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p3 partition of mc2p for values from (2, minvalue) to (2, 1); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p4 partition of mc2p for values from (2, 1) to (2, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc2p5 partition of mc2p for values from (2, maxvalue) to (maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from mc2p where a < 2; QUERY PLAN --------------------------------------------- @@ -1172,87 +1251,108 @@ explain (costs off) select * from mc2p where b is null; -- boolean partitioning create table boolpart (a bool) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table boolpart_default partition of boolpart default; +NOTICE: table has parent, setting distribution columns to match parent table create table boolpart_t partition of boolpart for values in ('true'); +NOTICE: table has parent, setting distribution columns to match parent table create table boolpart_f partition of boolpart for values in ('false'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from boolpart where a in (true, false); - QUERY PLAN ------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: boolpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on boolpart Number of partitions to scan: 2 (out of 3) Filter: (a = ANY ('{t,f}'::boolean[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from boolpart where a = false; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: boolpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on boolpart Number of partitions to scan: 1 (out of 3) Filter: (NOT a) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from boolpart where not a = false; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: boolpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on boolpart Number of partitions to scan: 1 (out of 3) Filter: a - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from boolpart where a is true or a is not true; - QUERY PLAN --------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: boolpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on boolpart Number of partitions to scan: 3 (out of 3) Filter: ((a IS TRUE) OR (a IS NOT TRUE)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from boolpart where a is not true; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: boolpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on boolpart Number of partitions to scan: 2 (out of 3) Filter: (a IS NOT TRUE) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from boolpart where a is not true and a is not false; - QUERY PLAN ---------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: boolpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +-------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on boolpart Number of partitions to scan: 1 (out of 3) Filter: ((a IS NOT TRUE) AND (a IS NOT FALSE)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from boolpart where a is unknown; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: boolpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on boolpart Number of partitions to scan: 1 (out of 3) Filter: (a IS UNKNOWN) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from boolpart where a is not unknown; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: boolpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on boolpart Number of partitions to scan: 2 (out of 3) Filter: (a IS NOT UNKNOWN) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) create table boolrangep (a bool, b bool, c int) partition by range (a,b,c); @@ -1278,135 +1378,171 @@ explain (costs off) select * from boolrangep where not a and not b and c = 25; -- test scalar-to-array operators create table coercepart (a varchar) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table coercepart_ab partition of coercepart for values in ('ab'); +NOTICE: table has parent, setting distribution columns to match parent table create table coercepart_bc partition of coercepart for values in ('bc'); +NOTICE: table has parent, setting distribution columns to match parent table create table coercepart_cd partition of coercepart for values in ('cd'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from coercepart where a in ('ab', to_char(125, '999')); - QUERY PLAN +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN ----------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 1 (out of 3) Filter: ((a)::text = ANY ('{ab," 125"}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a ~ any ('{ab}'); - QUERY PLAN +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN ---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 3 (out of 3) Filter: ((a)::text ~ ANY ('{ab}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a !~ all ('{ab}'); - QUERY PLAN +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN ----------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 3 (out of 3) Filter: ((a)::text !~ ALL ('{ab}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a ~ any ('{ab,bc}'); - QUERY PLAN +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN ------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 3 (out of 3) Filter: ((a)::text ~ ANY ('{ab,bc}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a !~ all ('{ab,bc}'); - QUERY PLAN +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN -------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 3 (out of 3) Filter: ((a)::text !~ ALL ('{ab,bc}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a = any ('{ab,bc}'); +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 2 (out of 3) Filter: ((a)::text = ANY ('{ab,bc}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a = any ('{ab,null}'); +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN --------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 1 (out of 3) Filter: ((a)::text = ANY ('{ab,NULL}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a = any (null::text[]); - QUERY PLAN --------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 3 (out of 3) Filter: ((a)::text = ANY (NULL::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a = all ('{ab}'); +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 1 (out of 3) Filter: ((a)::text = ALL ('{ab}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a = all ('{ab,bc}'); +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 2 (out of 3) Filter: ((a)::text = ALL ('{ab,bc}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a = all ('{ab,null}'); +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN --------------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 1 (out of 3) Filter: ((a)::text = ALL ('{ab,NULL}'::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from coercepart where a = all (null::text[]); - QUERY PLAN --------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: coercepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on coercepart Number of partitions to scan: 3 (out of 3) Filter: ((a)::text = ALL (NULL::text[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) drop table coercepart; CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a); +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT; +NOTICE: table has parent, setting distribution columns to match parent table CREATE TABLE part_rev (b INT, c INT, a INT); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- The distribution key must be the same for partition tables. ALTER TABLE part_rev SET DISTRIBUTED BY(a); ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- fail @@ -1551,57 +1687,70 @@ explain (costs off) select * from mc2p t1, lateral (select count(*) from mc3p t2 -- -- doesn't prune range partitions create table rp (a int) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rp0 partition of rp for values from (minvalue) to (1); +NOTICE: table has parent, setting distribution columns to match parent table create table rp1 partition of rp for values from (1) to (2); +NOTICE: table has parent, setting distribution columns to match parent table create table rp2 partition of rp for values from (2) to (maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from rp where a <> 1; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: rp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on rp Number of partitions to scan: 3 (out of 3) Filter: (a <> 1) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from rp where a <> 1 and a <> 2; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: rp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on rp Number of partitions to scan: 3 (out of 3) Filter: ((a <> 1) AND (a <> 2)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) -- null partition should be eliminated due to strict <> clause. explain (costs off) select * from lp where a <> 'a'; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 5 (out of 6) Filter: (a <> 'a'::bpchar) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) -- ensure we detect contradictions in clauses; a can't be NULL and NOT NULL. explain (costs off) select * from lp where a <> 'a' and a is null; - QUERY PLAN ---------------------------------------- + QUERY PLAN +-------------------------- Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) explain (costs off) select * from lp where (a <> 'a' and a <> 'd') or a is null; - QUERY PLAN +NOTICE: One or more columns in the following table(s) do not have statistics: lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN ------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on lp Number of partitions to scan: 5 (out of 6) Filter: (((a <> 'a'::bpchar) AND (a <> 'd'::bpchar)) OR (a IS NULL)) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) -- check that it also works for a partitioned table that's not root, @@ -1623,9 +1772,14 @@ explain (costs off) select * from rlp where a = 15 and b <> 'ab' and b <> 'cd' a -- different collations for different keys with same expression -- create table coll_pruning_multi (a text) partition by range (substr(a, 1) collate "POSIX", substr(a, 1) collate "C"); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table coll_pruning_multi1 partition of coll_pruning_multi for values from ('a', 'a') to ('a', 'e'); +NOTICE: table has parent, setting distribution columns to match parent table create table coll_pruning_multi2 partition of coll_pruning_multi for values from ('a', 'e') to ('a', 'z'); +NOTICE: table has parent, setting distribution columns to match parent table create table coll_pruning_multi3 partition of coll_pruning_multi for values from ('b', 'a') to ('b', 'e'); +NOTICE: table has parent, setting distribution columns to match parent table -- no pruning, because no value for the leading key explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' collate "C"; QUERY PLAN @@ -1668,54 +1822,71 @@ explain (costs off) select * from coll_pruning_multi where substr(a, 1) = 'e' co -- LIKE operators don't prune -- create table like_op_noprune (a text) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table like_op_noprune1 partition of like_op_noprune for values in ('ABC'); +NOTICE: table has parent, setting distribution columns to match parent table create table like_op_noprune2 partition of like_op_noprune for values in ('BCD'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from like_op_noprune where a like '%BC'; - QUERY PLAN -------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: like_op_noprune +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on like_op_noprune Number of partitions to scan: 2 (out of 2) Filter: (a ~~ '%BC'::text) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) -- -- tests wherein clause value requires a cross-type comparison function -- create table lparted_by_int2 (a smallint) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table lparted_by_int2_1 partition of lparted_by_int2 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table lparted_by_int2_16384 partition of lparted_by_int2 for values in (16384); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from lparted_by_int2 where a = 100000000000000; - QUERY PLAN ---------------------------------------- + QUERY PLAN +-------------------------- Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) create table rparted_by_int2 (a smallint) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rparted_by_int2_1 partition of rparted_by_int2 for values from (1) to (10); +NOTICE: table has parent, setting distribution columns to match parent table create table rparted_by_int2_16384 partition of rparted_by_int2 for values from (10) to (16384); +NOTICE: table has parent, setting distribution columns to match parent table -- all partitions pruned explain (costs off) select * from rparted_by_int2 where a > 100000000000000; - QUERY PLAN -------------------------------------- + QUERY PLAN +-------------------------- Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values from (16384) to (maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table -- all partitions but rparted_by_int2_maxvalue pruned explain (costs off) select * from rparted_by_int2 where a > 100000000000000; - QUERY PLAN -------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: rparted_by_int2 +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on rparted_by_int2 Number of partitions to scan: 1 (out of 3) Filter: (a > '100000000000000'::bigint) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; @@ -1728,10 +1899,16 @@ drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pru -- create table hp (a int, b text, c int) partition by hash (a part_test_int4_ops, b part_test_text_ops); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table hp0 partition of hp for values with (modulus 4, remainder 0); +NOTICE: table has parent, setting distribution columns to match parent table create table hp3 partition of hp for values with (modulus 4, remainder 3); +NOTICE: table has parent, setting distribution columns to match parent table create table hp1 partition of hp for values with (modulus 4, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table create table hp2 partition of hp for values with (modulus 4, remainder 2); +NOTICE: table has parent, setting distribution columns to match parent table insert into hp values (null, null, 0); insert into hp values (1, null, 1); insert into hp values (1, 'xxx', 2); @@ -1957,29 +2134,43 @@ explain (costs off) select * from hp where a = 1 and b = 'abcde' and drop table hp2; explain (costs off) select * from hp where a = 1 and b = 'abcde' and (c = 2 or c = 3); - QUERY PLAN --------------------------- + QUERY PLAN +------------------------------------- Result One-Time Filter: false -(2 rows) + Optimizer: Postgres query optimizer +(3 rows) -drop table hp; -- -- Test runtime partition pruning -- create table ab (a int not null, b int not null) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table ab_a2 partition of ab for values in(2) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a2_b1 partition of ab_a2 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a2_b2 partition of ab_a2 for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a2_b3 partition of ab_a2 for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a1 partition of ab for values in(1) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a1_b1 partition of ab_a1 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a1_b2 partition of ab_a1 for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a1_b3 partition of ab_a1 for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a3 partition of ab for values in(3) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a3_b1 partition of ab_a3 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a3_b2 partition of ab_a3 for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table ab_a3_b3 partition of ab_a3 for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table -- Disallow index only scans as concurrent transactions may stop visibility -- bits being set causing "Heap Fetches" to be unstable in the EXPLAIN ANALYZE -- output. @@ -2097,12 +2288,44 @@ explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); Optimizer: Postgres query optimizer (12 rows) +-- +-- Test runtime pruning with hash partitioned tables +-- +-- recreate partitions dropped above +create table hp1 partition of hp for values with (modulus 4, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp2 partition of hp for values with (modulus 4, remainder 2); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp3 partition of hp for values with (modulus 4, remainder 3); +NOTICE: table has parent, setting distribution columns to match parent table +-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual +prepare hp_q1 (text) as +select * from hp where a is null and b = $1; +explain (costs off) execute hp_q1('xxx'); + QUERY PLAN +-------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + -> Append + Subplans Removed: 3 + -> Seq Scan on hp2 hp_1 + Filter: ((a IS NULL) AND (b = $1)) + Optimizer: Postgres query optimizer +(6 rows) + +deallocate hp_q1; +drop table hp; -- Test a backwards Append scan create table list_part (a int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table list_part1 partition of list_part for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create table list_part2 partition of list_part for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create table list_part3 partition of list_part for values in (3); +NOTICE: table has parent, setting distribution columns to match parent table create table list_part4 partition of list_part for values in (4); +NOTICE: table has parent, setting distribution columns to match parent table insert into list_part select generate_series(1,4); begin; -- Don't select an actual value out of the table as the order of the Append's @@ -2119,6 +2342,8 @@ begin; create function list_part_fn(int) returns int as $$ begin return $1; end;$$ language plpgsql stable; -- Ensure pruning works using a stable function containing no Vars explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1); +NOTICE: One or more columns in the following table(s) do not have statistics: list_part +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------ Gather Motion 1:1 (slice1; segments: 1) (actual rows=1 loops=1) @@ -2126,11 +2351,13 @@ explain (analyze, costs off, summary off, timing off) select * from list_part wh Number of partitions to scan: 1 (out of 4) Filter: (a = 1) Partitions scanned: 1 . - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) -- Ensure pruning does not take place when the function has a Var parameter explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(a); +NOTICE: One or more columns in the following table(s) do not have statistics: list_part +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=4 loops=1) @@ -2138,11 +2365,13 @@ explain (analyze, costs off, summary off, timing off) select * from list_part wh Number of partitions to scan: 4 (out of 4) Filter: (a = list_part_fn(a)) Partitions scanned: Avg 4.0 x 3 workers. Max 4 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) -- Ensure pruning does not take place when the expression contains a Var. explain (analyze, costs off, summary off, timing off) select * from list_part where a = list_part_fn(1) + a; +NOTICE: One or more columns in the following table(s) do not have statistics: list_part +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -2150,7 +2379,7 @@ explain (analyze, costs off, summary off, timing off) select * from list_part wh Number of partitions to scan: 4 (out of 4) Filter: (a = (1 + a)) Partitions scanned: Avg 4.0 x 3 workers. Max 4 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) rollback; @@ -2285,6 +2514,8 @@ select explain_parallel_append('select count(*) from ab where (a = (select 1) or -- Test pruning during parallel nested loop query create table lprt_a (a int not null); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- Insert some values we won't find in ab insert into lprt_a select 0 from generate_series(1,100); -- and insert some values that we should find. @@ -2329,7 +2560,7 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on Index Cond: (a = ANY ('{0,0,1}'::integer[])) -> Sort (actual rows=N loops=N) Sort Key: a.a - Sort Method: quicksort Memory: 60kB + Sort Method: quicksort Memory: 29kB -> Partition Selector (selector id: $0) (actual rows=N loops=N) -> Seq Scan on lprt_a a (actual rows=N loops=N) Filter: (a = ANY ('{0,0,1}'::integer[])) @@ -2411,7 +2642,7 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on Index Cond: (a = ANY ('{1,0,3}'::integer[])) -> Sort (actual rows=N loops=N) Sort Key: a.a - Sort Method: quicksort Memory: 110kB + Sort Method: quicksort Memory: 54kB -> Partition Selector (selector id: $0) (actual rows=N loops=N) -> Seq Scan on lprt_a a (actual rows=N loops=N) Filter: (a = ANY ('{1,0,3}'::integer[])) @@ -2445,7 +2676,7 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on Index Cond: (a = ANY ('{1,0,0}'::integer[])) -> Sort (actual rows=N loops=N) Sort Key: a.a - Sort Method: quicksort Memory: 60kB + Sort Method: quicksort Memory: 29kB -> Partition Selector (selector id: $0) (actual rows=N loops=N) -> Seq Scan on lprt_a a (actual rows=N loops=N) Filter: (a = ANY ('{1,0,0}'::integer[])) @@ -2480,7 +2711,7 @@ select explain_parallel_append('select avg(ab.a) from ab inner join lprt_a a on Index Cond: (a = ANY ('{1,0,0}'::integer[])) -> Sort (actual rows=N loops=N) Sort Key: a.a - Sort Method: quicksort Memory: 60kB + Sort Method: quicksort Memory: 29kB -> Partition Selector (selector id: $0) (actual rows=N loops=N) -> Seq Scan on lprt_a a (actual rows=N loops=N) Filter: (a = ANY ('{1,0,0}'::integer[])) @@ -2562,8 +2793,8 @@ select * from ab where a = (select max(a) from lprt_a) and b = (select max(a)-1 -- Test run-time partition pruning with UNION ALL parents explain (analyze, costs off, summary off, timing off) select * from (select * from ab where a = 1 union all select * from ab) ab where b = (select 1); - QUERY PLAN ------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) InitPlan 1 (returns $0) (slice2) -> Result (actual rows=1 loops=1) @@ -2608,8 +2839,8 @@ select * from (select * from ab where a = 1 union all select * from ab) ab where -- A case containing a UNION ALL with a non-partitioned child. explain (analyze, costs off, summary off, timing off) select * from (select * from ab where a = 1 union all (values(10,5)) union all select * from ab) ab where b = (select 1); - QUERY PLAN ------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) InitPlan 1 (returns $0) (slice2) -> Result (actual rows=1 loops=1) @@ -2657,6 +2888,8 @@ select * from (select * from ab where a = 1 union all (values(10,5)) union all s -- Another UNION ALL test, but containing a mix of exec init and exec run-time pruning. create table xy_1 (x int, y int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'x' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. insert into xy_1 values(100,-10); set enable_bitmapscan = 0; set enable_indexscan = 0; @@ -2789,25 +3022,35 @@ update ab_a1 set b = 3 from ab_a2 where ab_a2.b = (select 1); select tableoid::regclass, * from ab; tableoid | a | b ----------+---+--- + ab_a2_b1 | 2 | 1 ab_a1_b3 | 1 | 3 ab_a1_b3 | 1 | 3 ab_a1_b3 | 1 | 3 - ab_a2_b1 | 2 | 1 (4 rows) drop table ab, lprt_a; -- Join create table tbl1(col1 int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'col1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. insert into tbl1 values (501), (505); analyze tbl1; -- Basic table create table tprt (col1 int) partition by range (col1); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'col1' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table tprt_1 partition of tprt for values from (1) to (501); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_2 partition of tprt for values from (501) to (1001); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_3 partition of tprt for values from (1001) to (2001); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_4 partition of tprt for values from (2001) to (3001); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_5 partition of tprt for values from (3001) to (4001); +NOTICE: table has parent, setting distribution columns to match parent table create table tprt_6 partition of tprt for values from (4001) to (5001); +NOTICE: table has parent, setting distribution columns to match parent table create index tprt1_idx on tprt_1 (col1); create index tprt2_idx on tprt_2 (col1); create index tprt3_idx on tprt_3 (col1); @@ -2820,6 +3063,8 @@ set enable_mergejoin = off; set enable_seqscan=off; explain (analyze, costs off, summary off, timing off) select * from tbl1 join tprt on tbl1.col1 > tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=6 loops=1) @@ -2831,13 +3076,15 @@ select * from tbl1 join tprt on tbl1.col1 > tprt.col1; Number of partitions to scan: 6 (out of 6) Partitions scanned: Avg 6.0 x 3 workers. Max 6 parts (seg0). -> Seq Scan on tbl1 (actual rows=1 loops=8) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (10 rows) explain (analyze, costs off, summary off, timing off) select * from tbl1 join tprt on tbl1.col1 = tprt.col1; - QUERY PLAN ------------------------------------------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=2 loops=1) -> Hash Join (actual rows=1 loops=1) Hash Cond: (tbl1.col1 = tprt.col1) @@ -2848,12 +3095,14 @@ select * from tbl1 join tprt on tbl1.col1 = tprt.col1; -> Dynamic Seq Scan on tprt (actual rows=3 loops=1) Number of partitions to scan: 6 (out of 6) Partitions scanned: Avg 6.0 x 3 workers. Max 6 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) -(10 rows) + Optimizer: GPORCA +(11 rows) select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 > tprt.col1 order by tbl1.col1, tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. col1 | col1 ------+------ 501 | 10 @@ -2867,6 +3116,8 @@ order by tbl1.col1, tprt.col1; select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 = tprt.col1 order by tbl1.col1, tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. col1 | col1 ------+------ 501 | 501 @@ -2877,6 +3128,8 @@ order by tbl1.col1, tprt.col1; insert into tbl1 values (1001), (1010), (1011); explain (analyze, costs off, summary off, timing off) select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=23 loops=1) @@ -2888,13 +3141,15 @@ select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1; Number of partitions to scan: 6 (out of 6) Partitions scanned: Avg 6.0 x 3 workers. Max 6 parts (seg0). -> Seq Scan on tbl1 (actual rows=3 loops=8) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (10 rows) explain (analyze, costs off, summary off, timing off) select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1; - QUERY PLAN ------------------------------------------------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=3 loops=1) -> Hash Join (actual rows=2 loops=1) Hash Cond: (tbl1.col1 = tprt.col1) @@ -2905,12 +3160,14 @@ select * from tbl1 inner join tprt on tbl1.col1 = tprt.col1; -> Dynamic Seq Scan on tprt (actual rows=3 loops=1) Number of partitions to scan: 6 (out of 6) Partitions scanned: Avg 6.0 x 3 workers. Max 6 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) -(10 rows) + Optimizer: GPORCA +(11 rows) select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 > tprt.col1 order by tbl1.col1, tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. col1 | col1 ------+------ 501 | 10 @@ -2941,6 +3198,8 @@ order by tbl1.col1, tprt.col1; select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 = tprt.col1 order by tbl1.col1, tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. col1 | col1 ------+------ 501 | 501 @@ -2953,6 +3212,8 @@ delete from tbl1; insert into tbl1 values (4400); explain (analyze, costs off, summary off, timing off) select * from tbl1 join tprt on tbl1.col1 < tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=1 loops=1) @@ -2964,12 +3225,14 @@ select * from tbl1 join tprt on tbl1.col1 < tprt.col1; Number of partitions to scan: 6 (out of 6) Partitions scanned: Avg 6.0 x 3 workers. Max 6 parts (seg0). -> Seq Scan on tbl1 (actual rows=1 loops=8) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (10 rows) select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 < tprt.col1 order by tbl1.col1, tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. col1 | col1 ------+------ 4400 | 4500 @@ -2981,6 +3244,8 @@ delete from tbl1; insert into tbl1 values (10000); explain (analyze, costs off, summary off, timing off) select * from tbl1 join tprt on tbl1.col1 = tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -2992,12 +3257,14 @@ select * from tbl1 join tprt on tbl1.col1 = tprt.col1; -> Dynamic Seq Scan on tprt (actual rows=3 loops=1) Number of partitions to scan: 6 (out of 6) Partitions scanned: Avg 6.0 x 3 workers. Max 6 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (10 rows) select tbl1.col1, tprt.col1 from tbl1 inner join tprt on tbl1.col1 = tprt.col1 order by tbl1.col1, tprt.col1; +NOTICE: One or more columns in the following table(s) do not have statistics: tprt +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. col1 | col1 ------+------ (0 rows) @@ -3005,9 +3272,17 @@ order by tbl1.col1, tprt.col1; drop table tbl1, tprt; -- Test with columns defined in varying orders between each level create table part_abc (a int not null, b int not null, c int not null) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_bac (b int not null, a int not null, c int not null) partition by list (b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'b' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_cab (c int not null, a int not null, b int not null) partition by list (c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'c' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table part_abc_p1 (a int not null, b int not null, c int not null); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. -- GPDB: the distribution keys must be the same in all parts of partition -- hierarchy. alter table part_bac set distributed by (a); @@ -3032,10 +3307,16 @@ drop table part_abc; -- Ensure that an Append node properly handles a sub-partitioned table -- matching without any of its leaf partitions matching the clause. create table listp (a int, b int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table listp_1 partition of listp for values in(1) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_1_1 partition of listp_1 for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_2 partition of listp for values in(2) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_2_1 partition of listp_2 for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table select * from listp where b = 1; a | b ---+--- @@ -3046,8 +3327,8 @@ select * from listp where b = 1; -- which match the given parameter. prepare q1 (int,int) as select * from listp where b in ($1,$2); explain (analyze, costs off, summary off, timing off) execute q1 (1,1); - QUERY PLAN ------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) -> Append (actual rows=0 loops=1) Subplans Removed: 1 @@ -3057,8 +3338,8 @@ explain (analyze, costs off, summary off, timing off) execute q1 (1,1); (6 rows) explain (analyze, costs off, summary off, timing off) execute q1 (2,2); - QUERY PLAN ------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) -> Append (actual rows=0 loops=1) Subplans Removed: 1 @@ -3127,26 +3408,33 @@ drop table listp; create table stable_qual_pruning (a timestamp) distributed randomly partition by range (a); create table stable_qual_pruning1 partition of stable_qual_pruning for values from ('2000-01-01') to ('2000-02-01'); +NOTICE: table has parent, setting distribution columns to match parent table create table stable_qual_pruning2 partition of stable_qual_pruning for values from ('2000-02-01') to ('2000-03-01'); +NOTICE: table has parent, setting distribution columns to match parent table create table stable_qual_pruning3 partition of stable_qual_pruning for values from ('3000-02-01') to ('3000-03-01'); +NOTICE: table has parent, setting distribution columns to match parent table -- comparison against a stable value requires run-time pruning explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a < localtimestamp; +NOTICE: One or more columns in the following table(s) do not have statistics: stable_qual_pruning +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN -------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) -> Dynamic Seq Scan on stable_qual_pruning (actual rows=0 loops=1) Number of partitions to scan: 2 (out of 3) - Filter: (a < 'Thu Dec 19 19:04:45.779097 2024'::timestamp without time zone) + Filter: (a < 'Tue Sep 22 04:45:44.293727 2026'::timestamp without time zone) Partitions scanned: Avg 2.0 x 3 workers. Max 2 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) -- timestamp < timestamptz comparison is only stable, not immutable explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a < '2000-02-01'::timestamptz; +NOTICE: One or more columns in the following table(s) do not have statistics: stable_qual_pruning +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN -------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -3154,23 +3442,25 @@ select * from stable_qual_pruning where a < '2000-02-01'::timestamptz; Number of partitions to scan: 1 (out of 3) Filter: (a < 'Tue Feb 01 00:00:00 2000 PST'::timestamp with time zone) Partitions scanned: Avg 1.0 x 3 workers. Max 1 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) -- check ScalarArrayOp cases explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a = any(array['2010-02-01', '2020-01-01']::timestamp[]); - QUERY PLAN ---------------------------------------- + QUERY PLAN +-------------------------------- Result (actual rows=0 loops=1) One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a = any(array['2000-02-01', '2010-01-01']::timestamp[]); +NOTICE: One or more columns in the following table(s) do not have statistics: stable_qual_pruning +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ---------------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -3178,35 +3468,39 @@ select * from stable_qual_pruning Number of partitions to scan: 1 (out of 3) Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000","Fri Jan 01 00:00:00 2010"}'::timestamp without time zone[])) Partitions scanned: Avg 1.0 x 3 workers. Max 1 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a = any(array['2000-02-01', localtimestamp]::timestamp[]); +NOTICE: One or more columns in the following table(s) do not have statistics: stable_qual_pruning +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) -> Dynamic Seq Scan on stable_qual_pruning (actual rows=0 loops=1) Number of partitions to scan: 1 (out of 3) - Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000","Thu Dec 19 19:04:45.852371 2024"}'::timestamp without time zone[])) + Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000","Tue Sep 22 04:45:44.303755 2026"}'::timestamp without time zone[])) Partitions scanned: Avg 1.0 x 3 workers. Max 1 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a = any(array['2010-02-01', '2020-01-01']::timestamptz[]); - QUERY PLAN ---------------------------------------- + QUERY PLAN +-------------------------------- Result (actual rows=0 loops=1) One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a = any(array['2000-02-01', '2010-01-01']::timestamptz[]); +NOTICE: One or more columns in the following table(s) do not have statistics: stable_qual_pruning +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN --------------------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -3214,12 +3508,14 @@ select * from stable_qual_pruning Number of partitions to scan: 1 (out of 3) Filter: (a = ANY ('{"Tue Feb 01 00:00:00 2000 PST","Fri Jan 01 00:00:00 2010 PST"}'::timestamp with time zone[])) Partitions scanned: Avg 1.0 x 3 workers. Max 1 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) explain (analyze, costs off, summary off, timing off) select * from stable_qual_pruning where a = any(null::timestamptz[]); +NOTICE: One or more columns in the following table(s) do not have statistics: stable_qual_pruning +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -3227,7 +3523,7 @@ select * from stable_qual_pruning Number of partitions to scan: 3 (out of 3) Filter: (a = ANY (NULL::timestamp with time zone[])) Partitions scanned: Avg 3.0 x 3 workers. Max 3 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) drop table stable_qual_pruning; @@ -3237,12 +3533,17 @@ drop table stable_qual_pruning; -- non-inclusive operator for an earlier key -- create table mc3p (a int, b int, c int) partition by range (a, abs(b), c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table mc3p0 partition of mc3p for values from (0, 0, 0) to (0, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p1 partition of mc3p for values from (1, 1, 1) to (2, minvalue, minvalue); +NOTICE: table has parent, setting distribution columns to match parent table create table mc3p2 partition of mc3p for values from (2, minvalue, minvalue) to (3, maxvalue, maxvalue); +NOTICE: table has parent, setting distribution columns to match parent table insert into mc3p values (0, 1, 1), (1, 1, 1), (2, 1, 1); explain (analyze, costs off, summary off, timing off) select * from mc3p where a < 3 and abs(b) = 1; @@ -3303,12 +3604,20 @@ deallocate ps2; drop table mc3p; -- Ensure runtime pruning works with initplans params with boolean types create table boolvalues (value bool not null); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'value' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. insert into boolvalues values('t'),('f'); create table boolp (a bool) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table boolp_t partition of boolp for values in('t'); +NOTICE: table has parent, setting distribution columns to match parent table create table boolp_f partition of boolp for values in('f'); +NOTICE: table has parent, setting distribution columns to match parent table explain (analyze, costs off, summary off, timing off) select * from boolp where a = (select value from boolvalues where value); +NOTICE: One or more columns in the following table(s) do not have statistics: boolp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -3328,11 +3637,13 @@ select * from boolp where a = (select value from boolvalues where value); -> Dynamic Seq Scan on boolp (actual rows=0 loops=1) Number of partitions to scan: 2 (out of 2) Partitions scanned: Avg 2.0 x 3 workers. Max 2 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (18 rows) explain (analyze, costs off, summary off, timing off) select * from boolp where a = (select value from boolvalues where not value); +NOTICE: One or more columns in the following table(s) do not have statistics: boolp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ------------------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -3352,7 +3663,7 @@ select * from boolp where a = (select value from boolvalues where not value); -> Dynamic Seq Scan on boolp (actual rows=0 loops=1) Number of partitions to scan: 2 (out of 2) Partitions scanned: Avg 2.0 x 3 workers. Max 2 parts (seg0). - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (18 rows) drop table boolp; @@ -3362,9 +3673,14 @@ drop table boolp; set enable_seqscan = off; set enable_sort = off; create table ma_test (a int, b int) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table ma_test_p1 partition of ma_test for values from (0) to (10); +NOTICE: table has parent, setting distribution columns to match parent table create table ma_test_p2 partition of ma_test for values from (10) to (20); +NOTICE: table has parent, setting distribution columns to match parent table create table ma_test_p3 partition of ma_test for values from (20) to (30); +NOTICE: table has parent, setting distribution columns to match parent table insert into ma_test select x,x from generate_series(0,29) t(x); create index on ma_test (b); analyze ma_test; @@ -3383,7 +3699,7 @@ explain (analyze, costs off, summary off, timing off) execute mt_q1(15); Filter: ((a >= $1) AND ((a % 10) = 5)) Partitions scanned: Avg 3.0 x 3 workers. Max 3 parts (seg0). Optimizer: GPORCA -(12 rows) +(11 rows) execute mt_q1(15); a @@ -3406,7 +3722,7 @@ explain (analyze, costs off, summary off, timing off) execute mt_q1(25); Filter: ((a >= $1) AND ((a % 10) = 5)) Partitions scanned: Avg 3.0 x 3 workers. Max 3 parts (seg0). Optimizer: GPORCA -(12 rows) +(11 rows) execute mt_q1(25); a @@ -3429,7 +3745,7 @@ explain (analyze, costs off, summary off, timing off) execute mt_q1(35); Filter: ((a >= $1) AND ((a % 10) = 5)) Partitions scanned: Avg 3.0 x 3 workers. Max 3 parts (seg0). Optimizer: GPORCA -(12 rows) +(11 rows) execute mt_q1(35); a @@ -3440,8 +3756,8 @@ deallocate mt_q1; prepare mt_q2 (int) as select * from ma_test where a >= $1 order by b limit 1; -- Ensure output list looks sane when the MergeAppend has no subplans. explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35); - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Limit (actual rows=0 loops=1) Output: a, b -> Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) @@ -3459,15 +3775,15 @@ explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35 Number of partitions to scan: 3 (out of 3) Filter: (ma_test.a >= $1) Partitions scanned: Avg 3.0 x 3 workers. Max 3 parts (seg0). - Settings: enable_hashjoin = 'off', enable_indexonlyscan = 'off', enable_mergejoin = 'off', enable_parallel = 'off', enable_seqscan = 'off', enable_sort = 'off', optimizer = 'on', plan_cache_mode = 'force_generic_plan' + Settings: enable_hashjoin = 'off', enable_indexonlyscan = 'off', enable_mergejoin = 'off', enable_seqscan = 'off', enable_sort = 'off', optimizer = 'on', plan_cache_mode = 'force_generic_plan' Optimizer: GPORCA (19 rows) deallocate mt_q2; -- ensure initplan params properly prune partitions explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b; - QUERY PLAN ------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=20 loops=1) Merge Key: ma_test.b -> Sort (actual rows=8 loops=1) @@ -3488,7 +3804,7 @@ explain (analyze, costs off, summary off, timing off) select * from ma_test wher Number of partitions to scan: 3 (out of 3) Partitions scanned: Avg 2.0 x 3 workers of 2 scans. Max 2 parts (seg0). Optimizer: GPORCA -(17 rows) +(20 rows) reset enable_seqscan; reset enable_sort; @@ -3500,34 +3816,42 @@ reset enable_indexonlyscan; -- -- array type list partition key create table pp_arrpart (a int[]) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_arrpart1 partition of pp_arrpart for values in ('{1}'); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_arrpart2 partition of pp_arrpart for values in ('{2, 3}', '{4, 5}'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_arrpart where a = '{1}'; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_arrpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on pp_arrpart Number of partitions to scan: 1 (out of 2) Filter: (a = '{1}'::integer[]) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from pp_arrpart where a = '{1, 2}'; - QUERY PLAN -------------------------------------- + QUERY PLAN +-------------------------- Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) explain (costs off) select * from pp_arrpart where a in ('{4, 5}', '{1}'); +NOTICE: One or more columns in the following table(s) do not have statistics: pp_arrpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. QUERY PLAN ---------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on pp_arrpart Number of partitions to scan: 2 (out of 2) Filter: ((a = '{4,5}'::integer[]) OR (a = '{1}'::integer[])) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) update pp_arrpart set a = a where a = '{1}'; @@ -3553,8 +3877,12 @@ explain (costs off) delete from pp_arrpart where a = '{1}'; drop table pp_arrpart; -- array type hash partition key create table pph_arrpart (a int[]) partition by hash (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pph_arrpart1 partition of pph_arrpart for values with (modulus 2, remainder 0); +NOTICE: table has parent, setting distribution columns to match parent table create table pph_arrpart2 partition of pph_arrpart for values with (modulus 2, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table insert into pph_arrpart values ('{1}'), ('{1, 2}'), ('{4, 5}'); select tableoid::regclass, * from pph_arrpart order by 1; tableoid | a @@ -3601,23 +3929,27 @@ create table pp_enumpart (a pp_colors) partition by list (a); NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_enumpart_green partition of pp_enumpart for values in ('green'); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_enumpart_blue partition of pp_enumpart for values in ('blue'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_enumpart where a = 'blue'; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_enumpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on pp_enumpart Number of partitions to scan: 1 (out of 2) Filter: (a = 'blue'::pp_colors) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from pp_enumpart where a = 'black'; - QUERY PLAN ---------------------------------------- + QUERY PLAN +-------------------------- Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) drop table pp_enumpart; @@ -3625,40 +3957,52 @@ drop type pp_colors; -- record type as partition key create type pp_rectype as (a int, b int); create table pp_recpart (a pp_rectype) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_recpart_11 partition of pp_recpart for values in ('(1,1)'); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_recpart_23 partition of pp_recpart for values in ('(2,3)'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_recpart where a = '(1,1)'::pp_rectype; - QUERY PLAN -------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_recpart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on pp_recpart Number of partitions to scan: 1 (out of 2) Filter: (a = '(1,1)'::pp_rectype) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from pp_recpart where a = '(1,2)'::pp_rectype; - QUERY PLAN ---------------------------------------- + QUERY PLAN +-------------------------- Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) drop table pp_recpart; drop type pp_rectype; -- range type partition key create table pp_intrangepart (a int4range) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_intrangepart12 partition of pp_intrangepart for values in ('[1,2]'); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_intrangepart2inf partition of pp_intrangepart for values in ('[2,)'); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_intrangepart where a = '[1,2]'::int4range; - QUERY PLAN -------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_intrangepart +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on pp_intrangepart Number of partitions to scan: 1 (out of 2) Filter: (a = '[1,3)'::int4range) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) select * from pp_intrangepart where a = '(1,2)'::int4range; @@ -3666,7 +4010,7 @@ explain (costs off) select * from pp_intrangepart where a = '(1,2)'::int4range; -------------------------- Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (3 rows) drop table pp_intrangepart; @@ -3674,16 +4018,22 @@ drop table pp_intrangepart; -- Ensure the enable_partition_prune GUC properly disables partition pruning. -- create table pp_lp (a int, value int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table pp_lp1 partition of pp_lp for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table pp_lp2 partition of pp_lp for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_lp where a = 1; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on pp_lp Number of partitions to scan: 1 (out of 2) Filter: (a = 1) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) update pp_lp set value = 10 where a = 1; @@ -3709,18 +4059,20 @@ explain (costs off) delete from pp_lp where a = 1; set enable_partition_pruning = off; set constraint_exclusion = 'partition'; -- this should not affect the result. explain (costs off) select * from pp_lp where a = 1; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on pp_lp Number of partitions to scan: 1 (out of 2) Filter: (a = 1) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) update pp_lp set value = 10 where a = 1; - QUERY PLAN -------------------------------------- + QUERY PLAN +---------------------------------------- Update on pp_lp Update on pp_lp1 pp_lp_1 Update on pp_lp2 pp_lp_2 @@ -3733,8 +4085,8 @@ explain (costs off) update pp_lp set value = 10 where a = 1; (9 rows) explain (costs off) delete from pp_lp where a = 1; - QUERY PLAN -------------------------------------- + QUERY PLAN +---------------------------------------- Delete on pp_lp Delete on pp_lp1 pp_lp_1 Delete on pp_lp2 pp_lp_2 @@ -3748,18 +4100,20 @@ explain (costs off) delete from pp_lp where a = 1; set constraint_exclusion = 'off'; -- this should not affect the result. explain (costs off) select * from pp_lp where a = 1; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_lp +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on pp_lp Number of partitions to scan: 1 (out of 2) Filter: (a = 1) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) explain (costs off) update pp_lp set value = 10 where a = 1; - QUERY PLAN -------------------------------------- + QUERY PLAN +---------------------------------------- Update on pp_lp Update on pp_lp1 pp_lp_1 Update on pp_lp2 pp_lp_2 @@ -3772,8 +4126,8 @@ explain (costs off) update pp_lp set value = 10 where a = 1; (9 rows) explain (costs off) delete from pp_lp where a = 1; - QUERY PLAN -------------------------------------- + QUERY PLAN +---------------------------------------- Delete on pp_lp Delete on pp_lp1 pp_lp_1 Delete on pp_lp2 pp_lp_2 @@ -3788,10 +4142,14 @@ explain (costs off) delete from pp_lp where a = 1; drop table pp_lp; -- Ensure enable_partition_prune does not affect non-partitioned tables. create table inh_lp (a int, value int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table inh_lp1 (a int, value int, check(a = 1)) inherits (inh_lp); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging column "a" with inherited definition NOTICE: merging column "value" with inherited definition create table inh_lp2 (a int, value int, check(a = 2)) inherits (inh_lp); +NOTICE: table has parent, setting distribution columns to match parent table NOTICE: merging column "a" with inherited definition NOTICE: merging column "value" with inherited definition set constraint_exclusion = 'partition'; @@ -3809,8 +4167,8 @@ explain (costs off) select * from inh_lp where a = 1; (7 rows) explain (costs off) update inh_lp set value = 10 where a = 1; - QUERY PLAN -------------------------------------- + QUERY PLAN +------------------------------------------------ Update on inh_lp Update on inh_lp inh_lp_1 Update on inh_lp1 inh_lp_2 @@ -3824,8 +4182,8 @@ explain (costs off) update inh_lp set value = 10 where a = 1; (10 rows) explain (costs off) delete from inh_lp where a = 1; - QUERY PLAN -------------------------------------- + QUERY PLAN +------------------------------------------ Delete on inh_lp Delete on inh_lp inh_lp_1 Delete on inh_lp1 inh_lp_2 @@ -3835,19 +4193,19 @@ explain (costs off) delete from inh_lp where a = 1; -> Seq Scan on inh_lp1 inh_lp_2 Filter: (a = 1) Optimizer: Postgres query optimizer -(8 rows) +(9 rows) -- Ensure we don't exclude normal relations when we only expect to exclude -- inheritance children explain (costs off) update inh_lp1 set value = 10 where a = 2; - QUERY PLAN -------------------------------------- + QUERY PLAN +-------------------------------------------- Update on inh_lp1 -> Result -> Result -> Result One-Time Filter: false - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (6 rows) drop table inh_lp cascade; @@ -3858,39 +4216,59 @@ reset enable_partition_pruning; reset constraint_exclusion; -- Check pruning for a partition tree containing only temporary relations create temp table pp_temp_parent (a int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table pp_temp_part_1 partition of pp_temp_parent for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create temp table pp_temp_part_def partition of pp_temp_parent default; +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from pp_temp_parent where true; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_temp_parent +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Dynamic Seq Scan on pp_temp_parent Number of partitions to scan: 2 (out of 2) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (4 rows) explain (costs off) select * from pp_temp_parent where a = 2; - QUERY PLAN ------------------------------------------- +NOTICE: One or more columns in the following table(s) do not have statistics: pp_temp_parent +HINT: For non-partitioned tables, run analyze (). For partitioned tables, run analyze rootpartition (). See log for columns missing statistics. + QUERY PLAN +---------------------------------------------------- Gather Motion 1:1 (slice1; segments: 1) -> Dynamic Seq Scan on pp_temp_parent Number of partitions to scan: 1 (out of 2) Filter: (a = 2) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (5 rows) drop table pp_temp_parent; -- Stress run-time partition pruning a bit more, per bug reports create temp table p (a int, b int, c int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table p1 partition of p for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create temp table p2 partition of p for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q (a int, b int, c int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create temp table q1 partition of q for values in (1) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q11 partition of q1 for values in (1) partition by list (c); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q111 partition of q11 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q2 partition of q for values in (2) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q21 partition of q2 for values in (1); +NOTICE: table has parent, setting distribution columns to match parent table create temp table q22 partition of q2 for values in (2); +NOTICE: table has parent, setting distribution columns to match parent table insert into q22 values (2, 2, 3); -- GPDB: This is the query that needs the "matchsubs" rule at the top of the file -- The constant third branch of the UNION is executed at random segment. If the @@ -3909,7 +4287,7 @@ from ( where s.a = 1 and s.b = 1 and s.c = (select 1); QUERY PLAN ------------------------------------------------------------- - Gather Motion 1:1 (slice1; segments: 1) + Gather Motion 2:1 (slice1; segments: 2) InitPlan 1 (returns $0) (slice2) -> Result -> Append @@ -3918,7 +4296,7 @@ where s.a = 1 and s.b = 1 and s.c = (select 1); -> Seq Scan on q111 q1 Filter: ((a = 1) AND (b = 1) AND (c = $0)) -> Result - One-Time Filter: (gp_execution_segment() = 1) + One-Time Filter: (gp_execution_segment() = 0) -> Result One-Time Filter: (1 = $0) Optimizer: Postgres query optimizer @@ -3961,7 +4339,7 @@ explain (costs off) execute q (1, 1); -> Seq Scan on q111 q1 Filter: ((a = $1) AND (b = $2) AND (c = $0)) -> Result - One-Time Filter: (gp_execution_segment() = 1) + One-Time Filter: (gp_execution_segment() = 0) -> Result One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = $0)) Optimizer: Postgres query optimizer @@ -3977,9 +4355,14 @@ drop table p, q; -- Ensure run-time pruning works correctly when we match a partitioned table -- on the first level but find no matching partitions on the second level. create table listp (a int, b int) partition by list (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table listp1 partition of listp for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table listp2 partition of listp for values in(2) partition by list(b); +NOTICE: table has parent, setting distribution columns to match parent table create table listp2_10 partition of listp2 for values in (10); +NOTICE: table has parent, setting distribution columns to match parent table explain (analyze, costs off, summary off, timing off) select * from listp where a = (select 2) and b <> 10; QUERY PLAN @@ -4006,7 +4389,7 @@ explain (costs off) select * from listp1 where a = 2; Gather Motion 1:1 (slice1; segments: 1) -> Seq Scan on listp1 Filter: (a = 2) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (4 rows) explain (costs off) update listp1 set a = 1 where a = 2; @@ -4021,7 +4404,7 @@ explain (costs off) update listp1 set a = 1 where a = 2; -> Split -> Seq Scan on listp1 Filter: (a = 2) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (10 rows) -- constraint exclusion enabled @@ -4032,7 +4415,7 @@ explain (costs off) select * from listp1 where a = 2; Gather Motion 1:1 (slice1; segments: 1) -> Seq Scan on listp1 Filter: (a = 2) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (4 rows) explain (costs off) update listp1 set a = 1 where a = 2; @@ -4047,7 +4430,7 @@ explain (costs off) update listp1 set a = 1 where a = 2; -> Split -> Seq Scan on listp1 Filter: (a = 2) - Optimizer: Pivotal Optimizer (GPORCA) + Optimizer: GPORCA (10 rows) reset constraint_exclusion; @@ -4057,9 +4440,14 @@ drop table listp; set parallel_setup_cost to 0; set parallel_tuple_cost to 0; create table listp (a int) partition by list(a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table listp_12 partition of listp for values in(1,2) partition by list(a); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_12_1 partition of listp_12 for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table listp_12_2 partition of listp_12 for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table -- Force the 2nd subnode of the Append to be non-parallel. This results in -- a nested Append node because the mixed parallel / non-parallel paths cannot -- be pulled into the top-level Append. @@ -4114,19 +4502,26 @@ reset parallel_setup_cost; -- Test case for run-time pruning with a nested Merge Append set enable_sort to 0; create table rangep (a int, b int) partition by range (a); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rangep_0_to_100 partition of rangep for values from (0) to (100) partition by list (b); +NOTICE: table has parent, setting distribution columns to match parent table -- We need 3 sub-partitions. 1 to validate pruning worked and another two -- because a single remaining partition would be pulled up to the main Append. create table rangep_0_to_100_1 partition of rangep_0_to_100 for values in(1); +NOTICE: table has parent, setting distribution columns to match parent table create table rangep_0_to_100_2 partition of rangep_0_to_100 for values in(2); +NOTICE: table has parent, setting distribution columns to match parent table create table rangep_0_to_100_3 partition of rangep_0_to_100 for values in(3); +NOTICE: table has parent, setting distribution columns to match parent table create table rangep_100_to_200 partition of rangep for values from (100) to (200); +NOTICE: table has parent, setting distribution columns to match parent table create index on rangep (a); -- Ensure run-time pruning works on the nested Merge Append explain (analyze on, costs off, timing off, summary off) select * from rangep where b IN((select 1),(select 2)) order by a; - QUERY PLAN ------------------------------------------------------------------------------------------------------------ + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (actual rows=0 loops=1) Merge Key: rangep.a InitPlan 1 (returns $0) (slice2) @@ -4154,8 +4549,12 @@ drop table rangep; -- clauses for different partition keys -- create table rp_prefix_test1 (a int, b varchar) partition by range(a, b); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rp_prefix_test1_p1 partition of rp_prefix_test1 for values from (1, 'a') to (1, 'b'); +NOTICE: table has parent, setting distribution columns to match parent table create table rp_prefix_test1_p2 partition of rp_prefix_test1 for values from (2, 'a') to (2, 'b'); +NOTICE: table has parent, setting distribution columns to match parent table -- Don't call get_steps_using_prefix() with the last partition key b plus -- an empty prefix explain (costs off) select * from rp_prefix_test1 where a <= 1 and b = 'a'; @@ -4168,8 +4567,12 @@ explain (costs off) select * from rp_prefix_test1 where a <= 1 and b = 'a'; (4 rows) create table rp_prefix_test2 (a int, b int, c int) partition by range(a, b, c); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rp_prefix_test2_p1 partition of rp_prefix_test2 for values from (1, 1, 0) to (1, 1, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table rp_prefix_test2_p2 partition of rp_prefix_test2 for values from (2, 2, 0) to (2, 2, 10); +NOTICE: table has parent, setting distribution columns to match parent table -- Don't call get_steps_using_prefix() with the last partition key c plus -- an invalid prefix (ie, b = 1) explain (costs off) select * from rp_prefix_test2 where a <= 1 and b = 1 and c >= 0; @@ -4182,8 +4585,12 @@ explain (costs off) select * from rp_prefix_test2 where a <= 1 and b = 1 and c > (4 rows) create table rp_prefix_test3 (a int, b int, c int, d int) partition by range(a, b, c, d); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table rp_prefix_test3_p1 partition of rp_prefix_test3 for values from (1, 1, 1, 0) to (1, 1, 1, 10); +NOTICE: table has parent, setting distribution columns to match parent table create table rp_prefix_test3_p2 partition of rp_prefix_test3 for values from (2, 2, 2, 0) to (2, 2, 2, 10); +NOTICE: table has parent, setting distribution columns to match parent table -- Test that get_steps_using_prefix() handles a prefix that contains multiple -- clauses for the partition key b (ie, b >= 1 and b >= 2) explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b >= 2 and c >= 2 and d >= 0; @@ -4207,22 +4614,265 @@ explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b Optimizer: Postgres query optimizer (4 rows) -create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); -create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0); -create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1); --- Test that get_steps_using_prefix() handles non-NULL step_nullkeys -explain (costs off) select * from hp_prefix_test where a = 1 and b is null and c = 1 and d = 1; - QUERY PLAN -------------------------------------------------------------------- +drop table rp_prefix_test1; +drop table rp_prefix_test2; +drop table rp_prefix_test3; +-- +-- Test that get_steps_using_prefix() handles IS NULL clauses correctly +-- +create table hp_prefix_test (a int, b int, c int, d int) + partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +-- create 8 partitions +select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 8, remainder ' || x::text || ');' +from generate_Series(0,7) x; + ?column? +------------------------------------------------------------------------------------------------------ + create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0); + create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1); + create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2); + create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3); + create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4); + create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5); + create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6); + create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7); +(8 rows) + +\gexec +create table hp_prefix_test_p0 partition of hp_prefix_test for values with (modulus 8, remainder 0); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 8, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 8, remainder 2); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p3 partition of hp_prefix_test for values with (modulus 8, remainder 3); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p4 partition of hp_prefix_test for values with (modulus 8, remainder 4); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p5 partition of hp_prefix_test for values with (modulus 8, remainder 5); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p6 partition of hp_prefix_test for values with (modulus 8, remainder 6); +NOTICE: table has parent, setting distribution columns to match parent table +create table hp_prefix_test_p7 partition of hp_prefix_test for values with (modulus 8, remainder 7); +NOTICE: table has parent, setting distribution columns to match parent table +-- insert 16 rows, one row for each test to perform. +insert into hp_prefix_test +select + case a when 0 then null else 1 end, + case b when 0 then null else 2 end, + case c when 0 then null else 3 end, + case d when 0 then null else 4 end +from + generate_series(0,1) a, + generate_series(0,1) b, + generate_Series(0,1) c, + generate_Series(0,1) d; +-- Ensure partition pruning works correctly for each combination of IS NULL +-- and equality quals. This may seem a little excessive, but there have been +-- a number of bugs in this area over the years. We make use of row only +-- output to reduce the size of the expected results. +--GPDB_14_MERGE_FIXME: with the force-parallel (ic-cbdb-parallel) suite these +--headerless explain outputs cannot be treated as plan blocks by the result +--comparison machinery, and the plan text differs (Parallel Seq Scan). Mask +--the whole explain section via start_ignore. +--start_ignore +\t on +select + 'explain (costs off) select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + +\gexec +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p0 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d IS NULL)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null Gather Motion 1:1 (slice1; segments: 1) -> Seq Scan on hp_prefix_test_p1 hp_prefix_test - Filter: ((b IS NULL) AND (a = 1) AND (c = 1) AND (d = 1)) + Filter: ((b IS NULL) AND (c IS NULL) AND (d IS NULL) AND (a = 1)) Optimizer: Postgres query optimizer -(4 rows) -drop table rp_prefix_test1; -drop table rp_prefix_test2; -drop table rp_prefix_test3; +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p2 hp_prefix_test + Filter: ((a IS NULL) AND (c IS NULL) AND (d IS NULL) AND (b = 2)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((c IS NULL) AND (d IS NULL) AND (a = 1) AND (b = 2)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p3 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (d IS NULL) AND (c = 3)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p7 hp_prefix_test + Filter: ((b IS NULL) AND (d IS NULL) AND (a = 1) AND (c = 3)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a IS NULL) AND (d IS NULL) AND (b = 2) AND (c = 3)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p5 hp_prefix_test + Filter: ((d IS NULL) AND (a = 1) AND (b = 2) AND (c = 3)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c IS NULL) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p6 hp_prefix_test + Filter: ((b IS NULL) AND (c IS NULL) AND (a = 1) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p5 hp_prefix_test + Filter: ((a IS NULL) AND (c IS NULL) AND (b = 2) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p6 hp_prefix_test + Filter: ((c IS NULL) AND (a = 1) AND (b = 2) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a IS NULL) AND (b IS NULL) AND (c = 3) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p5 hp_prefix_test + Filter: ((b IS NULL) AND (a = 1) AND (c = 3) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p6 hp_prefix_test + Filter: ((a IS NULL) AND (b = 2) AND (c = 3) AND (d = 4)) + Optimizer: Postgres query optimizer + +explain (costs off) select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on hp_prefix_test_p4 hp_prefix_test + Filter: ((a = 1) AND (b = 2) AND (c = 3) AND (d = 4)) + Optimizer: Postgres query optimizer + +--end_ignore +-- And ensure we get exactly 1 row from each. Again, all 16 possible combinations. +select + 'select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + +\gexec +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d is null + hp_prefix_test_p0 | | | | + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d is null + hp_prefix_test_p1 | 1 | | | + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d is null + hp_prefix_test_p2 | | 2 | | + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d is null + hp_prefix_test_p4 | 1 | 2 | | + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d is null + hp_prefix_test_p3 | | | 3 | + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d is null + hp_prefix_test_p7 | 1 | | 3 | + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d is null + hp_prefix_test_p4 | | 2 | 3 | + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d is null + hp_prefix_test_p5 | 1 | 2 | 3 | + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c is null and d = 4 + hp_prefix_test_p4 | | | | 4 + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c is null and d = 4 + hp_prefix_test_p6 | 1 | | | 4 + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c is null and d = 4 + hp_prefix_test_p5 | | 2 | | 4 + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c is null and d = 4 + hp_prefix_test_p6 | 1 | 2 | | 4 + +select tableoid::regclass,* from hp_prefix_test where a is null and b is null and c = 3 and d = 4 + hp_prefix_test_p4 | | | 3 | 4 + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b is null and c = 3 and d = 4 + hp_prefix_test_p5 | 1 | | 3 | 4 + +select tableoid::regclass,* from hp_prefix_test where a is null and b = 2 and c = 3 and d = 4 + hp_prefix_test_p6 | | 2 | 3 | 4 + +select tableoid::regclass,* from hp_prefix_test where a = 1 and b = 2 and c = 3 and d = 4 + hp_prefix_test_p4 | 1 | 2 | 3 | 4 + +\t off drop table hp_prefix_test; -- -- Check that gen_partprune_steps() detects self-contradiction from clauses @@ -4242,21 +4892,27 @@ using hash as operator 1 ===, function 2 part_hashint4_noop(int4, int8); create table hp_contradict_test (a int, b int) partition by hash (a part_test_int4_ops2, b part_test_int4_ops2); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'a' as the Apache Cloudberry data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. create table hp_contradict_test_p1 partition of hp_contradict_test for values with (modulus 2, remainder 0); +NOTICE: table has parent, setting distribution columns to match parent table create table hp_contradict_test_p2 partition of hp_contradict_test for values with (modulus 2, remainder 1); +NOTICE: table has parent, setting distribution columns to match parent table explain (costs off) select * from hp_contradict_test where a is null and a === 1 and b === 1; - QUERY PLAN --------------------------- + QUERY PLAN +------------------------------------- Result One-Time Filter: false -(2 rows) + Optimizer: Postgres query optimizer +(3 rows) explain (costs off) select * from hp_contradict_test where a === 1 and b === 1 and a is null; - QUERY PLAN --------------------------- + QUERY PLAN +------------------------------------- Result One-Time Filter: false -(2 rows) + Optimizer: Postgres query optimizer +(3 rows) drop table hp_contradict_test; drop operator class part_test_int4_ops2 using hash; diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out index 03dc2ab3a79..4cb3a1c06fa 100644 --- a/src/test/regress/expected/rowtypes.out +++ b/src/test/regress/expected/rowtypes.out @@ -1234,6 +1234,69 @@ select r, r is null as isnull, r is not null as isnotnull from r; (,) | t | f (6 rows) +-- +-- Check parsing of indirect references to composite values (bug #18077) +-- +explain (verbose, costs off) +with cte(c) as materialized (select row(1, 2)), + cte2(c) as (select * from cte) +select * from cte2 as t +where (select * from (select c as c1) s + where (select (c1).f1 > 0)) is not null; + QUERY PLAN +------------------------------------------ + Subquery Scan on t + Output: t.c + Filter: ((SubPlan 2) IS NOT NULL) + -> Shared Scan (share slice:id 0:0) + Output: share0_ref1."row" + -> Result + Output: ROW(1, 2) + SubPlan 2 + -> Result + Output: t.c + One-Time Filter: $1 + InitPlan 1 (returns $1) + -> Result + Output: ((t.c).f1 > 0) + Settings: optimizer = 'off' + Optimizer: Postgres query optimizer +(16 rows) + +with cte(c) as materialized (select row(1, 2)), + cte2(c) as (select * from cte) +select * from cte2 as t +where (select * from (select c as c1) s + where (select (c1).f1 > 0)) is not null; + c +------- + (1,2) +(1 row) + +-- Also check deparsing of such cases +create view composite_v as +with cte(c) as materialized (select row(1, 2)), + cte2(c) as (select * from cte) +select 1 as one from cte2 as t +where (select * from (select c as c1) s + where (select (c1).f1 > 0)) is not null; +select pg_get_viewdef('composite_v', true); + pg_get_viewdef +-------------------------------------------------------- + WITH cte(c) AS MATERIALIZED ( + + SELECT ROW(1, 2) AS "row" + + ), cte2(c) AS ( + + SELECT cte.c + + FROM cte + + ) + + SELECT 1 AS one + + FROM cte2 t + + WHERE (( SELECT s.c1 + + FROM ( SELECT t.c AS c1) s + + WHERE ( SELECT (s.c1).f1 > 0))) IS NOT NULL; +(1 row) + +drop view composite_v; -- -- Tests for component access / FieldSelect -- diff --git a/src/test/regress/sql/brin_multi.sql b/src/test/regress/sql/brin_multi.sql index 327a4215bf9..17891e9194d 100644 --- a/src/test/regress/sql/brin_multi.sql +++ b/src/test/regress/sql/brin_multi.sql @@ -421,6 +421,122 @@ CREATE INDEX brin_test_multi_b_idx ON brin_test_multi USING brin (b) WITH (pages VACUUM ANALYZE brin_test_multi; -- Ensure brin index is used when columns are perfectly correlated +--GPDB_14_MERGE_FIXME: plan shapes differ between the normal and +--force-parallel (ic-cbdb-parallel) suites; mask via start_ignore. +--start_ignore EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1; -- Ensure brin index is not used when values are not correlated EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1; +--end_ignore + +-- test overflows during CREATE INDEX with extreme timestamp values +CREATE TABLE brin_timestamp_test(a TIMESTAMPTZ); + +SET datestyle TO iso; + +-- values close to timetamp minimum +INSERT INTO brin_timestamp_test +SELECT '4713-01-01 00:00:01 BC'::timestamptz + (i || ' seconds')::interval + FROM generate_series(1,30) s(i); + +-- values close to timetamp maximum +INSERT INTO brin_timestamp_test +SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval + FROM generate_series(1,30) s(i); + +CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1); +DROP TABLE brin_timestamp_test; + +-- test overflows during CREATE INDEX with extreme date values +CREATE TABLE brin_date_test(a DATE); + +-- insert values close to date minimum +INSERT INTO brin_date_test SELECT '4713-01-01 BC'::date + i FROM generate_series(1, 30) s(i); + +-- insert values close to date minimum +INSERT INTO brin_date_test SELECT '5874897-12-01'::date + i FROM generate_series(1, 30) s(i); + +CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1); + +SET enable_seqscan = off; + +-- make sure the ranges were built correctly and 2023-01-01 eliminates all +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date; +--end_ignore + +DROP TABLE brin_date_test; +RESET enable_seqscan; + +-- test handling of infinite timestamp values +CREATE TABLE brin_timestamp_test(a TIMESTAMP); + +INSERT INTO brin_timestamp_test VALUES ('-infinity'), ('infinity'); +INSERT INTO brin_timestamp_test +SELECT i FROM generate_series('2000-01-01'::timestamp, '2000-02-09'::timestamp, '1 day'::interval) s(i); + +CREATE INDEX ON brin_timestamp_test USING brin (a timestamp_minmax_multi_ops) WITH (pages_per_range=1); + +SET enable_seqscan = off; + +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_timestamp_test WHERE a = '2023-01-01'::timestamp; +--end_ignore +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_timestamp_test WHERE a = '1900-01-01'::timestamp; +--end_ignore + +DROP TABLE brin_timestamp_test; +RESET enable_seqscan; + +-- test handling of infinite date values +CREATE TABLE brin_date_test(a DATE); + +INSERT INTO brin_date_test VALUES ('-infinity'), ('infinity'); +INSERT INTO brin_date_test SELECT '2000-01-01'::date + i FROM generate_series(1, 40) s(i); + +CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1); + +SET enable_seqscan = off; + +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date; +--end_ignore + +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_date_test WHERE a = '1900-01-01'::date; +--end_ignore + +DROP TABLE brin_date_test; +RESET enable_seqscan; +RESET datestyle; + +-- test handling of overflow for interval values +CREATE TABLE brin_interval_test(a INTERVAL); + +INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM generate_series(-178000000, -177999980) s(i); + +INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM generate_series( 177999980, 178000000) s(i); + +CREATE INDEX ON brin_interval_test USING brin (a interval_minmax_multi_ops) WITH (pages_per_range=1); + +SET enable_seqscan = off; + +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_interval_test WHERE a = '-30 years'::interval; +--end_ignore + +--start_ignore +EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) +SELECT * FROM brin_interval_test WHERE a = '30 years'::interval; +--end_ignore + +DROP TABLE brin_interval_test; +RESET enable_seqscan; +RESET datestyle; diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql index e5f9b980776..c6acd74cb84 100644 --- a/src/test/regress/sql/inherit.sql +++ b/src/test/regress/sql/inherit.sql @@ -97,6 +97,25 @@ SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; CREATE TEMP TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a); INSERT INTO z VALUES (NULL, 'text'); -- should fail +-- Check inherited UPDATE with first child excluded +create table some_tab (f1 int, f2 int, f3 int, check (f1 < 10) no inherit); +create table some_tab_child () inherits(some_tab); +insert into some_tab_child select i, i+1, 0 from generate_series(1,1000) i; +create index on some_tab_child(f1, f2); +-- while at it, also check that statement-level triggers fire +create function some_tab_stmt_trig_func() returns trigger as +$$begin raise notice 'updating some_tab'; return NULL; end;$$ +language plpgsql; +create trigger some_tab_stmt_trig + before update on some_tab execute function some_tab_stmt_trig_func(); + +explain (costs off) +update some_tab set f3 = 11 where f1 = 12 and f2 = 13; +update some_tab set f3 = 11 where f1 = 12 and f2 = 13; + +drop table some_tab cascade; +drop function some_tab_stmt_trig_func(); + -- Check inherited UPDATE with all children excluded create table some_tab (a int, b int) distributed randomly; create table some_tab_child () inherits (some_tab); diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index f9e3b1f2013..64a21c3ae78 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -375,8 +375,6 @@ drop table hp2; explain (costs off) select * from hp where a = 1 and b = 'abcde' and (c = 2 or c = 3); -drop table hp; - -- -- Test runtime partition pruning -- @@ -427,6 +425,25 @@ select a from ab where b between $1 and $2 and a < (select 3); explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); +-- +-- Test runtime pruning with hash partitioned tables +-- + +-- recreate partitions dropped above +create table hp1 partition of hp for values with (modulus 4, remainder 1); +create table hp2 partition of hp for values with (modulus 4, remainder 2); +create table hp3 partition of hp for values with (modulus 4, remainder 3); + +-- Ensure we correctly prune unneeded partitions when there is an IS NULL qual +prepare hp_q1 (text) as +select * from hp where a is null and b = $1; + +explain (costs off) execute hp_q1('xxx'); + +deallocate hp_q1; + +drop table hp; + -- Test a backwards Append scan create table list_part (a int) partition by list (a); create table list_part1 partition of list_part for values in (1); @@ -1191,16 +1208,63 @@ explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b -- that the caller arranges clauses in that prefix in the required order) explain (costs off) select * from rp_prefix_test3 where a >= 1 and b >= 1 and b = 2 and c = 2 and d >= 0; -create table hp_prefix_test (a int, b int, c int, d int) partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); -create table hp_prefix_test_p1 partition of hp_prefix_test for values with (modulus 2, remainder 0); -create table hp_prefix_test_p2 partition of hp_prefix_test for values with (modulus 2, remainder 1); - --- Test that get_steps_using_prefix() handles non-NULL step_nullkeys -explain (costs off) select * from hp_prefix_test where a = 1 and b is null and c = 1 and d = 1; - drop table rp_prefix_test1; drop table rp_prefix_test2; drop table rp_prefix_test3; + +-- +-- Test that get_steps_using_prefix() handles IS NULL clauses correctly +-- +create table hp_prefix_test (a int, b int, c int, d int) + partition by hash (a part_test_int4_ops, b part_test_int4_ops, c part_test_int4_ops, d part_test_int4_ops); + +-- create 8 partitions +select 'create table hp_prefix_test_p' || x::text || ' partition of hp_prefix_test for values with (modulus 8, remainder ' || x::text || ');' +from generate_Series(0,7) x; +\gexec + +-- insert 16 rows, one row for each test to perform. +insert into hp_prefix_test +select + case a when 0 then null else 1 end, + case b when 0 then null else 2 end, + case c when 0 then null else 3 end, + case d when 0 then null else 4 end +from + generate_series(0,1) a, + generate_series(0,1) b, + generate_Series(0,1) c, + generate_Series(0,1) d; + +-- Ensure partition pruning works correctly for each combination of IS NULL +-- and equality quals. This may seem a little excessive, but there have been +-- a number of bugs in this area over the years. We make use of row only +-- output to reduce the size of the expected results. +--GPDB_14_MERGE_FIXME: with the force-parallel (ic-cbdb-parallel) suite these +--headerless explain outputs cannot be treated as plan blocks by the result +--comparison machinery, and the plan text differs (Parallel Seq Scan). Mask +--the whole explain section via start_ignore. +--start_ignore +\t on +select + 'explain (costs off) select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; +\gexec +--end_ignore + +-- And ensure we get exactly 1 row from each. Again, all 16 possible combinations. +select + 'select tableoid::regclass,* from hp_prefix_test where ' || + string_agg(c.colname || case when g.s & (1 << c.colpos) = 0 then ' is null' else ' = ' || (colpos+1)::text end, ' and ' order by c.colpos) +from (values('a',0),('b',1),('c',2),('d',3)) c(colname, colpos), generate_Series(0,15) g(s) +group by g.s +order by g.s; +\gexec +\t off + drop table hp_prefix_test; -- diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql index 54f338af1f8..50e0ca6cd6b 100644 --- a/src/test/regress/sql/rowtypes.sql +++ b/src/test/regress/sql/rowtypes.sql @@ -501,6 +501,31 @@ with r(a,b) as materialized (null,row(1,2)), (null,row(null,null)), (null,null) ) select r, r is null as isnull, r is not null as isnotnull from r; +-- +-- Check parsing of indirect references to composite values (bug #18077) +-- +explain (verbose, costs off) +with cte(c) as materialized (select row(1, 2)), + cte2(c) as (select * from cte) +select * from cte2 as t +where (select * from (select c as c1) s + where (select (c1).f1 > 0)) is not null; + +with cte(c) as materialized (select row(1, 2)), + cte2(c) as (select * from cte) +select * from cte2 as t +where (select * from (select c as c1) s + where (select (c1).f1 > 0)) is not null; + +-- Also check deparsing of such cases +create view composite_v as +with cte(c) as materialized (select row(1, 2)), + cte2(c) as (select * from cte) +select 1 as one from cte2 as t +where (select * from (select c as c1) s + where (select (c1).f1 > 0)) is not null; +select pg_get_viewdef('composite_v', true); +drop view composite_v; -- -- Tests for component access / FieldSelect diff --git a/src/test/singlenode_regress/expected/opr_sanity.out b/src/test/singlenode_regress/expected/opr_sanity.out index 2fb7ba06584..d3bc9936213 100644 --- a/src/test/singlenode_regress/expected/opr_sanity.out +++ b/src/test/singlenode_regress/expected/opr_sanity.out @@ -2231,6 +2231,7 @@ ORDER BY 1, 2, 3; | complex_ops | complex_ops | complex | float_ops | float4_ops | real | float_ops | float8_ops | double precision + | interval_ops | interval_ops | interval | jsonb_ops | jsonb_ops | jsonb | multirange_ops | multirange_ops | anymultirange | numeric_ops | numeric_ops | numeric @@ -2239,7 +2240,7 @@ ORDER BY 1, 2, 3; | record_ops | record_ops | record | tsquery_ops | tsquery_ops | tsquery | tsvector_ops | tsvector_ops | tsvector -(16 rows) +(17 rows) -- **************** pg_index **************** -- Look for illegal values in pg_index fields.