Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:EDV_Lotse:1C
postgresql10
00010-joinsel.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 00010-joinsel.patch of Package postgresql10
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 132916aa70..75e1986ecd 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -306,6 +306,27 @@ performDeletion(const ObjectAddress *object, Relation depRel; ObjectAddresses *targetObjects; + if (flags & PERFORM_DELETION_CONCURRENTLY) + { + /* + * We must commit our transaction in order to make the first pg_index + * state update visible to other sessions. If the DROP machinery has + * already performed any other actions (removal of other objects, + * pg_depend entries, etc), the commit would make those actions + * permanent, which would leave us with inconsistent catalog state if + * we fail partway through the following sequence. Since DROP INDEX + * CONCURRENTLY is restricted to dropping just one index that has no + * dependencies, we should get here before anything's been done --- + * but let's check that to be sure. We can verify that the current + * transaction has not executed any transactional updates by checking + * that no XID has been assigned. + */ + if (GetTopTransactionIdIfAny() != InvalidTransactionId) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("DROP INDEX CONCURRENTLY must be first action in transaction"))); + } + /* * We save some cycles by opening pg_depend just once and passing the * Relation pointer down to all the recursive deletion steps. diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 0c2e457697..764d9de75f 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -46,9 +46,11 @@ #include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" +#include "catalog/pg_type_fn.h" #include "catalog/storage.h" #include "commands/tablecmds.h" #include "commands/trigger.h" +#include "commands/typecmds.h" #include "executor/executor.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -94,7 +96,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, List *indexColNames, Oid accessMethodObjectId, Oid *collationObjectId, - Oid *classObjectId); + Oid *classObjectId, + bool *preserveAttrType); static void InitializeAttributeOids(Relation indexRelation, int numatts, Oid indexoid); static void AppendAttributeTuples(Relation indexRelation, int numatts); @@ -282,7 +285,8 @@ ConstructTupleDescriptor(Relation heapRelation, List *indexColNames, Oid accessMethodObjectId, Oid *collationObjectId, - Oid *classObjectId) + Oid *classObjectId, + bool *preserveAttrType) { int numatts = indexInfo->ii_NumIndexAttrs; ListCell *colnames_item = list_head(indexColNames); @@ -306,6 +310,11 @@ ConstructTupleDescriptor(Relation heapRelation, indexTupDesc = CreateTemplateTupleDesc(numatts, false); /* + * By default assume that index will reuse attribute types of the heap relation + */ + *preserveAttrType = true; + + /* * For simple index columns, we copy the pg_attribute row from the parent * relation and modify it as necessary. For expressions we have to cons * up a pg_attribute row the hard way. @@ -476,6 +485,7 @@ ConstructTupleDescriptor(Relation heapRelation, to->attbyval = typeTup->typbyval; to->attalign = typeTup->typalign; to->attstorage = typeTup->typstorage; + *preserveAttrType = false; ReleaseSysCache(tuple); } @@ -732,10 +742,14 @@ index_create(Relation heapRelation, Oid namespaceId; int i; char relpersistence; + bool preserve_attr_type; + Oid ownerId; + ObjectAddress new_type_addr; is_exclusion = (indexInfo->ii_ExclusionOps != NULL); pg_class = heap_open(RelationRelationId, RowExclusiveLock); + ownerId = GetUserId(); /* * The index will be in the same namespace as its parent table, and is @@ -821,7 +835,8 @@ index_create(Relation heapRelation, indexColNames, accessMethodObjectId, collationObjectId, - classObjectId); + classObjectId, + &preserve_attr_type); /* * Allocate an OID for the index, unless we were told what to use. @@ -868,6 +883,95 @@ index_create(Relation heapRelation, Assert(indexRelationId == RelationGetRelid(indexRelation)); + if (indexInfo->ii_NumIndexAttrs > 1 + && preserve_attr_type + && !is_internal + && (!IsBinaryUpgrade || binary_upgrade_next_pg_type_oid != InvalidOid)) + { + Oid new_array_oid = AssignTypeArrayOid(); + char* relarrayname; + + /* + * Build compound type for compound index to be able to use it in statistic. + * We need to collect statistic for compound indexes to be able to better predict selectivity of multicolumn joins. + */ + new_type_addr = TypeCreate(InvalidOid, + indexRelationName, + namespaceId, + indexRelationId, + RELKIND_INDEX, + ownerId, /* owner's ID */ + -1, /* internal size (varlena) */ + TYPTYPE_COMPOSITE, /* type-type (composite) */ + TYPCATEGORY_COMPOSITE, /* type-category (ditto) */ + false, /* composite types are never preferred */ + DEFAULT_TYPDELIM, /* default array delimiter */ + F_RECORD_IN, /* input procedure */ + F_RECORD_OUT, /* output procedure */ + F_RECORD_RECV, /* receive procedure */ + F_RECORD_SEND, /* send procedure */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + InvalidOid, /* analyze procedure - default */ + InvalidOid, /* array element type - irrelevant */ + false, /* this is not an array type */ + new_array_oid, /* array type if any */ + InvalidOid, /* domain base type - irrelevant */ + NULL, /* default value - none */ + NULL, /* default binary representation */ + false, /* passed by reference */ + 'd', /* alignment - must be the largest! */ + 'x', /* fully TOASTable */ + -1, /* typmod */ + 0, /* array dimensions for typBaseType */ + false, /* Type NOT NULL */ + InvalidOid); /* rowtypes never have a collation */ + + indexRelation->rd_rel->reltype = new_type_addr.objectId; + + relarrayname = makeArrayTypeName(indexRelationName, namespaceId); + + TypeCreate(new_array_oid, /* force the type's OID to this */ + relarrayname, /* Array type name */ + namespaceId, /* Same namespace as parent */ + InvalidOid, /* Not composite, no relationOid */ + 0, /* relkind, also N/A here */ + ownerId, /* owner's ID */ + -1, /* Internal size (varlena) */ + TYPTYPE_BASE, /* Not composite - typelem is */ + TYPCATEGORY_ARRAY, /* type-category (array) */ + false, /* array types are never preferred */ + DEFAULT_TYPDELIM, /* default array delimiter */ + F_ARRAY_IN, /* array input proc */ + F_ARRAY_OUT, /* array output proc */ + F_ARRAY_RECV, /* array recv (bin) proc */ + F_ARRAY_SEND, /* array send (bin) proc */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + F_ARRAY_TYPANALYZE, /* array analyze procedure */ + indexRelation->rd_rel->reltype, /* array element type - the rowtype */ + true, /* yes, this is an array type */ + InvalidOid, /* this has no array type */ + InvalidOid, /* domain base type - irrelevant */ + NULL, /* default value - none */ + NULL, /* default binary representation */ + false, /* passed by reference */ + 'd', /* alignment - must be the largest! */ + 'x', /* fully TOASTable */ + -1, /* typmod */ + 0, /* array dimensions for typBaseType */ + false, /* Type NOT NULL */ + InvalidOid); /* rowtypes never have a collation */ + + pfree(relarrayname); + } + + /* + * Register relcache invalidation on the indexes' heap relation, to + * maintain consistency of its index list + */ + CacheInvalidateRelcache(heapRelation); + /* * Obtain exclusive lock on it. Although no other backends can see it * until we commit, this prevents deadlock-risk complaints from lock @@ -923,12 +1027,6 @@ index_create(Relation heapRelation, !deferrable, !concurrent); - /* - * Register relcache invalidation on the indexes' heap relation, to - * maintain consistency of its index list - */ - CacheInvalidateRelcache(heapRelation); - /* * Register constraint and dependencies for the index. * @@ -1428,24 +1526,6 @@ index_drop(Oid indexId, bool concurrent) */ if (concurrent) { - /* - * We must commit our transaction in order to make the first pg_index - * state update visible to other sessions. If the DROP machinery has - * already performed any other actions (removal of other objects, - * pg_depend entries, etc), the commit would make those actions - * permanent, which would leave us with inconsistent catalog state if - * we fail partway through the following sequence. Since DROP INDEX - * CONCURRENTLY is restricted to dropping just one index that has no - * dependencies, we should get here before anything's been done --- - * but let's check that to be sure. We can verify that the current - * transaction has not executed any transactional updates by checking - * that no XID has been assigned. - */ - if (GetTopTransactionIdIfAny() != InvalidTransactionId) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("DROP INDEX CONCURRENTLY must be first action in transaction"))); - /* * Mark index invalid by updating its pg_index entry */ @@ -3219,9 +3299,6 @@ index_set_state_flags(Oid indexId, IndexStateFlagsAction action) HeapTuple indexTuple; Form_pg_index indexForm; - /* Assert that current xact hasn't done any transactional updates */ - Assert(GetTopTransactionIdIfAny() == InvalidTransactionId); - /* Open pg_index and fetch a writable copy of the index's tuple */ pg_index = heap_open(IndexRelationId, RowExclusiveLock); diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 925d06ed8c..d5a024db14 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -35,8 +35,11 @@ #include "commands/vacuum.h" #include "executor/executor.h" #include "foreign/fdwapi.h" +#include "funcapi.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "nodes/makefuncs.h" +#include "nodes/pg_list.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "pgstat.h" @@ -61,6 +64,7 @@ #include "utils/syscache.h" #include "utils/timestamp.h" #include "utils/tqual.h" +#include "utils/typcache.h" /* Per-index data for ANALYZE */ @@ -70,6 +74,7 @@ typedef struct AnlIndexData double tupleFract; /* fraction of rows for partial index */ VacAttrStats **vacattrstats; /* index attrs to analyze */ int attr_cnt; + bool multicolumn; /* Collect compound row statistic for multicolumn index */ } AnlIndexData; @@ -480,6 +485,21 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params, } thisdata->attr_cnt = tcnt; } + else if (indexInfo->ii_NumIndexAttrs > 1 && va_cols == NIL && + Irel[ind]->rd_rel->reltype != InvalidOid) + { + /* Collect statistic for multicolumn index for better predicting selectivity of multicolumn joins */ + RowExpr* row = makeNode(RowExpr); + row->row_typeid = Irel[ind]->rd_rel->reltype; + row->row_format = COERCE_EXPLICIT_CAST; + row->location = -1; + row->colnames = NULL; + thisdata->vacattrstats = (VacAttrStats **)palloc(sizeof(VacAttrStats *)); + thisdata->vacattrstats[0] = examine_attribute(Irel[ind], 1, (Node*)row); + thisdata->vacattrstats[0]->tupDesc = lookup_type_cache(row->row_typeid, TYPECACHE_TUPDESC)->tupDesc; + thisdata->attr_cnt = 1; + thisdata->multicolumn = true; + } } } @@ -797,28 +817,41 @@ compute_index_stats(Relation onerel, double totalrows, values, isnull); - /* - * Save just the columns we care about. We copy the values - * into ind_context from the estate's per-tuple context. - */ - for (i = 0; i < attr_cnt; i++) + if (thisdata->multicolumn) { - VacAttrStats *stats = thisdata->vacattrstats[i]; - int attnum = stats->attr->attnum; - - if (isnull[attnum - 1]) - { - exprvals[tcnt] = (Datum) 0; - exprnulls[tcnt] = true; - } - else + /* For multicolumn index construct compound value */ + VacAttrStats *stats = thisdata->vacattrstats[0]; + exprvals[tcnt] = HeapTupleGetDatum(heap_form_tuple(stats->tupDesc, + values, + isnull)); + exprnulls[tcnt] = false; + tcnt++; + } + else + { + /* + * Save just the columns we care about. We copy the values + * into ind_context from the estate's per-tuple context. + */ + for (i = 0; i < attr_cnt; i++) { - exprvals[tcnt] = datumCopy(values[attnum - 1], - stats->attrtype->typbyval, - stats->attrtype->typlen); - exprnulls[tcnt] = false; + VacAttrStats *stats = thisdata->vacattrstats[i]; + int attnum = stats->attr->attnum; + + if (isnull[attnum - 1]) + { + exprvals[tcnt] = (Datum) 0; + exprnulls[tcnt] = true; + } + else + { + exprvals[tcnt] = datumCopy(values[attnum - 1], + stats->attrtype->typbyval, + stats->attrtype->typlen); + exprnulls[tcnt] = false; + } + tcnt++; } - tcnt++; } } } diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 9d340255c3..ea9660bb62 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -14,6 +14,9 @@ */ #include "postgres.h" +#include "access/genam.h" +#include "access/htup_details.h" +#include "funcapi.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" @@ -21,7 +24,10 @@ #include "optimizer/plancat.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" +#include "utils/rel.h" #include "utils/selfuncs.h" +#include "utils/syscache.h" +#include "utils/typcache.h" #include "statistics/statistics.h" @@ -43,6 +49,327 @@ static void addRangeClause(RangeQueryClause **rqlist, Node *clause, bool varonleft, bool isLTsel, Selectivity s2); static RelOptInfo *find_single_rel_for_clauses(PlannerInfo *root, List *clauses); +/* + * Get variabe node. Returns null if node is not a Var node. + */ +static inline Var* +get_var(Node* node) +{ + if (IsA(node, RelabelType)) + node = (Node *) ((RelabelType *) node)->arg; + + return IsA(node, Var) ? (Var*)node : NULL; +} + +/* + * Locate compound index which can be used for multicolumn join. Join clauses can refer to index keys in any order, but this order should be the same + * for left and right indexes. This is why we first call this function with strict_vars_order == false and fill permutation array. And second time wee use this + * permutation for previously chosen order. + */ +static IndexOptInfo* +locate_multicolumn_index(PlannerInfo *root, Index varno, List* vars, int* permutation, bool strict_vars_order) +{ + ListCell *ilist; + RelOptInfo* rel = find_base_rel(root, varno); + int n_vars = list_length(vars); + + Assert(n_vars > 1); + + foreach(ilist, rel->indexlist) + { + IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); + ListCell *vlist = list_head(vars); + int i = 0; + bool used[INDEX_MAX_KEYS] = {false}; + + if (index->ncolumns != n_vars) + continue; + + foreach (vlist, vars) + { + Var* var = lfirst(vlist); + if (strict_vars_order) + { + if (index->indexkeys[permutation[i]] != var->varattno) + break; + } + else + { + int pos; + for (pos = 0; pos < n_vars; pos++) + { + if (index->indexkeys[pos] == var->varattno && !used[pos]) + { + used[pos] = true; + permutation[i] = pos; + break; + } + } + if (pos == n_vars) + break; + } + i += 1; + } + if (vlist == NULL) + return index; + } + return NULL; +} + +/* + * treat_as_join_clause - + * Decide whether an operator clause is to be handled by the + * restriction or join estimator. Subroutine for clause_selectivity(). + */ +static inline bool +treat_as_join_clause(Node *clause, RestrictInfo *rinfo, + int varRelid, SpecialJoinInfo *sjinfo) +{ + if (varRelid != 0) + { + /* + * Caller is forcing restriction mode (eg, because we are examining an + * inner indexscan qual). + */ + return false; + } + else if (sjinfo == NULL) + { + /* + * It must be a restriction clause, since it's being evaluated at a + * scan node. + */ + return false; + } + else + { + /* + * Otherwise, it's a join if there's more than one relation used. We + * can optimize this calculation if an rinfo was passed. + * + * XXX Since we know the clause is being evaluated at a join, the + * only way it could be single-relation is if it was delayed by outer + * joins. Although we can make use of the restriction qual estimators + * anyway, it seems likely that we ought to account for the + * probability of injected nulls somehow. + */ + if (rinfo) + return (bms_membership(rinfo->clause_relids) == BMS_MULTIPLE); + else + return (NumRelids(clause) > 1); + } +} + + +/* + * Check if clauses represent multicolumnn join with compound indexes available for both side or + * comparison of indexed columns of one relation with constant values. + * If so, calculates selectivity of compound type comparison and returns true + */ +static bool +use_multicolumn_statistic(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, Selectivity* selectivity) +{ + ListCell *l; + List* vars1 = NULL; + List* vars2 = NULL; + Index varno1 = 0; + Index varno2 = 0; + VariableStatData vardata[2]; + IndexOptInfo* index[2]; + TypeCacheEntry *typentry; + int i; + int n_indexes; + int permutation[INDEX_MAX_KEYS]; + bool varonleft = true; + Datum constant = 0; + + foreach(l, clauses) + { + Node* clause = (Node *) lfirst(l); + RestrictInfo* rinfo = NULL; + OpExpr *opclause = NULL; + + if (IsA(clause, RestrictInfo)) + { + rinfo = (RestrictInfo *) clause; + if (!rinfo->orclause) + clause = (Node*)rinfo->clause; + } + if (IsA(clause, OpExpr)) + opclause = (OpExpr*)clause; + + if (opclause + && list_length(opclause->args) == 2 + && get_oprrest(opclause->opno) == F_EQSEL) + { + Node* arg1 = (Node*) linitial(opclause->args); + Node* arg2 = (Node*) lsecond(opclause->args); + Var* var1 = get_var(arg1); + Var* var2 = get_var(arg2); + + if (treat_as_join_clause((Node*)opclause, NULL, varRelid, sjinfo)) + { + if (var1 == NULL || var2 == NULL || var1->vartype != var2->vartype) + return false; + + if (varno1 == 0) + { + varno1 = var1->varno; + varno2 = var2->varno; + vars1 = list_make1(var1); + vars2 = list_make1(var2); + } + else if (var1->varno == varno1 && var2->varno == varno2) + { + vars1 = lappend(vars1, var1); + vars2 = lappend(vars2, var2); + } + else if (var1->varno == varno2 && var2->varno == varno1) + { + vars1 = lappend(vars1, var2); + vars2 = lappend(vars2, var1); + } + else + { + return false; + } + } + else /* Estimate selectivity for a restriction clause. */ + { + /* + * Give up if it is not equality comparison of variable with + * constant or some other clause is treated as join condition + */ + if (((var1 == NULL) == (var2 == NULL)) || varno2 != 0) + return false; + + if (var1 != NULL) /* variable at left */ + { + if (varno1 == 0) + varno1 = var1->varno; + else if (var1->varno != varno1) + return false; + vars1 = lappend(vars1, var1); + + if ((rinfo && is_pseudo_constant_clause_relids(arg2, rinfo->right_relids)) + || (!rinfo && NumRelids(clause) == 1 && is_pseudo_constant_clause(arg2))) + { + /* Restriction clause with a pseudoconstant on right side. */ + Node* const_val = estimate_expression_value(root, arg2); + if (IsA(const_val, Const)) + vars2 = lappend(vars2, const_val); + else + return false; + } + else + return false; + } + else + { + varonleft = false; /* Case (x=1 and 2=x) is not considered */ + + if (varno1 == 0) + varno1 = var2->varno; + else if (var2->varno != varno1) + return false; + vars1 = lappend(vars1, var2); + + if ((rinfo && is_pseudo_constant_clause_relids(arg1, rinfo->left_relids)) + || (!rinfo && NumRelids(clause) == 1 && is_pseudo_constant_clause(arg1))) + { + /* Restriction clause with a pseudoconstant on left side. */ + Node* const_val = estimate_expression_value(root, arg1); + if (IsA(const_val, Const)) + vars2 = lappend(vars2, const_val); + else + return false; + } + else + return false; + } + } + } + else + return false; + } + if (varno1 == 0) + return false; + + index[0] = locate_multicolumn_index(root, varno1, vars1, permutation, false); + if (!index[0]) + return false; + + if (varno2 != 0) + { + index[1] = locate_multicolumn_index(root, varno2, vars2, permutation, true); + if (!index[1]) + return false; + n_indexes = 2; + } + else + n_indexes = 1; + + for (i = 0; i < n_indexes; i++) + { + Relation indexRel = index_open(index[i]->indexoid, AccessShareLock); + + if (!indexRel->rd_rel->reltype) + { + index_close(indexRel, AccessShareLock); + + while (--i >= 0) + ReleaseVariableStats(vardata[i]); + + return false; + } + memset(&vardata[i], 0, sizeof(vardata[i])); + vardata[i].isunique = index[i]->unique; + vardata[i].atttype = indexRel->rd_rel->reltype; + vardata[i].rel = index[i]->rel; + vardata[i].acl_ok = true; + vardata[i].statsTuple = SearchSysCache3(STATRELATTINH, + ObjectIdGetDatum(index[i]->indexoid), + Int16GetDatum(1), + BoolGetDatum(false)); + vardata[i].freefunc = ReleaseSysCache; + + index_close(indexRel, AccessShareLock); + } + + /* + * Assume that two compound types are coheerent, so we can use equality function from one type + * to compare it with other type. + */ + typentry = lookup_type_cache(vardata[0].atttype, TYPECACHE_EQ_OPR|TYPECACHE_TUPDESC); + + if (n_indexes == 1) /* restriction clause selectivity */ + { + /* Create compound constant value */ + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + i = 0; + foreach (l, vars2) + { + Const* c = (Const*) lfirst(l); + int j = permutation[i++]; + values[j] = c->constvalue; + isnull[j] = c->constisnull; + } + constant = HeapTupleGetDatum(heap_form_tuple(typentry->tupDesc, values, isnull)); + *selectivity = eqconst_selectivity(typentry->eq_opr, &vardata[0], + constant, false, varonleft, false); + } + else /* join clause selectivity */ + { + *selectivity = eqjoin_selectivity(root, typentry->eq_opr, + &vardata[0], &vardata[1], sjinfo); + } + + for (i = 0; i < n_indexes; i++) + ReleaseVariableStats(vardata[i]); + + return true; +} /**************************************************************************** * ROUTINES TO COMPUTE SELECTIVITIES @@ -103,6 +430,7 @@ clauselist_selectivity(PlannerInfo *root, SpecialJoinInfo *sjinfo) { Selectivity s1 = 1.0; + Selectivity s2; RelOptInfo *rel; Bitmapset *estimatedclauses = NULL; RangeQueryClause *rqlist = NULL; @@ -141,108 +469,120 @@ clauselist_selectivity(PlannerInfo *root, } /* - * Apply normal selectivity estimates for remaining clauses. We'll be - * careful to skip any clauses which were already estimated above. - * - * Anything that doesn't look like a potential rangequery clause gets - * multiplied into s1 and forgotten. Anything that does gets inserted into - * an rqlist entry. + * Check if join conjuncts corresponds to some compound indexes on left and + * right joined relations. In this case selectivity of join can be + * calculated based on statistic of this compound indexes. */ - listidx = -1; - foreach(l, clauses) + if (use_multicolumn_statistic(root, clauses, varRelid, jointype, sjinfo, &s2)) + { + s1 *= s2; + } + else { - Node *clause = (Node *) lfirst(l); - RestrictInfo *rinfo; - Selectivity s2; - - listidx++; - - /* - * Skip this clause if it's already been estimated by some other - * statistics above. - */ - if (bms_is_member(listidx, estimatedclauses)) - continue; - - /* Always compute the selectivity using clause_selectivity */ - s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo); - /* - * Check for being passed a RestrictInfo. + * Apply normal selectivity estimates for remaining clauses. We'll be + * careful to skip any clauses which were already estimated above. * - * If it's a pseudoconstant RestrictInfo, then s2 is either 1.0 or - * 0.0; just use that rather than looking for range pairs. + * Anything that doesn't look like a potential rangequery clause gets + * multiplied into s1 and forgotten. Anything that does gets inserted + * into an rqlist entry. */ - if (IsA(clause, RestrictInfo)) + + listidx = -1; + foreach(l, clauses) { - rinfo = (RestrictInfo *) clause; - if (rinfo->pseudoconstant) - { - s1 = s1 * s2; + Node *clause = (Node *) lfirst(l); + RestrictInfo *rinfo; + + listidx++; + + /* + * Skip this clause if it's already been estimated by some other + * statistics above. + */ + if (bms_is_member(listidx, estimatedclauses)) continue; - } - clause = (Node *) rinfo->clause; - } - else - rinfo = NULL; - /* - * See if it looks like a restriction clause with a pseudoconstant on - * one side. (Anything more complicated than that might not behave in - * the simple way we are expecting.) Most of the tests here can be - * done more efficiently with rinfo than without. - */ - if (is_opclause(clause) && list_length(((OpExpr *) clause)->args) == 2) - { - OpExpr *expr = (OpExpr *) clause; - bool varonleft = true; - bool ok; + /* Always compute the selectivity using clause_selectivity */ + s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo); - if (rinfo) + /* + * Check for being passed a RestrictInfo. + * + * If it's a pseudoconstant RestrictInfo, then s2 is either 1.0 or + * 0.0; just use that rather than looking for range pairs. + */ + if (IsA(clause, RestrictInfo)) { - ok = (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) && - (is_pseudo_constant_clause_relids(lsecond(expr->args), - rinfo->right_relids) || - (varonleft = false, - is_pseudo_constant_clause_relids(linitial(expr->args), - rinfo->left_relids))); + rinfo = (RestrictInfo *) clause; + if (rinfo->pseudoconstant) + { + s1 = s1 * s2; + continue; + } + clause = (Node *) rinfo->clause; } else - { - ok = (NumRelids(clause) == 1) && - (is_pseudo_constant_clause(lsecond(expr->args)) || - (varonleft = false, - is_pseudo_constant_clause(linitial(expr->args)))); - } + rinfo = NULL; - if (ok) + /* + * See if it looks like a restriction clause with a pseudoconstant + * on one side. (Anything more complicated than that might not + * behave in the simple way we are expecting.) Most of the tests + * here can be done more efficiently with rinfo than without. + */ + if (is_opclause(clause) && list_length(((OpExpr *) clause)->args) == 2) { - /* - * If it's not a "<" or ">" operator, just merge the - * selectivity in generically. But if it's the right oprrest, - * add the clause to rqlist for later processing. - */ - switch (get_oprrest(expr->opno)) + OpExpr *expr = (OpExpr *) clause; + bool varonleft = true; + bool ok; + + if (rinfo) { - case F_SCALARLTSEL: - addRangeClause(&rqlist, clause, - varonleft, true, s2); - break; - case F_SCALARGTSEL: - addRangeClause(&rqlist, clause, - varonleft, false, s2); + ok = (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) && + (is_pseudo_constant_clause_relids(lsecond(expr->args), + rinfo->right_relids) || + (varonleft = false, + is_pseudo_constant_clause_relids(linitial(expr->args), + rinfo->left_relids))); + } + else + { + ok = (NumRelids(clause) == 1) && + (is_pseudo_constant_clause(lsecond(expr->args)) || + (varonleft = false, + is_pseudo_constant_clause(linitial(expr->args)))); + } + + if (ok) + { + /* + * If it's not a "<"/"<="/">"/">=" operator, just merge the + * selectivity in generically. But if it's the right + * oprrest, add the clause to rqlist for later processing. + */ + switch (get_oprrest(expr->opno)) + { + case F_SCALARLTSEL: + addRangeClause(&rqlist, clause, + varonleft, true, s2); break; - default: - /* Just merge the selectivity in generically */ - s1 = s1 * s2; + case F_SCALARGTSEL: + addRangeClause(&rqlist, clause, + varonleft, false, s2); break; + default: + /* Just merge the selectivity in generically */ + s1 = s1 * s2; + break; + } + continue; /* drop to loop bottom */ } - continue; /* drop to loop bottom */ } - } - /* Not the right form, so treat it generically. */ - s1 = s1 * s2; + /* Not the right form, so treat it generically. */ + s1 = s1 * s2; + } } /* @@ -485,51 +825,6 @@ bms_is_subset_singleton(const Bitmapset *s, int x) return false; } -/* - * treat_as_join_clause - - * Decide whether an operator clause is to be handled by the - * restriction or join estimator. Subroutine for clause_selectivity(). - */ -static inline bool -treat_as_join_clause(Node *clause, RestrictInfo *rinfo, - int varRelid, SpecialJoinInfo *sjinfo) -{ - if (varRelid != 0) - { - /* - * Caller is forcing restriction mode (eg, because we are examining an - * inner indexscan qual). - */ - return false; - } - else if (sjinfo == NULL) - { - /* - * It must be a restriction clause, since it's being evaluated at a - * scan node. - */ - return false; - } - else - { - /* - * Otherwise, it's a join if there's more than one relation used. We - * can optimize this calculation if an rinfo was passed. - * - * XXX Since we know the clause is being evaluated at a join, the - * only way it could be single-relation is if it was delayed by outer - * joins. Although we can make use of the restriction qual estimators - * anyway, it seems likely that we ought to account for the - * probability of injected nulls somehow. - */ - if (rinfo) - return (bms_membership(rinfo->clause_relids) == BMS_MULTIPLE); - else - return (NumRelids(clause) > 1); - } -} - - /* * clause_selectivity - * Compute the selectivity of a general boolean expression clause. diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 3d633de8ff..2e570f9d8b 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -297,12 +297,21 @@ static double var_eq_const(VariableStatData *vardata, Oid operator, Datum constval, bool constisnull, bool varonleft, bool negate) +{ + return eqconst_selectivity(operator, vardata, constval, constisnull, varonleft, negate); +} + + +Selectivity +eqconst_selectivity(Oid operator, + VariableStatData *vardata, + Datum constval, bool constisnull, + bool varonleft, bool negate) { double selec; double nullfrac = 0.0; bool isdefault; Oid opfuncoid; - /* * If the constant is NULL, assume operator is strict and return zero, ie, * operator will never return TRUE. (It's zero even for a negator op.) @@ -2208,21 +2217,36 @@ eqjoinsel(PG_FUNCTION_ARGS) JoinType jointype = (JoinType) PG_GETARG_INT16(3); #endif SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) PG_GETARG_POINTER(4); - double selec; VariableStatData vardata1; VariableStatData vardata2; bool join_is_reversed; - RelOptInfo *inner_rel; + double selec; get_join_variables(root, args, sjinfo, &vardata1, &vardata2, &join_is_reversed); + selec = join_is_reversed + ? eqjoin_selectivity(root, operator, &vardata2, &vardata1, sjinfo) + : eqjoin_selectivity(root, operator, &vardata1, &vardata2, sjinfo); + + ReleaseVariableStats(vardata1); + ReleaseVariableStats(vardata2); + + PG_RETURN_FLOAT8((float8)selec); +} + +Selectivity +eqjoin_selectivity(PlannerInfo *root, Oid operator, VariableStatData* vardata1, VariableStatData* vardata2, SpecialJoinInfo *sjinfo) +{ + Selectivity selec; + RelOptInfo *inner_rel; + switch (sjinfo->jointype) { case JOIN_INNER: case JOIN_LEFT: case JOIN_FULL: - selec = eqjoinsel_inner(operator, &vardata1, &vardata2); + selec = eqjoinsel_inner(operator, vardata1, vardata2); break; case JOIN_SEMI: case JOIN_ANTI: @@ -2235,13 +2259,7 @@ eqjoinsel(PG_FUNCTION_ARGS) */ inner_rel = find_join_input_rel(root, sjinfo->min_righthand); - if (!join_is_reversed) - selec = eqjoinsel_semi(operator, &vardata1, &vardata2, - inner_rel); - else - selec = eqjoinsel_semi(get_commutator(operator), - &vardata2, &vardata1, - inner_rel); + selec = eqjoinsel_semi(operator, vardata1, vardata2, inner_rel); break; default: /* other values not expected here */ @@ -2251,12 +2269,9 @@ eqjoinsel(PG_FUNCTION_ARGS) break; } - ReleaseVariableStats(vardata1); - ReleaseVariableStats(vardata2); - CLAMP_PROBABILITY(selec); - PG_RETURN_FLOAT8((float8) selec); + return selec; } /* diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index a7b07827e0..7bf031ce60 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -180,6 +180,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, /* Otherwise we need the pg_proc entry */ procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); + Assert(procedureTuple); if (!HeapTupleIsValid(procedureTuple)) elog(ERROR, "cache lookup failed for function %u", functionId); procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index 57acc1932f..b511423bca 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -1146,6 +1146,25 @@ repairDependencyLoop(DumpableObject **loop, return; } + /* Indirect loop between index and its generated type. + * Originally types were generated only for tables, + * but for better estimation of multicolumn join selectivity + * types are also generated for compound indexes. + * But indexes and tables are assigned difference priorities + * DO_INDEX=28, DO_TABLE=18 which are on different sides of + * DO_PRE_DATA_BOUNDARY=26. The trick with introducing DO_DUMMY_TYPE=19 + * done in selectDumpableType doesn't work for types generated for indexes + * and we got cyclic dependency here. So we have to explicitly break such loop + * here to avoid circular constrains warning + */ + if (nLoop >= 2 && + loop[0]->objType == DO_DUMMY_TYPE && + loop[1]->objType == DO_INDEX) + { + removeObjectDependency(loop[0], loop[1]->dumpId); + return; + } + /* Indirect loop involving domain and CHECK constraint */ if (nLoop > 2) { diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h index c7fdd540e8..0d20ef3561 100644 --- a/src/include/utils/selfuncs.h +++ b/src/include/utils/selfuncs.h @@ -221,5 +221,14 @@ extern Selectivity scalararraysel_containment(PlannerInfo *root, Node *leftop, Node *rightop, Oid elemtype, bool isEquality, bool useOr, int varRelid); +extern Selectivity +eqjoin_selectivity(PlannerInfo *root, Oid operator, VariableStatData* vardata1, VariableStatData* vardata2, SpecialJoinInfo *sjinfo); + + +extern Selectivity +eqconst_selectivity(Oid operator, + VariableStatData *vardata, + Datum constval, bool constisnull, + bool varonleft, bool negate); #endif /* SELFUNCS_H */ diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index f60f76756a..e5e2a69264 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -2935,11 +2935,13 @@ RESET enable_indexonlyscan; -- explain (costs off) select * from tenk1 where (thousand, tenthous) in ((1,1001), (null,null)); - QUERY PLAN ------------------------------------------------------- - Index Scan using tenk1_thous_tenthous on tenk1 - Index Cond: ((thousand = 1) AND (tenthous = 1001)) -(2 rows) + QUERY PLAN +------------------------------------------------------------ + Bitmap Heap Scan on tenk1 + Recheck Cond: ((thousand = 1) AND (tenthous = 1001)) + -> Bitmap Index Scan on tenk1_thous_tenthous + Index Cond: ((thousand = 1) AND (tenthous = 1001)) +(4 rows) -- -- Check matching of boolean index columns to WHERE conditions and sort keys diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out index 054a381dad..a4b0ce6dd8 100644 --- a/src/test/regress/expected/stats_ext.out +++ b/src/test/regress/expected/stats_ext.out @@ -445,19 +445,24 @@ INSERT INTO functional_dependencies (a, b, c, filler1) ANALYZE functional_dependencies; EXPLAIN (COSTS OFF) SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1'; - QUERY PLAN ------------------------------------------------------------ - Index Scan using fdeps_abc_idx on functional_dependencies - Index Cond: ((a = 1) AND (b = '1'::text)) -(2 rows) + QUERY PLAN +--------------------------------------------------- + Bitmap Heap Scan on functional_dependencies + Recheck Cond: ((a = 1) AND (b = '1'::text)) + -> Bitmap Index Scan on fdeps_abc_idx + Index Cond: ((a = 1) AND (b = '1'::text)) +(4 rows) EXPLAIN (COSTS OFF) SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1' AND c = 1; - QUERY PLAN ------------------------------------------------------------ - Index Scan using fdeps_abc_idx on functional_dependencies - Index Cond: ((a = 1) AND (b = '1'::text) AND (c = 1)) -(2 rows) + QUERY PLAN +--------------------------------------------------- + Bitmap Heap Scan on functional_dependencies + Recheck Cond: ((a = 1) AND (b = '1'::text)) + Filter: (c = 1) + -> Bitmap Index Scan on fdeps_ab_idx + Index Cond: ((a = 1) AND (b = '1'::text)) +(5 rows) -- create statistics CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor