/*
* Initialize the subplan_map and subpart_map.
*
- * Because we request detached partitions to be included, and
- * detaching waits for old transactions, it is safe to assume that
- * no partitions have disappeared since this query was planned.
+ * The set of partitions that exist now might not be the same that
+ * existed when the plan was made. The normal case is that it is;
+ * optimize for that case with a quick comparison, and just copy
+ * the subplan_map and make subpart_map point to the one in
+ * PruneInfo.
*
- * However, new partitions may have been added.
+ * For the case where they aren't identical, we could have more
+ * partitions on either side; or even exactly the same number of
+ * them on both but the set of OIDs doesn't match fully. Handle
+ * this by creating new subplan_map and subpart_map arrays that
+ * corresponds to the ones in the PruneInfo where the new
+ * partition descriptor's OIDs match. Any that don't match can be
+ * set to -1, as if they were pruned. Both arrays must be in
+ * numerical OID order.
*/
- Assert(partdesc->nparts >= pinfo->nparts);
pprune->nparts = partdesc->nparts;
pprune->subplan_map = palloc(sizeof(int) * partdesc->nparts);
- if (partdesc->nparts == pinfo->nparts)
+
+ if (partdesc->nparts == pinfo->nparts &&
+ memcmp(partdesc->oids, pinfo->relid_map,
+ sizeof(int) * partdesc->nparts) == 0)
{
- /*
- * There are no new partitions, so this is simple. We can
- * simply point to the subpart_map from the plan, but we must
- * copy the subplan_map since we may change it later.
- */
pprune->subpart_map = pinfo->subpart_map;
memcpy(pprune->subplan_map, pinfo->subplan_map,
sizeof(int) * pinfo->nparts);
-
- /*
- * Double-check that the list of unpruned relations has not
- * changed. (Pruned partitions are not in relid_map[].)
- */
-#ifdef USE_ASSERT_CHECKING
- for (int k = 0; k < pinfo->nparts; k++)
- {
- Assert(partdesc->oids[k] == pinfo->relid_map[k] ||
- pinfo->subplan_map[k] == -1);
- }
-#endif
}
else
{
int pp_idx;
/*
- * Some new partitions have appeared since plan time, and
- * those are reflected in our PartitionDesc but were not
- * present in the one used to construct subplan_map and
- * subpart_map. So we must construct new and longer arrays
- * where the partitions that were originally present map to
- * the same sub-structures, and any added partitions map to
- * -1, as if the new partitions had been pruned.
+ * When the partition arrays are not identical, there could be
+ * some new ones but it's also possible that one was removed;
+ * we cope with both situations by walking the arrays and
+ * discarding those that don't match.
*
- * Note: pinfo->relid_map[] may contain InvalidOid entries for
- * partitions pruned by the planner. We cannot tell exactly
- * which of the partdesc entries these correspond to, but we
- * don't have to; just skip over them. The non-pruned
- * relid_map entries, however, had better be a subset of the
- * partdesc entries and in the same order.
+ * If the number of partitions on both sides match, it's still
+ * possible that one partition has been detached and another
+ * attached. Cope with that by creating a map that skips any
+ * mismatches.
*/
pprune->subpart_map = palloc(sizeof(int) * partdesc->nparts);
+
for (pp_idx = 0; pp_idx < partdesc->nparts; pp_idx++)
{
/* Skip any InvalidOid relid_map entries */
!OidIsValid(pinfo->relid_map[pd_idx]))
pd_idx++;
+ recheck:
if (pd_idx < pinfo->nparts &&
pinfo->relid_map[pd_idx] == partdesc->oids[pp_idx])
{
pprune->subpart_map[pp_idx] =
pinfo->subpart_map[pd_idx];
pd_idx++;
+ continue;
}
- else
+
+ /*
+ * There isn't an exact match in the corresponding
+ * positions of both arrays. Peek ahead in
+ * pinfo->relid_map to see if we have a match for the
+ * current partition in partdesc. Normally if a match
+ * exists it's just one element ahead, and it means the
+ * planner saw one extra partition that we no longer see
+ * now (its concurrent detach finished just in between);
+ * so we skip that one by updating pd_idx to the new
+ * location and jumping above. We can then continue to
+ * match the rest of the elements after skipping the OID
+ * with no match; no future matches are tried for the
+ * element that was skipped, because we know the arrays to
+ * be in the same order.
+ *
+ * If we don't see a match anywhere in the rest of the
+ * pinfo->relid_map array, that means we see an element
+ * now that the planner didn't see, so mark that one as
+ * pruned and move on.
+ */
+ for (int pd_idx2 = pd_idx + 1; pd_idx2 < pinfo->nparts; pd_idx2++)
{
- /* this partdesc entry is not in the plan */
- pprune->subplan_map[pp_idx] = -1;
- pprune->subpart_map[pp_idx] = -1;
+ if (pd_idx2 >= pinfo->nparts)
+ break;
+ if (pinfo->relid_map[pd_idx2] == partdesc->oids[pp_idx])
+ {
+ pd_idx = pd_idx2;
+ goto recheck;
+ }
}
- }
- /*
- * It might seem that we need to skip any trailing InvalidOid
- * entries in pinfo->relid_map before checking that we scanned
- * all of the relid_map. But we will have skipped them above,
- * because they must correspond to some partdesc->oids
- * entries; we just couldn't tell which.
- */
- if (pd_idx != pinfo->nparts)
- elog(ERROR, "could not match partition child tables to plan elements");
+ pprune->subpart_map[pp_idx] = -1;
+ pprune->subplan_map[pp_idx] = -1;
+ }
}
/* present_parts is also subject to later modification */