/*------------------------------------------------------------------------- * * plancat.c * routines for accessing the system catalogs * * * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/optimizer/util/plancat.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include "access/genam.h" #include "access/htup_details.h" #include "access/nbtree.h" #include "access/sysattr.h" #include "access/table.h" #include "access/tableam.h" #include "access/transam.h" #include "access/xlog.h" #include "catalog/catalog.h" #include "catalog/heap.h" #include "catalog/pg_am.h" #include "catalog/pg_proc.h" #include "catalog/pg_statistic_ext.h" #include "catalog/pg_statistic_ext_data.h" #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/optimizer.h" #include "optimizer/plancat.h" #include "optimizer/prep.h" #include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" #include "statistics/statistics.h" #include "storage/bufmgr.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/partcache.h" #include "utils/rel.h" #include "utils/snapmgr.h" #include "utils/syscache.h" /* GUC parameter */ int constraint_exclusion = CONSTRAINT_EXCLUSION_PARTITION; /* Hook for plugins to get control in get_relation_info() */ get_relation_info_hook_type get_relation_info_hook = NULL; static void get_relation_foreign_keys(PlannerInfo *root, RelOptInfo *rel, Relation relation, bool inhparent); static bool infer_collation_opclass_match(InferenceElem *elem, Relation idxRel, List *idxExprs); static List *get_relation_constraints(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel, bool include_noinherit, bool include_notnull, bool include_partition); static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index, Relation heapRelation); static List *get_relation_statistics(RelOptInfo *rel, Relation relation); static void set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel, Relation relation); static PartitionScheme find_partition_scheme(PlannerInfo *root, Relation rel); static void set_baserel_partition_key_exprs(Relation relation, RelOptInfo *rel); static void set_baserel_partition_constraint(Relation relation, RelOptInfo *rel); /* * get_relation_info - * Retrieves catalog information for a given relation. * * Given the Oid of the relation, return the following info into fields * of the RelOptInfo struct: * * min_attr lowest valid AttrNumber * max_attr highest valid AttrNumber * indexlist list of IndexOptInfos for relation's indexes * statlist list of StatisticExtInfo for relation's statistic objects * serverid if it's a foreign table, the server OID * fdwroutine if it's a foreign table, the FDW function pointers * pages number of pages * tuples number of tuples * rel_parallel_workers user-defined number of parallel workers * * Also, add information about the relation's foreign keys to root->fkey_list. * * Also, initialize the attr_needed[] and attr_widths[] arrays. In most * cases these are left as zeroes, but sometimes we need to compute attr * widths here, and we may as well cache the results for costsize.c. * * If inhparent is true, all we need to do is set up the attr arrays: * the RelOptInfo actually represents the appendrel formed by an inheritance * tree, and so the parent rel's physical size and index information isn't * important for it. */ void get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, RelOptInfo *rel) { Index varno = rel->relid; Relation relation; bool hasindex; List *indexinfos = NIL; /* * We need not lock the relation since it was already locked, either by * the rewriter or when expand_inherited_rtentry() added it to the query's * rangetable. */ relation = table_open(relationObjectId, NoLock); /* * Relations without a table AM can be used in a query only if they are of * special-cased relkinds. This check prevents us from crashing later if, * for example, a view's ON SELECT rule has gone missing. Note that * table_open() already rejected indexes and composite types; spell the * error the same way it does. */ if (!relation->rd_tableam) { if (!(relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE || relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot open relation \"%s\"", RelationGetRelationName(relation)), errdetail_relkind_not_supported(relation->rd_rel->relkind))); } /* Temporary and unlogged relations are inaccessible during recovery. */ if (!RelationIsPermanent(relation) && RecoveryInProgress()) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary or unlogged relations during recovery"))); rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1; rel->max_attr = RelationGetNumberOfAttributes(relation); rel->reltablespace = RelationGetForm(relation)->reltablespace; Assert(rel->max_attr >= rel->min_attr); rel->attr_needed = (Relids *) palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids)); rel->attr_widths = (int32 *) palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32)); /* * Estimate relation size --- unless it's an inheritance parent, in which * case the size we want is not the rel's own size but the size of its * inheritance tree. That will be computed in set_append_rel_size(). */ if (!inhparent) estimate_rel_size(relation, rel->attr_widths - rel->min_attr, &rel->pages, &rel->tuples, &rel->allvisfrac); /* Retrieve the parallel_workers reloption, or -1 if not set. */ rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1); /* * Make list of indexes. Ignore indexes on system catalogs if told to. * Don't bother with indexes for an inheritance parent, either. */ if (inhparent || (IgnoreSystemIndexes && IsSystemRelation(relation))) hasindex = false; else hasindex = relation->rd_rel->relhasindex; if (hasindex) { List *indexoidlist; LOCKMODE lmode; ListCell *l; indexoidlist = RelationGetIndexList(relation); /* * For each index, we get the same type of lock that the executor will * need, and do not release it. This saves a couple of trips to the * shared lock manager while not creating any real loss of * concurrency, because no schema changes could be happening on the * index while we hold lock on the parent rel, and no lock type used * for queries blocks any other kind of index operation. */ lmode = root->simple_rte_array[varno]->rellockmode; foreach(l, indexoidlist) { Oid indexoid = lfirst_oid(l); Relation indexRelation; Form_pg_index index; IndexAmRoutine *amroutine; IndexOptInfo *info; int ncolumns, nkeycolumns; int i; /* * Extract info from the relation descriptor for the index. */ indexRelation = index_open(indexoid, lmode); index = indexRelation->rd_index; /* * Ignore invalid indexes, since they can't safely be used for * queries. Note that this is OK because the data structure we * are constructing is only used by the planner --- the executor * still needs to insert into "invalid" indexes, if they're marked * indisready. */ if (!index->indisvalid) { index_close(indexRelation, NoLock); continue; } /* * Ignore partitioned indexes, since they are not usable for * queries. */ if (indexRelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) { index_close(indexRelation, NoLock); continue; } /* * If the index is valid, but cannot yet be used, ignore it; but * mark the plan we are generating as transient. See * src/backend/access/heap/README.HOT for discussion. */ if (index->indcheckxmin && !TransactionIdPrecedes(HeapTupleHeaderGetXmin(indexRelation->rd_indextuple->t_data), TransactionXmin)) { root->glob->transientPlan = true; index_close(indexRelation, NoLock); continue; } info = makeNode(IndexOptInfo); info->indexoid = index->indexrelid; info->reltablespace = RelationGetForm(indexRelation)->reltablespace; info->rel = rel; info->ncolumns = ncolumns = index->indnatts; info->nkeycolumns = nkeycolumns = index->indnkeyatts; info->indexkeys = (int *) palloc(sizeof(int) * ncolumns); info->indexcollations = (Oid *) palloc(sizeof(Oid) * nkeycolumns); info->opfamily = (Oid *) palloc(sizeof(Oid) * nkeycolumns); info->opcintype = (Oid *) palloc(sizeof(Oid) * nkeycolumns); info->canreturn = (bool *) palloc(sizeof(bool) * ncolumns); for (i = 0; i < ncolumns; i++) { info->indexkeys[i] = index->indkey.values[i]; info->canreturn[i] = index_can_return(indexRelation, i + 1); } for (i = 0; i < nkeycolumns; i++) { info->opfamily[i] = indexRelation->rd_opfamily[i]; info->opcintype[i] = indexRelation->rd_opcintype[i]; info->indexcollations[i] = indexRelation->rd_indcollation[i]; } info->relam = indexRelation->rd_rel->relam; /* We copy just the fields we need, not all of rd_indam */ amroutine = indexRelation->rd_indam; info->amcanorderbyop = amroutine->amcanorderbyop; info->amoptionalkey = amroutine->amoptionalkey; info->amsearcharray = amroutine->amsearcharray; info->amsearchnulls = amroutine->amsearchnulls; info->amcanparallel = amroutine->amcanparallel; info->amhasgettuple = (amroutine->amgettuple != NULL); info->amhasgetbitmap = amroutine->amgetbitmap != NULL && relation->rd_tableam->scan_bitmap_next_block != NULL; info->amcanmarkpos = (amroutine->ammarkpos != NULL && amroutine->amrestrpos != NULL); info->amcostestimate = amroutine->amcostestimate; Assert(info->amcostestimate != NULL); /* Fetch index opclass options */ info->opclassoptions = RelationGetIndexAttOptions(indexRelation, true); /* * Fetch the ordering information for the index, if any. */ if (info->relam == BTREE_AM_OID) { /* * If it's a btree index, we can use its opfamily OIDs * directly as the sort ordering opfamily OIDs. */ Assert(amroutine->amcanorder); info->sortopfamily = info->opfamily; info->reverse_sort = (bool *) palloc(sizeof(bool) * nkeycolumns); info->nulls_first = (bool *) palloc(sizeof(bool) * nkeycolumns); for (i = 0; i < nkeycolumns; i++) { int16 opt = indexRelation->rd_indoption[i]; info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0; info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0; } } else if (amroutine->amcanorder) { /* * Otherwise, identify the corresponding btree opfamilies by * trying to map this index's "<" operators into btree. Since * "<" uniquely defines the behavior of a sort order, this is * a sufficient test. * * XXX This method is rather slow and also requires the * undesirable assumption that the other index AM numbers its * strategies the same as btree. It'd be better to have a way * to explicitly declare the corresponding btree opfamily for * each opfamily of the other index type. But given the lack * of current or foreseeable amcanorder index types, it's not * worth expending more effort on now. */ info->sortopfamily = (Oid *) palloc(sizeof(Oid) * nkeycolumns); info->reverse_sort = (bool *) palloc(sizeof(bool) * nkeycolumns); info->nulls_first = (bool *) palloc(sizeof(bool) * nkeycolumns); for (i = 0; i < nkeycolumns; i++) { int16 opt = indexRelation->rd_indoption[i]; Oid ltopr; Oid btopfamily; Oid btopcintype; int16 btstrategy; info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0; info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0; ltopr = get_opfamily_member(info->opfamily[i], info->opcintype[i], info->opcintype[i], BTLessStrategyNumber); if (OidIsValid(ltopr) && get_ordering_op_properties(ltopr, &btopfamily, &btopcintype, &btstrategy) && btopcintype == info->opcintype[i] && btstrategy == BTLessStrategyNumber) { /* Successful mapping */ info->sortopfamily[i] = btopfamily; } else { /* Fail ... quietly treat index as unordered */ info->sortopfamily = NULL; info->reverse_sort = NULL; info->nulls_first = NULL; break; } } } else { info->sortopfamily = NULL; info->reverse_sort = NULL; info->nulls_first = NULL; } /* * Fetch the index expressions and predicate, if any. We must * modify the copies we obtain from the relcache to have the * correct varno for the parent relation, so that they match up * correctly against qual clauses. */ info->indexprs = RelationGetIndexExpressions(indexRelation); info->indpred = RelationGetIndexPredicate(indexRelation); if (info->indexprs && varno != 1) ChangeVarNodes((Node *) info->indexprs, 1, varno, 0); if (info->indpred && varno != 1) ChangeVarNodes((Node *) info->indpred, 1, varno, 0); /* Build targetlist using the completed indexprs data */ info->indextlist = build_index_tlist(root, info, relation); info->indrestrictinfo = NIL; /* set later, in indxpath.c */ info->predOK = false; /* set later, in indxpath.c */ info->unique = index->indisunique; info->immediate = index->indimmediate; info->hypothetical = false; /* * Estimate the index size. If it's not a partial index, we lock * the number-of-tuples estimate to equal the parent table; if it * is partial then we have to use the same methods as we would for * a table, except we can be sure that the index is not larger * than the table. */ if (info->indpred == NIL) { info->pages = RelationGetNumberOfBlocks(indexRelation); info->tuples = rel->tuples; } else { double allvisfrac; /* dummy */ estimate_rel_size(indexRelation, NULL, &info->pages, &info->tuples, &allvisfrac); if (info->tuples > rel->tuples) info->tuples = rel->tuples; } if (info->relam == BTREE_AM_OID) { /* For btrees, get tree height while we have the index open */ info->tree_height = _bt_getrootheight(indexRelation); } else { /* For other index types, just set it to "unknown" for now */ info->tree_height = -1; } index_close(indexRelation, NoLock); /* * We've historically used lcons() here. It'd make more sense to * use lappend(), but that causes the planner to change behavior * in cases where two indexes seem equally attractive. For now, * stick with lcons() --- few tables should have so many indexes * that the O(N^2) behavior of lcons() is really a problem. */ indexinfos = lcons(info, indexinfos); } list_free(indexoidlist); } rel->indexlist = indexinfos; rel->statlist = get_relation_statistics(rel, relation); /* Grab foreign-table info using the relcache, while we have it */ if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) { rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation)); rel->fdwroutine = GetFdwRoutineForRelation(relation, true); } else { rel->serverid = InvalidOid; rel->fdwroutine = NULL; } /* Collect info about relation's foreign keys, if relevant */ get_relation_foreign_keys(root, rel, relation, inhparent); /* Collect info about functions implemented by the rel's table AM. */ if (relation->rd_tableam && relation->rd_tableam->scan_set_tidrange != NULL && relation->rd_tableam->scan_getnextslot_tidrange != NULL) rel->amflags |= AMFLAG_HAS_TID_RANGE; /* * Collect info about relation's partitioning scheme, if any. Only * inheritance parents may be partitioned. */ if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) set_relation_partition_info(root, rel, relation); table_close(relation, NoLock); /* * Allow a plugin to editorialize on the info we obtained from the * catalogs. Actions might include altering the assumed relation size, * removing an index, or adding a hypothetical index to the indexlist. */ if (get_relation_info_hook) (*get_relation_info_hook) (root, relationObjectId, inhparent, rel); } /* * get_relation_foreign_keys - * Retrieves foreign key information for a given relation. * * ForeignKeyOptInfos for relevant foreign keys are created and added to * root->fkey_list. We do this now while we have the relcache entry open. * We could sometimes avoid making useless ForeignKeyOptInfos if we waited * until all RelOptInfos have been built, but the cost of re-opening the * relcache entries would probably exceed any savings. */ static void get_relation_foreign_keys(PlannerInfo *root, RelOptInfo *rel, Relation relation, bool inhparent) { List *rtable = root->parse->rtable; List *cachedfkeys; ListCell *lc; /* * If it's not a baserel, we don't care about its FKs. Also, if the query * references only a single relation, we can skip the lookup since no FKs * could satisfy the requirements below. */ if (rel->reloptkind != RELOPT_BASEREL || list_length(rtable) < 2) return; /* * If it's the parent of an inheritance tree, ignore its FKs. We could * make useful FK-based deductions if we found that all members of the * inheritance tree have equivalent FK constraints, but detecting that * would require code that hasn't been written. */ if (inhparent) return; /* * Extract data about relation's FKs from the relcache. Note that this * list belongs to the relcache and might disappear in a cache flush, so * we must not do any further catalog access within this function. */ cachedfkeys = RelationGetFKeyList(relation); /* * Figure out which FKs are of interest for this query, and create * ForeignKeyOptInfos for them. We want only FKs that reference some * other RTE of the current query. In queries containing self-joins, * there might be more than one other RTE for a referenced table, and we * should make a ForeignKeyOptInfo for each occurrence. * * Ideally, we would ignore RTEs that correspond to non-baserels, but it's * too hard to identify those here, so we might end up making some useless * ForeignKeyOptInfos. If so, match_foreign_keys_to_quals() will remove * them again. */ foreach(lc, cachedfkeys) { ForeignKeyCacheInfo *cachedfk = (ForeignKeyCacheInfo *) lfirst(lc); Index rti; ListCell *lc2; /* conrelid should always be that of the table we're considering */ Assert(cachedfk->conrelid == RelationGetRelid(relation)); /* Scan to find other RTEs matching confrelid */ rti = 0; foreach(lc2, rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2); ForeignKeyOptInfo *info; rti++; /* Ignore if not the correct table */ if (rte->rtekind != RTE_RELATION || rte->relid != cachedfk->confrelid) continue; /* Ignore if it's an inheritance parent; doesn't really match */ if (rte->inh) continue; /* Ignore self-referential FKs; we only care about joins */ if (rti == rel->relid) continue; /* OK, let's make an entry */ info = makeNode(ForeignKeyOptInfo); info->con_relid = rel->relid; info->ref_relid = rti; info->nkeys = cachedfk->nkeys; memcpy(info->conkey, cachedfk->conkey, sizeof(info->conkey)); memcpy(info->confkey, cachedfk->confkey, sizeof(info->confkey)); memcpy(info->conpfeqop, cachedfk->conpfeqop, sizeof(info->conpfeqop)); /* zero out fields to be filled by match_foreign_keys_to_quals */ info->nmatched_ec = 0; info->nconst_ec = 0; info->nmatched_rcols = 0; info->nmatched_ri = 0; memset(info->eclass, 0, sizeof(info->eclass)); memset(info->fk_eclass_member, 0, sizeof(info->fk_eclass_member)); memset(info->rinfos, 0, sizeof(info->rinfos)); root->fkey_list = lappend(root->fkey_list, info); } } } /* * infer_arbiter_indexes - * Determine the unique indexes used to arbitrate speculative insertion. * * Uses user-supplied inference clause expressions and predicate to match a * unique index from those defined and ready on the heap relation (target). * An exact match is required on columns/expressions (although they can appear * in any order). However, the predicate given by the user need only restrict * insertion to a subset of some part of the table covered by some particular * unique index (in particular, a partial unique index) in order to be * inferred. * * The implementation does not consider which B-Tree operator class any * particular available unique index attribute uses, unless one was specified * in the inference specification. The same is true of collations. In * particular, there is no system dependency on the default operator class for * the purposes of inference. If no opclass (or collation) is specified, then * all matching indexes (that may or may not match the default in terms of * each attribute opclass/collation) are used for inference. */ List * infer_arbiter_indexes(PlannerInfo *root) { OnConflictExpr *onconflict = root->parse->onConflict; /* Iteration state */ RangeTblEntry *rte; Relation relation; Oid indexOidFromConstraint = InvalidOid; List *indexList; ListCell *l; /* Normalized inference attributes and inference expressions: */ Bitmapset *inferAttrs = NULL; List *inferElems = NIL; /* Results */ List *results = NIL; /* * Quickly return NIL for ON CONFLICT DO NOTHING without an inference * specification or named constraint. ON CONFLICT DO UPDATE statements * must always provide one or the other (but parser ought to have caught * that already). */ if (onconflict->arbiterElems == NIL && onconflict->constraint == InvalidOid) return NIL; /* * We need not lock the relation since it was already locked, either by * the rewriter or when expand_inherited_rtentry() added it to the query's * rangetable. */ rte = rt_fetch(root->parse->resultRelation, root->parse->rtable); relation = table_open(rte->relid, NoLock); /* * Build normalized/BMS representation of plain indexed attributes, as * well as a separate list of expression items. This simplifies matching * the cataloged definition of indexes. */ foreach(l, onconflict->arbiterElems) { InferenceElem *elem = (InferenceElem *) lfirst(l); Var *var; int attno; if (!IsA(elem->expr, Var)) { /* If not a plain Var, just shove it in inferElems for now */ inferElems = lappend(inferElems, elem->expr); continue; } var = (Var *) elem->expr; attno = var->varattno; if (attno == 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("whole row unique index inference specifications are not supported"))); inferAttrs = bms_add_member(inferAttrs, attno - FirstLowInvalidHeapAttributeNumber); } /* * Lookup named constraint's index. This is not immediately returned * because some additional sanity checks are required. */ if (onconflict->constraint != InvalidOid) { indexOidFromConstraint = get_constraint_index(onconflict->constraint); if (indexOidFromConstraint == InvalidOid) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("constraint in ON CONFLICT clause has no associated index"))); } /* * Using that representation, iterate through the list of indexes on the * target relation to try and find a match */ indexList = RelationGetIndexList(relation); foreach(l, indexList) { Oid indexoid = lfirst_oid(l); Relation idxRel; Form_pg_index idxForm; Bitmapset *indexedAttrs; List *idxExprs; List *predExprs; AttrNumber natt; ListCell *el; /* * Extract info from the relation descriptor for the index. Obtain * the same lock type that the executor will ultimately use. * * Let executor complain about !indimmediate case directly, because * enforcement needs to occur there anyway when an inference clause is * omitted. */ idxRel = index_open(indexoid, rte->rellockmode); idxForm = idxRel->rd_index; if (!idxForm->indisvalid) goto next; /* * Note that we do not perform a check against indcheckxmin (like e.g. * get_relation_info()) here to eliminate candidates, because * uniqueness checking only cares about the most recently committed * tuple versions. */ /* * Look for match on "ON constraint_name" variant, which may not be * unique constraint. This can only be a constraint name. */ if (indexOidFromConstraint == idxForm->indexrelid) { if (!idxForm->indisunique && onconflict->action == ONCONFLICT_UPDATE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("ON CONFLICT DO UPDATE not supported with exclusion constraints"))); results = lappend_oid(results, idxForm->indexrelid); list_free(indexList); index_close(idxRel, NoLock); table_close(relation, NoLock); return results; } else if (indexOidFromConstraint != InvalidOid) { /* No point in further work for index in named constraint case */ goto next; } /* * Only considering conventional inference at this point (not named * constraints), so index under consideration can be immediately * skipped if it's not unique */ if (!idxForm->indisunique) goto next; /* Build BMS representation of plain (non expression) index attrs */ indexedAttrs = NULL; for (natt = 0; natt < idxForm->indnkeyatts; natt++) { int attno = idxRel->rd_index->indkey.values[natt]; if (attno != 0) indexedAttrs = bms_add_member(indexedAttrs, attno - FirstLowInvalidHeapAttributeNumber); } /* Non-expression attributes (if any) must match */ if (!bms_equal(indexedAttrs, inferAttrs)) goto next; /* Expression attributes (if any) must match */ idxExprs = RelationGetIndexExpressions(idxRel); foreach(el, onconflict->arbiterElems) { InferenceElem *elem = (InferenceElem *) lfirst(el); /* * Ensure that collation/opclass aspects of inference expression * element match. Even though this loop is primarily concerned * with matching expressions, it is a convenient point to check * this for both expressions and ordinary (non-expression) * attributes appearing as inference elements. */ if (!infer_collation_opclass_match(elem, idxRel, idxExprs)) goto next; /* * Plain Vars don't factor into count of expression elements, and * the question of whether or not they satisfy the index * definition has already been considered (they must). */ if (IsA(elem->expr, Var)) continue; /* * Might as well avoid redundant check in the rare cases where * infer_collation_opclass_match() is required to do real work. * Otherwise, check that element expression appears in cataloged * index definition. */ if (elem->infercollid != InvalidOid || elem->inferopclass != InvalidOid || list_member(idxExprs, elem->expr)) continue; goto next; } /* * Now that all inference elements were matched, ensure that the * expression elements from inference clause are not missing any * cataloged expressions. This does the right thing when unique * indexes redundantly repeat the same attribute, or if attributes * redundantly appear multiple times within an inference clause. */ if (list_difference(idxExprs, inferElems) != NIL) goto next; /* * If it's a partial index, its predicate must be implied by the ON * CONFLICT's WHERE clause. */ predExprs = RelationGetIndexPredicate(idxRel); if (!predicate_implied_by(predExprs, (List *) onconflict->arbiterWhere, false)) goto next; results = lappend_oid(results, idxForm->indexrelid); next: index_close(idxRel, NoLock); } list_free(indexList); table_close(relation, NoLock); if (results == NIL) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("there is no unique or exclusion constraint matching the ON CONFLICT specification"))); return results; } /* * infer_collation_opclass_match - ensure infer element opclass/collation match * * Given unique index inference element from inference specification, if * collation was specified, or if opclass was specified, verify that there is * at least one matching indexed attribute (occasionally, there may be more). * Skip this in the common case where inference specification does not include * collation or opclass (instead matching everything, regardless of cataloged * collation/opclass of indexed attribute). * * At least historically, Postgres has not offered collations or opclasses * with alternative-to-default notions of equality, so these additional * criteria should only be required infrequently. * * Don't give up immediately when an inference element matches some attribute * cataloged as indexed but not matching additional opclass/collation * criteria. This is done so that the implementation is as forgiving as * possible of redundancy within cataloged index attributes (or, less * usefully, within inference specification elements). If collations actually * differ between apparently redundantly indexed attributes (redundant within * or across indexes), then there really is no redundancy as such. * * Note that if an inference element specifies an opclass and a collation at * once, both must match in at least one particular attribute within index * catalog definition in order for that inference element to be considered * inferred/satisfied. */ static bool infer_collation_opclass_match(InferenceElem *elem, Relation idxRel, List *idxExprs) { AttrNumber natt; Oid inferopfamily = InvalidOid; /* OID of opclass opfamily */ Oid inferopcinputtype = InvalidOid; /* OID of opclass input type */ int nplain = 0; /* # plain attrs observed */ /* * If inference specification element lacks collation/opclass, then no * need to check for exact match. */ if (elem->infercollid == InvalidOid && elem->inferopclass == InvalidOid) return true; /* * Lookup opfamily and input type, for matching indexes */ if (elem->inferopclass) { inferopfamily = get_opclass_family(elem->inferopclass); inferopcinputtype = get_opclass_input_type(elem->inferopclass); } for (natt = 1; natt <= idxRel->rd_att->natts; natt++) { Oid opfamily = idxRel->rd_opfamily[natt - 1]; Oid opcinputtype = idxRel->rd_opcintype[natt - 1]; Oid collation = idxRel->rd_indcollation[natt - 1]; int attno = idxRel->rd_index->indkey.values[natt - 1]; if (attno != 0) nplain++; if (elem->inferopclass != InvalidOid && (inferopfamily != opfamily || inferopcinputtype != opcinputtype)) { /* Attribute needed to match opclass, but didn't */ continue; } if (elem->infercollid != InvalidOid && elem->infercollid != collation) { /* Attribute needed to match collation, but didn't */ continue; } /* If one matching index att found, good enough -- return true */ if (IsA(elem->expr, Var)) { if (((Var *) elem->expr)->varattno == attno) return true; } else if (attno == 0) { Node *nattExpr = list_nth(idxExprs, (natt - 1) - nplain); /* * Note that unlike routines like match_index_to_operand() we * don't need to care about RelabelType. Neither the index * definition nor the inference clause should contain them. */ if (equal(elem->expr, nattExpr)) return true; } } return false; } /* * estimate_rel_size - estimate # pages and # tuples in a table or index * * We also estimate the fraction of the pages that are marked all-visible in * the visibility map, for use in estimation of index-only scans. * * If attr_widths isn't NULL, it points to the zero-index entry of the * relation's attr_widths[] cache; we fill this in if we have need to compute * the attribute widths for estimation purposes. */ void estimate_rel_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac) { BlockNumber curpages; BlockNumber relpages; double reltuples; BlockNumber relallvisible; double density; if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind)) { table_relation_estimate_size(rel, attr_widths, pages, tuples, allvisfrac); } else if (rel->rd_rel->relkind == RELKIND_INDEX) { /* * XXX: It'd probably be good to move this into a callback, individual * index types e.g. know if they have a metapage. */ /* it has storage, ok to call the smgr */ curpages = RelationGetNumberOfBlocks(rel); /* report estimated # pages */ *pages = curpages; /* quick exit if rel is clearly empty */ if (curpages == 0) { *tuples = 0; *allvisfrac = 0; return; } /* coerce values in pg_class to more desirable types */ relpages = (BlockNumber) rel->rd_rel->relpages; reltuples = (double) rel->rd_rel->reltuples; relallvisible = (BlockNumber) rel->rd_rel->relallvisible; /* * Discount the metapage while estimating the number of tuples. This * is a kluge because it assumes more than it ought to about index * structure. Currently it's OK for btree, hash, and GIN indexes but * suspect for GiST indexes. */ if (relpages > 0) { curpages--; relpages--; } /* estimate number of tuples from previous tuple density */ if (reltuples >= 0 && relpages > 0) density = reltuples / (double) relpages; else { /* * If we have no data because the relation was never vacuumed, * estimate tuple width from attribute datatypes. We assume here * that the pages are completely full, which is OK for tables * (since they've presumably not been VACUUMed yet) but is * probably an overestimate for indexes. Fortunately * get_relation_info() can clamp the overestimate to the parent * table's size. * * Note: this code intentionally disregards alignment * considerations, because (a) that would be gilding the lily * considering how crude the estimate is, and (b) it creates * platform dependencies in the default plans which are kind of a * headache for regression testing. * * XXX: Should this logic be more index specific? */ int32 tuple_width; tuple_width = get_rel_data_width(rel, attr_widths); tuple_width += MAXALIGN(SizeofHeapTupleHeader); tuple_width += sizeof(ItemIdData); /* note: integer division is intentional here */ density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width; } *tuples = rint(density * (double) curpages); /* * We use relallvisible as-is, rather than scaling it up like we do * for the pages and tuples counts, on the theory that any pages added * since the last VACUUM are most likely not marked all-visible. But * costsize.c wants it converted to a fraction. */ if (relallvisible == 0 || curpages <= 0) *allvisfrac = 0; else if ((double) relallvisible >= curpages) *allvisfrac = 1; else *allvisfrac = (double) relallvisible / curpages; } else { /* * Just use whatever's in pg_class. This covers foreign tables, * sequences, and also relkinds without storage (shouldn't get here?); * see initializations in AddNewRelationTuple(). Note that FDW must * cope if reltuples is -1! */ *pages = rel->rd_rel->relpages; *tuples = rel->rd_rel->reltuples; *allvisfrac = 0; } } /* * get_rel_data_width * * Estimate the average width of (the data part of) the relation's tuples. * * If attr_widths isn't NULL, it points to the zero-index entry of the * relation's attr_widths[] cache; use and update that cache as appropriate. * * Currently we ignore dropped columns. Ideally those should be included * in the result, but we haven't got any way to get info about them; and * since they might be mostly NULLs, treating them as zero-width is not * necessarily the wrong thing anyway. */ int32 get_rel_data_width(Relation rel, int32 *attr_widths) { int32 tuple_width = 0; int i; for (i = 1; i <= RelationGetNumberOfAttributes(rel); i++) { Form_pg_attribute att = TupleDescAttr(rel->rd_att, i - 1); int32 item_width; if (att->attisdropped) continue; /* use previously cached data, if any */ if (attr_widths != NULL && attr_widths[i] > 0) { tuple_width += attr_widths[i]; continue; } /* This should match set_rel_width() in costsize.c */ item_width = get_attavgwidth(RelationGetRelid(rel), i); if (item_width <= 0) { item_width = get_typavgwidth(att->atttypid, att->atttypmod); Assert(item_width > 0); } if (attr_widths != NULL) attr_widths[i] = item_width; tuple_width += item_width; } return tuple_width; } /* * get_relation_data_width * * External API for get_rel_data_width: same behavior except we have to * open the relcache entry. */ int32 get_relation_data_width(Oid relid, int32 *attr_widths) { int32 result; Relation relation; /* As above, assume relation is already locked */ relation = table_open(relid, NoLock); result = get_rel_data_width(relation, attr_widths); table_close(relation, NoLock); return result; } /* * get_relation_constraints * * Retrieve the applicable constraint expressions of the given relation. * * Returns a List (possibly empty) of constraint expressions. Each one * has been canonicalized, and its Vars are changed to have the varno * indicated by rel->relid. This allows the expressions to be easily * compared to expressions taken from WHERE. * * If include_noinherit is true, it's okay to include constraints that * are marked NO INHERIT. * * If include_notnull is true, "col IS NOT NULL" expressions are generated * and added to the result for each column that's marked attnotnull. * * If include_partition is true, and the relation is a partition, * also include the partitioning constraints. * * Note: at present this is invoked at most once per relation per planner * run, and in many cases it won't be invoked at all, so there seems no * point in caching the data in RelOptInfo. */ static List * get_relation_constraints(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel, bool include_noinherit, bool include_notnull, bool include_partition) { List *result = NIL; Index varno = rel->relid; Relation relation; TupleConstr *constr; /* * We assume the relation has already been safely locked. */ relation = table_open(relationObjectId, NoLock); constr = relation->rd_att->constr; if (constr != NULL) { int num_check = constr->num_check; int i; for (i = 0; i < num_check; i++) { Node *cexpr; /* * If this constraint hasn't been fully validated yet, we must * ignore it here. Also ignore if NO INHERIT and we weren't told * that that's safe. */ if (!constr->check[i].ccvalid) continue; if (constr->check[i].ccnoinherit && !include_noinherit) continue; cexpr = stringToNode(constr->check[i].ccbin); /* * Run each expression through const-simplification and * canonicalization. This is not just an optimization, but is * necessary, because we will be comparing it to * similarly-processed qual clauses, and may fail to detect valid * matches without this. This must match the processing done to * qual clauses in preprocess_expression()! (We can skip the * stuff involving subqueries, however, since we don't allow any * in check constraints.) */ cexpr = eval_const_expressions(root, cexpr); cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true); /* Fix Vars to have the desired varno */ if (varno != 1) ChangeVarNodes(cexpr, 1, varno, 0); /* * Finally, convert to implicit-AND format (that is, a List) and * append the resulting item(s) to our output list. */ result = list_concat(result, make_ands_implicit((Expr *) cexpr)); } /* Add NOT NULL constraints in expression form, if requested */ if (include_notnull && constr->has_not_null) { int natts = relation->rd_att->natts; for (i = 1; i <= natts; i++) { Form_pg_attribute att = TupleDescAttr(relation->rd_att, i - 1); if (att->attnotnull && !att->attisdropped) { NullTest *ntest = makeNode(NullTest); ntest->arg = (Expr *) makeVar(varno, i, att->atttypid, att->atttypmod, att->attcollation, 0); ntest->nulltesttype = IS_NOT_NULL; /* * argisrow=false is correct even for a composite column, * because attnotnull does not represent a SQL-spec IS NOT * NULL test in such a case, just IS DISTINCT FROM NULL. */ ntest->argisrow = false; ntest->location = -1; result = lappend(result, ntest); } } } } /* * Add partitioning constraints, if requested. */ if (include_partition && relation->rd_rel->relispartition) { /* make sure rel->partition_qual is set */ set_baserel_partition_constraint(relation, rel); result = list_concat(result, rel->partition_qual); } table_close(relation, NoLock); return result; } /* * Try loading data for the statistics object. * * We don't know if the data (specified by statOid and inh value) exist. * The result is stored in stainfos list. */ static void get_relation_statistics_worker(List **stainfos, RelOptInfo *rel, Oid statOid, bool inh, Bitmapset *keys, List *exprs) { Form_pg_statistic_ext_data dataForm; HeapTuple dtup; dtup = SearchSysCache2(STATEXTDATASTXOID, ObjectIdGetDatum(statOid), BoolGetDatum(inh)); if (!HeapTupleIsValid(dtup)) return; dataForm = (Form_pg_statistic_ext_data) GETSTRUCT(dtup); /* add one StatisticExtInfo for each kind built */ if (statext_is_kind_built(dtup, STATS_EXT_NDISTINCT)) { StatisticExtInfo *info = makeNode(StatisticExtInfo); info->statOid = statOid; info->inherit = dataForm->stxdinherit; info->rel = rel; info->kind = STATS_EXT_NDISTINCT; info->keys = bms_copy(keys); info->exprs = exprs; *stainfos = lappend(*stainfos, info); } if (statext_is_kind_built(dtup, STATS_EXT_DEPENDENCIES)) { StatisticExtInfo *info = makeNode(StatisticExtInfo); info->statOid = statOid; info->inherit = dataForm->stxdinherit; info->rel = rel; info->kind = STATS_EXT_DEPENDENCIES; info->keys = bms_copy(keys); info->exprs = exprs; *stainfos = lappend(*stainfos, info); } if (statext_is_kind_built(dtup, STATS_EXT_MCV)) { StatisticExtInfo *info = makeNode(StatisticExtInfo); info->statOid = statOid; info->inherit = dataForm->stxdinherit; info->rel = rel; info->kind = STATS_EXT_MCV; info->keys = bms_copy(keys); info->exprs = exprs; *stainfos = lappend(*stainfos, info); } if (statext_is_kind_built(dtup, STATS_EXT_EXPRESSIONS)) { StatisticExtInfo *info = makeNode(StatisticExtInfo); info->statOid = statOid; info->inherit = dataForm->stxdinherit; info->rel = rel; info->kind = STATS_EXT_EXPRESSIONS; info->keys = bms_copy(keys); info->exprs = exprs; *stainfos = lappend(*stainfos, info); } ReleaseSysCache(dtup); } /* * get_relation_statistics * Retrieve extended statistics defined on the table. * * Returns a List (possibly empty) of StatisticExtInfo objects describing * the statistics. Note that this doesn't load the actual statistics data, * just the identifying metadata. Only stats actually built are considered. */ static List * get_relation_statistics(RelOptInfo *rel, Relation relation) { Index varno = rel->relid; List *statoidlist; List *stainfos = NIL; ListCell *l; statoidlist = RelationGetStatExtList(relation); foreach(l, statoidlist) { Oid statOid = lfirst_oid(l); Form_pg_statistic_ext staForm; HeapTuple htup; Bitmapset *keys = NULL; List *exprs = NIL; int i; htup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statOid)); if (!HeapTupleIsValid(htup)) elog(ERROR, "cache lookup failed for statistics object %u", statOid); staForm = (Form_pg_statistic_ext) GETSTRUCT(htup); /* * First, build the array of columns covered. This is ultimately * wasted if no stats within the object have actually been built, but * it doesn't seem worth troubling over that case. */ for (i = 0; i < staForm->stxkeys.dim1; i++) keys = bms_add_member(keys, staForm->stxkeys.values[i]); /* * Preprocess expressions (if any). We read the expressions, run them * through eval_const_expressions, and fix the varnos. * * XXX We don't know yet if there are any data for this stats object, * with either stxdinherit value. But it's reasonable to assume there * is at least one of those, possibly both. So it's better to process * keys and expressions here. */ { bool isnull; Datum datum; /* decode expression (if any) */ datum = SysCacheGetAttr(STATEXTOID, htup, Anum_pg_statistic_ext_stxexprs, &isnull); if (!isnull) { char *exprsString; exprsString = TextDatumGetCString(datum); exprs = (List *) stringToNode(exprsString); pfree(exprsString); /* * Run the expressions through eval_const_expressions. This is * not just an optimization, but is necessary, because the * planner will be comparing them to similarly-processed qual * clauses, and may fail to detect valid matches without this. * We must not use canonicalize_qual, however, since these * aren't qual expressions. */ exprs = (List *) eval_const_expressions(NULL, (Node *) exprs); /* May as well fix opfuncids too */ fix_opfuncids((Node *) exprs); /* * Modify the copies we obtain from the relcache to have the * correct varno for the parent relation, so that they match * up correctly against qual clauses. */ if (varno != 1) ChangeVarNodes((Node *) exprs, 1, varno, 0); } } /* extract statistics for possible values of stxdinherit flag */ get_relation_statistics_worker(&stainfos, rel, statOid, true, keys, exprs); get_relation_statistics_worker(&stainfos, rel, statOid, false, keys, exprs); ReleaseSysCache(htup); bms_free(keys); } list_free(statoidlist); return stainfos; } /* * relation_excluded_by_constraints * * Detect whether the relation need not be scanned because it has either * self-inconsistent restrictions, or restrictions inconsistent with the * relation's applicable constraints. * * Note: this examines only rel->relid, rel->reloptkind, and * rel->baserestrictinfo; therefore it can be called before filling in * other fields of the RelOptInfo. */ bool relation_excluded_by_constraints(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { bool include_noinherit; bool include_notnull; bool include_partition = false; List *safe_restrictions; List *constraint_pred; List *safe_constraints; ListCell *lc; /* As of now, constraint exclusion works only with simple relations. */ Assert(IS_SIMPLE_REL(rel)); /* * If there are no base restriction clauses, we have no hope of proving * anything below, so fall out quickly. */ if (rel->baserestrictinfo == NIL) return false; /* * Regardless of the setting of constraint_exclusion, detect * constant-FALSE-or-NULL restriction clauses. Because const-folding will * reduce "anything AND FALSE" to just "FALSE", any such case should * result in exactly one baserestrictinfo entry. This doesn't fire very * often, but it seems cheap enough to be worth doing anyway. (Without * this, we'd miss some optimizations that 9.5 and earlier found via much * more roundabout methods.) */ if (list_length(rel->baserestrictinfo) == 1) { RestrictInfo *rinfo = (RestrictInfo *) linitial(rel->baserestrictinfo); Expr *clause = rinfo->clause; if (clause && IsA(clause, Const) && (((Const *) clause)->constisnull || !DatumGetBool(((Const *) clause)->constvalue))) return true; } /* * Skip further tests, depending on constraint_exclusion. */ switch (constraint_exclusion) { case CONSTRAINT_EXCLUSION_OFF: /* In 'off' mode, never make any further tests */ return false; case CONSTRAINT_EXCLUSION_PARTITION: /* * When constraint_exclusion is set to 'partition' we only handle * appendrel members. Partition pruning has already been applied, * so there is no need to consider the rel's partition constraints * here. */ if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL) break; /* appendrel member, so process it */ return false; case CONSTRAINT_EXCLUSION_ON: /* * In 'on' mode, always apply constraint exclusion. If we are * considering a baserel that is a partition (i.e., it was * directly named rather than expanded from a parent table), then * its partition constraints haven't been considered yet, so * include them in the processing here. */ if (rel->reloptkind == RELOPT_BASEREL) include_partition = true; break; /* always try to exclude */ } /* * Check for self-contradictory restriction clauses. We dare not make * deductions with non-immutable functions, but any immutable clauses that * are self-contradictory allow us to conclude the scan is unnecessary. * * Note: strip off RestrictInfo because predicate_refuted_by() isn't * expecting to see any in its predicate argument. */ safe_restrictions = NIL; foreach(lc, rel->baserestrictinfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); if (!contain_mutable_functions((Node *) rinfo->clause)) safe_restrictions = lappend(safe_restrictions, rinfo->clause); } /* * We can use weak refutation here, since we're comparing restriction * clauses with restriction clauses. */ if (predicate_refuted_by(safe_restrictions, safe_restrictions, true)) return true; /* * Only plain relations have constraints, so stop here for other rtekinds. */ if (rte->rtekind != RTE_RELATION) return false; /* * If we are scanning just this table, we can use NO INHERIT constraints, * but not if we're scanning its children too. (Note that partitioned * tables should never have NO INHERIT constraints; but it's not necessary * for us to assume that here.) */ include_noinherit = !rte->inh; /* * Currently, attnotnull constraints must be treated as NO INHERIT unless * this is a partitioned table. In future we might track their * inheritance status more accurately, allowing this to be refined. */ include_notnull = (!rte->inh || rte->relkind == RELKIND_PARTITIONED_TABLE); /* * Fetch the appropriate set of constraint expressions. */ constraint_pred = get_relation_constraints(root, rte->relid, rel, include_noinherit, include_notnull, include_partition); /* * We do not currently enforce that CHECK constraints contain only * immutable functions, so it's necessary to check here. We daren't draw * conclusions from plan-time evaluation of non-immutable functions. Since * they're ANDed, we can just ignore any mutable constraints in the list, * and reason about the rest. */ safe_constraints = NIL; foreach(lc, constraint_pred) { Node *pred = (Node *) lfirst(lc); if (!contain_mutable_functions(pred)) safe_constraints = lappend(safe_constraints, pred); } /* * The constraints are effectively ANDed together, so we can just try to * refute the entire collection at once. This may allow us to make proofs * that would fail if we took them individually. * * Note: we use rel->baserestrictinfo, not safe_restrictions as might seem * an obvious optimization. Some of the clauses might be OR clauses that * have volatile and nonvolatile subclauses, and it's OK to make * deductions with the nonvolatile parts. * * We need strong refutation because we have to prove that the constraints * would yield false, not just NULL. */ if (predicate_refuted_by(safe_constraints, rel->baserestrictinfo, false)) return true; return false; } /* * build_physical_tlist * * Build a targetlist consisting of exactly the relation's user attributes, * in order. The executor can special-case such tlists to avoid a projection * step at runtime, so we use such tlists preferentially for scan nodes. * * Exception: if there are any dropped or missing columns, we punt and return * NIL. Ideally we would like to handle these cases too. However this * creates problems for ExecTypeFromTL, which may be asked to build a tupdesc * for a tlist that includes vars of no-longer-existent types. In theory we * could dig out the required info from the pg_attribute entries of the * relation, but that data is not readily available to ExecTypeFromTL. * For now, we don't apply the physical-tlist optimization when there are * dropped cols. * * We also support building a "physical" tlist for subqueries, functions, * values lists, table expressions, and CTEs, since the same optimization can * occur in SubqueryScan, FunctionScan, ValuesScan, CteScan, TableFunc, * NamedTuplestoreScan, and WorkTableScan nodes. */ List * build_physical_tlist(PlannerInfo *root, RelOptInfo *rel) { List *tlist = NIL; Index varno = rel->relid; RangeTblEntry *rte = planner_rt_fetch(varno, root); Relation relation; Query *subquery; Var *var; ListCell *l; int attrno, numattrs; List *colvars; switch (rte->rtekind) { case RTE_RELATION: /* Assume we already have adequate lock */ relation = table_open(rte->relid, NoLock); numattrs = RelationGetNumberOfAttributes(relation); for (attrno = 1; attrno <= numattrs; attrno++) { Form_pg_attribute att_tup = TupleDescAttr(relation->rd_att, attrno - 1); if (att_tup->attisdropped || att_tup->atthasmissing) { /* found a dropped or missing col, so punt */ tlist = NIL; break; } var = makeVar(varno, attrno, att_tup->atttypid, att_tup->atttypmod, att_tup->attcollation, 0); tlist = lappend(tlist, makeTargetEntry((Expr *) var, attrno, NULL, false)); } table_close(relation, NoLock); break; case RTE_SUBQUERY: subquery = rte->subquery; foreach(l, subquery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); /* * A resjunk column of the subquery can be reflected as * resjunk in the physical tlist; we need not punt. */ var = makeVarFromTargetEntry(varno, tle); tlist = lappend(tlist, makeTargetEntry((Expr *) var, tle->resno, NULL, tle->resjunk)); } break; case RTE_FUNCTION: case RTE_TABLEFUNC: case RTE_VALUES: case RTE_CTE: case RTE_NAMEDTUPLESTORE: case RTE_RESULT: /* Not all of these can have dropped cols, but share code anyway */ expandRTE(rte, varno, 0, -1, true /* include dropped */ , NULL, &colvars); foreach(l, colvars) { var = (Var *) lfirst(l); /* * A non-Var in expandRTE's output means a dropped column; * must punt. */ if (!IsA(var, Var)) { tlist = NIL; break; } tlist = lappend(tlist, makeTargetEntry((Expr *) var, var->varattno, NULL, false)); } break; default: /* caller error */ elog(ERROR, "unsupported RTE kind %d in build_physical_tlist", (int) rte->rtekind); break; } return tlist; } /* * build_index_tlist * * Build a targetlist representing the columns of the specified index. * Each column is represented by a Var for the corresponding base-relation * column, or an expression in base-relation Vars, as appropriate. * * There are never any dropped columns in indexes, so unlike * build_physical_tlist, we need no failure case. */ static List * build_index_tlist(PlannerInfo *root, IndexOptInfo *index, Relation heapRelation) { List *tlist = NIL; Index varno = index->rel->relid; ListCell *indexpr_item; int i; indexpr_item = list_head(index->indexprs); for (i = 0; i < index->ncolumns; i++) { int indexkey = index->indexkeys[i]; Expr *indexvar; if (indexkey != 0) { /* simple column */ const FormData_pg_attribute *att_tup; if (indexkey < 0) att_tup = SystemAttributeDefinition(indexkey); else att_tup = TupleDescAttr(heapRelation->rd_att, indexkey - 1); indexvar = (Expr *) makeVar(varno, indexkey, att_tup->atttypid, att_tup->atttypmod, att_tup->attcollation, 0); } else { /* expression column */ if (indexpr_item == NULL) elog(ERROR, "wrong number of index expressions"); indexvar = (Expr *) lfirst(indexpr_item); indexpr_item = lnext(index->indexprs, indexpr_item); } tlist = lappend(tlist, makeTargetEntry(indexvar, i + 1, NULL, false)); } if (indexpr_item != NULL) elog(ERROR, "wrong number of index expressions"); return tlist; } /* * restriction_selectivity * * Returns the selectivity of a specified restriction operator clause. * This code executes registered procedures stored in the * operator relation, by calling the function manager. * * See clause_selectivity() for the meaning of the additional parameters. */ Selectivity restriction_selectivity(PlannerInfo *root, Oid operatorid, List *args, Oid inputcollid, int varRelid) { RegProcedure oprrest = get_oprrest(operatorid); float8 result; /* * if the oprrest procedure is missing for whatever reason, use a * selectivity of 0.5 */ if (!oprrest) return (Selectivity) 0.5; result = DatumGetFloat8(OidFunctionCall4Coll(oprrest, inputcollid, PointerGetDatum(root), ObjectIdGetDatum(operatorid), PointerGetDatum(args), Int32GetDatum(varRelid))); if (result < 0.0 || result > 1.0) elog(ERROR, "invalid restriction selectivity: %f", result); return (Selectivity) result; } /* * join_selectivity * * Returns the selectivity of a specified join operator clause. * This code executes registered procedures stored in the * operator relation, by calling the function manager. * * See clause_selectivity() for the meaning of the additional parameters. */ Selectivity join_selectivity(PlannerInfo *root, Oid operatorid, List *args, Oid inputcollid, JoinType jointype, SpecialJoinInfo *sjinfo) { RegProcedure oprjoin = get_oprjoin(operatorid); float8 result; /* * if the oprjoin procedure is missing for whatever reason, use a * selectivity of 0.5 */ if (!oprjoin) return (Selectivity) 0.5; result = DatumGetFloat8(OidFunctionCall5Coll(oprjoin, inputcollid, PointerGetDatum(root), ObjectIdGetDatum(operatorid), PointerGetDatum(args), Int16GetDatum(jointype), PointerGetDatum(sjinfo))); if (result < 0.0 || result > 1.0) elog(ERROR, "invalid join selectivity: %f", result); return (Selectivity) result; } /* * function_selectivity * * Returns the selectivity of a specified boolean function clause. * This code executes registered procedures stored in the * pg_proc relation, by calling the function manager. * * See clause_selectivity() for the meaning of the additional parameters. */ Selectivity function_selectivity(PlannerInfo *root, Oid funcid, List *args, Oid inputcollid, bool is_join, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo) { RegProcedure prosupport = get_func_support(funcid); SupportRequestSelectivity req; SupportRequestSelectivity *sresult; /* * If no support function is provided, use our historical default * estimate, 0.3333333. This seems a pretty unprincipled choice, but * Postgres has been using that estimate for function calls since 1992. * The hoariness of this behavior suggests that we should not be in too * much hurry to use another value. */ if (!prosupport) return (Selectivity) 0.3333333; req.type = T_SupportRequestSelectivity; req.root = root; req.funcid = funcid; req.args = args; req.inputcollid = inputcollid; req.is_join = is_join; req.varRelid = varRelid; req.jointype = jointype; req.sjinfo = sjinfo; req.selectivity = -1; /* to catch failure to set the value */ sresult = (SupportRequestSelectivity *) DatumGetPointer(OidFunctionCall1(prosupport, PointerGetDatum(&req))); /* If support function fails, use default */ if (sresult != &req) return (Selectivity) 0.3333333; if (req.selectivity < 0.0 || req.selectivity > 1.0) elog(ERROR, "invalid function selectivity: %f", req.selectivity); return (Selectivity) req.selectivity; } /* * add_function_cost * * Get an estimate of the execution cost of a function, and *add* it to * the contents of *cost. The estimate may include both one-time and * per-tuple components, since QualCost does. * * The funcid must always be supplied. If it is being called as the * implementation of a specific parsetree node (FuncExpr, OpExpr, * WindowFunc, etc), pass that as "node", else pass NULL. * * In some usages root might be NULL, too. */ void add_function_cost(PlannerInfo *root, Oid funcid, Node *node, QualCost *cost) { HeapTuple proctup; Form_pg_proc procform; proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(proctup); if (OidIsValid(procform->prosupport)) { SupportRequestCost req; SupportRequestCost *sresult; req.type = T_SupportRequestCost; req.root = root; req.funcid = funcid; req.node = node; /* Initialize cost fields so that support function doesn't have to */ req.startup = 0; req.per_tuple = 0; sresult = (SupportRequestCost *) DatumGetPointer(OidFunctionCall1(procform->prosupport, PointerGetDatum(&req))); if (sresult == &req) { /* Success, so accumulate support function's estimate into *cost */ cost->startup += req.startup; cost->per_tuple += req.per_tuple; ReleaseSysCache(proctup); return; } } /* No support function, or it failed, so rely on procost */ cost->per_tuple += procform->procost * cpu_operator_cost; ReleaseSysCache(proctup); } /* * get_function_rows * * Get an estimate of the number of rows returned by a set-returning function. * * The funcid must always be supplied. In current usage, the calling node * will always be supplied, and will be either a FuncExpr or OpExpr. * But it's a good idea to not fail if it's NULL. * * In some usages root might be NULL, too. * * Note: this returns the unfiltered result of the support function, if any. * It's usually a good idea to apply clamp_row_est() to the result, but we * leave it to the caller to do so. */ double get_function_rows(PlannerInfo *root, Oid funcid, Node *node) { HeapTuple proctup; Form_pg_proc procform; double result; proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(proctup); Assert(procform->proretset); /* else caller error */ if (OidIsValid(procform->prosupport)) { SupportRequestRows req; SupportRequestRows *sresult; req.type = T_SupportRequestRows; req.root = root; req.funcid = funcid; req.node = node; req.rows = 0; /* just for sanity */ sresult = (SupportRequestRows *) DatumGetPointer(OidFunctionCall1(procform->prosupport, PointerGetDatum(&req))); if (sresult == &req) { /* Success */ ReleaseSysCache(proctup); return req.rows; } } /* No support function, or it failed, so rely on prorows */ result = procform->prorows; ReleaseSysCache(proctup); return result; } /* * has_unique_index * * Detect whether there is a unique index on the specified attribute * of the specified relation, thus allowing us to conclude that all * the (non-null) values of the attribute are distinct. * * This function does not check the index's indimmediate property, which * means that uniqueness may transiently fail to hold intra-transaction. * That's appropriate when we are making statistical estimates, but beware * of using this for any correctness proofs. */ bool has_unique_index(RelOptInfo *rel, AttrNumber attno) { ListCell *ilist; foreach(ilist, rel->indexlist) { IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); /* * Note: ignore partial indexes, since they don't allow us to conclude * that all attr values are distinct, *unless* they are marked predOK * which means we know the index's predicate is satisfied by the * query. We don't take any interest in expressional indexes either. * Also, a multicolumn unique index doesn't allow us to conclude that * just the specified attr is unique. */ if (index->unique && index->nkeycolumns == 1 && index->indexkeys[0] == attno && (index->indpred == NIL || index->predOK)) return true; } return false; } /* * has_row_triggers * * Detect whether the specified relation has any row-level triggers for event. */ bool has_row_triggers(PlannerInfo *root, Index rti, CmdType event) { RangeTblEntry *rte = planner_rt_fetch(rti, root); Relation relation; TriggerDesc *trigDesc; bool result = false; /* Assume we already have adequate lock */ relation = table_open(rte->relid, NoLock); trigDesc = relation->trigdesc; switch (event) { case CMD_INSERT: if (trigDesc && (trigDesc->trig_insert_after_row || trigDesc->trig_insert_before_row)) result = true; break; case CMD_UPDATE: if (trigDesc && (trigDesc->trig_update_after_row || trigDesc->trig_update_before_row)) result = true; break; case CMD_DELETE: if (trigDesc && (trigDesc->trig_delete_after_row || trigDesc->trig_delete_before_row)) result = true; break; /* There is no separate event for MERGE, only INSERT/UPDATE/DELETE */ case CMD_MERGE: result = false; break; default: elog(ERROR, "unrecognized CmdType: %d", (int) event); break; } table_close(relation, NoLock); return result; } /* * has_stored_generated_columns * * Does table identified by RTI have any STORED GENERATED columns? */ bool has_stored_generated_columns(PlannerInfo *root, Index rti) { RangeTblEntry *rte = planner_rt_fetch(rti, root); Relation relation; TupleDesc tupdesc; bool result = false; /* Assume we already have adequate lock */ relation = table_open(rte->relid, NoLock); tupdesc = RelationGetDescr(relation); result = tupdesc->constr && tupdesc->constr->has_generated_stored; table_close(relation, NoLock); return result; } /* * get_dependent_generated_columns * * Get the column numbers of any STORED GENERATED columns of the relation * that depend on any column listed in target_cols. Both the input and * result bitmapsets contain column numbers offset by * FirstLowInvalidHeapAttributeNumber. */ Bitmapset * get_dependent_generated_columns(PlannerInfo *root, Index rti, Bitmapset *target_cols) { Bitmapset *dependentCols = NULL; RangeTblEntry *rte = planner_rt_fetch(rti, root); Relation relation; TupleDesc tupdesc; TupleConstr *constr; /* Assume we already have adequate lock */ relation = table_open(rte->relid, NoLock); tupdesc = RelationGetDescr(relation); constr = tupdesc->constr; if (constr && constr->has_generated_stored) { for (int i = 0; i < constr->num_defval; i++) { AttrDefault *defval = &constr->defval[i]; Node *expr; Bitmapset *attrs_used = NULL; /* skip if not generated column */ if (!TupleDescAttr(tupdesc, defval->adnum - 1)->attgenerated) continue; /* identify columns this generated column depends on */ expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); if (bms_overlap(target_cols, attrs_used)) dependentCols = bms_add_member(dependentCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } table_close(relation, NoLock); return dependentCols; } /* * set_relation_partition_info * * Set partitioning scheme and related information for a partitioned table. */ static void set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel, Relation relation) { PartitionDesc partdesc; /* * Create the PartitionDirectory infrastructure if we didn't already. */ if (root->glob->partition_directory == NULL) { root->glob->partition_directory = CreatePartitionDirectory(CurrentMemoryContext, true); } partdesc = PartitionDirectoryLookup(root->glob->partition_directory, relation); rel->part_scheme = find_partition_scheme(root, relation); Assert(partdesc != NULL && rel->part_scheme != NULL); rel->boundinfo = partdesc->boundinfo; rel->nparts = partdesc->nparts; set_baserel_partition_key_exprs(relation, rel); set_baserel_partition_constraint(relation, rel); } /* * find_partition_scheme * * Find or create a PartitionScheme for this Relation. */ static PartitionScheme find_partition_scheme(PlannerInfo *root, Relation relation) { PartitionKey partkey = RelationGetPartitionKey(relation); ListCell *lc; int partnatts, i; PartitionScheme part_scheme; /* A partitioned table should have a partition key. */ Assert(partkey != NULL); partnatts = partkey->partnatts; /* Search for a matching partition scheme and return if found one. */ foreach(lc, root->part_schemes) { part_scheme = lfirst(lc); /* Match partitioning strategy and number of keys. */ if (partkey->strategy != part_scheme->strategy || partnatts != part_scheme->partnatts) continue; /* Match partition key type properties. */ if (memcmp(partkey->partopfamily, part_scheme->partopfamily, sizeof(Oid) * partnatts) != 0 || memcmp(partkey->partopcintype, part_scheme->partopcintype, sizeof(Oid) * partnatts) != 0 || memcmp(partkey->partcollation, part_scheme->partcollation, sizeof(Oid) * partnatts) != 0) continue; /* * Length and byval information should match when partopcintype * matches. */ Assert(memcmp(partkey->parttyplen, part_scheme->parttyplen, sizeof(int16) * partnatts) == 0); Assert(memcmp(partkey->parttypbyval, part_scheme->parttypbyval, sizeof(bool) * partnatts) == 0); /* * If partopfamily and partopcintype matched, must have the same * partition comparison functions. Note that we cannot reliably * Assert the equality of function structs themselves for they might * be different across PartitionKey's, so just Assert for the function * OIDs. */ #ifdef USE_ASSERT_CHECKING for (i = 0; i < partkey->partnatts; i++) Assert(partkey->partsupfunc[i].fn_oid == part_scheme->partsupfunc[i].fn_oid); #endif /* Found matching partition scheme. */ return part_scheme; } /* * Did not find matching partition scheme. Create one copying relevant * information from the relcache. We need to copy the contents of the * array since the relcache entry may not survive after we have closed the * relation. */ part_scheme = (PartitionScheme) palloc0(sizeof(PartitionSchemeData)); part_scheme->strategy = partkey->strategy; part_scheme->partnatts = partkey->partnatts; part_scheme->partopfamily = (Oid *) palloc(sizeof(Oid) * partnatts); memcpy(part_scheme->partopfamily, partkey->partopfamily, sizeof(Oid) * partnatts); part_scheme->partopcintype = (Oid *) palloc(sizeof(Oid) * partnatts); memcpy(part_scheme->partopcintype, partkey->partopcintype, sizeof(Oid) * partnatts); part_scheme->partcollation = (Oid *) palloc(sizeof(Oid) * partnatts); memcpy(part_scheme->partcollation, partkey->partcollation, sizeof(Oid) * partnatts); part_scheme->parttyplen = (int16 *) palloc(sizeof(int16) * partnatts); memcpy(part_scheme->parttyplen, partkey->parttyplen, sizeof(int16) * partnatts); part_scheme->parttypbyval = (bool *) palloc(sizeof(bool) * partnatts); memcpy(part_scheme->parttypbyval, partkey->parttypbyval, sizeof(bool) * partnatts); part_scheme->partsupfunc = (FmgrInfo *) palloc(sizeof(FmgrInfo) * partnatts); for (i = 0; i < partnatts; i++) fmgr_info_copy(&part_scheme->partsupfunc[i], &partkey->partsupfunc[i], CurrentMemoryContext); /* Add the partitioning scheme to PlannerInfo. */ root->part_schemes = lappend(root->part_schemes, part_scheme); return part_scheme; } /* * set_baserel_partition_key_exprs * * Builds partition key expressions for the given base relation and fills * rel->partexprs. */ static void set_baserel_partition_key_exprs(Relation relation, RelOptInfo *rel) { PartitionKey partkey = RelationGetPartitionKey(relation); int partnatts; int cnt; List **partexprs; ListCell *lc; Index varno = rel->relid; Assert(IS_SIMPLE_REL(rel) && rel->relid > 0); /* A partitioned table should have a partition key. */ Assert(partkey != NULL); partnatts = partkey->partnatts; partexprs = (List **) palloc(sizeof(List *) * partnatts); lc = list_head(partkey->partexprs); for (cnt = 0; cnt < partnatts; cnt++) { Expr *partexpr; AttrNumber attno = partkey->partattrs[cnt]; if (attno != InvalidAttrNumber) { /* Single column partition key is stored as a Var node. */ Assert(attno > 0); partexpr = (Expr *) makeVar(varno, attno, partkey->parttypid[cnt], partkey->parttypmod[cnt], partkey->parttypcoll[cnt], 0); } else { if (lc == NULL) elog(ERROR, "wrong number of partition key expressions"); /* Re-stamp the expression with given varno. */ partexpr = (Expr *) copyObject(lfirst(lc)); ChangeVarNodes((Node *) partexpr, 1, varno, 0); lc = lnext(partkey->partexprs, lc); } /* Base relations have a single expression per key. */ partexprs[cnt] = list_make1(partexpr); } rel->partexprs = partexprs; /* * A base relation does not have nullable partition key expressions, since * no outer join is involved. We still allocate an array of empty * expression lists to keep partition key expression handling code simple. * See build_joinrel_partition_info() and match_expr_to_partition_keys(). */ rel->nullable_partexprs = (List **) palloc0(sizeof(List *) * partnatts); } /* * set_baserel_partition_constraint * * Builds the partition constraint for the given base relation and sets it * in the given RelOptInfo. All Var nodes are restamped with the relid of the * given relation. */ static void set_baserel_partition_constraint(Relation relation, RelOptInfo *rel) { List *partconstr; if (rel->partition_qual) /* already done */ return; /* * Run the partition quals through const-simplification similar to check * constraints. We skip canonicalize_qual, though, because partition * quals should be in canonical form already; also, since the qual is in * implicit-AND format, we'd have to explicitly convert it to explicit-AND * format and back again. */ partconstr = RelationGetPartitionQual(relation); if (partconstr) { partconstr = (List *) expression_planner((Expr *) partconstr); if (rel->relid != 1) ChangeVarNodes((Node *) partconstr, 1, rel->relid, 0); rel->partition_qual = partconstr; } }