diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 2268cc27..22e0762e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -1159,7 +1159,7 @@ heapam_index_build_range_scan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - ExprState *predicate; + ExprState *predicateExpand; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -1200,7 +1200,7 @@ heapam_index_build_range_scan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); + predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -1607,9 +1607,9 @@ heapam_index_build_range_scan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NULL) + if (predicateExpand != NULL) { - if (!ExecQual(predicate, econtext)) + if (!ExecQual(predicateExpand, econtext)) continue; } @@ -1709,7 +1709,9 @@ heapam_index_build_range_scan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; + indexInfo->ii_ExpressionsExpandState = NIL; indexInfo->ii_PredicateState = NULL; + indexInfo->ii_PredicateExpandState = NULL; return reltuples; } @@ -1726,7 +1728,7 @@ heapam_index_validate_scan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - ExprState *predicate; + ExprState *predicateExpand; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -1758,7 +1760,7 @@ heapam_index_validate_scan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); + predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -1895,9 +1897,9 @@ heapam_index_validate_scan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NULL) + if (predicateExpand != NULL) { - if (!ExecQual(predicate, econtext)) + if (!ExecQual(predicateExpand, econtext)) continue; } @@ -1952,7 +1954,9 @@ heapam_index_validate_scan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; + indexInfo->ii_ExpressionsExpandState = NIL; indexInfo->ii_PredicateState = NULL; + indexInfo->ii_PredicateExpandState = NULL; } /* diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index b0dcd987..80bf4fc4 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1156,11 +1156,17 @@ index_register(Oid heap, /* expressions will likely be null, but may as well copy it */ newind->il_info->ii_Expressions = copyObject(indexInfo->ii_Expressions); + newind->il_info->ii_ExpressionsExpand = + copyObject(indexInfo->ii_ExpressionsExpand); newind->il_info->ii_ExpressionsState = NIL; + newind->il_info->ii_ExpressionsExpandState = NIL; /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = copyObject(indexInfo->ii_Predicate); + newind->il_info->ii_PredicateExpand = + copyObject(indexInfo->ii_PredicateExpand); newind->il_info->ii_PredicateState = NULL; + newind->il_info->ii_PredicateExpandState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357..7755e874 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1403,6 +1403,10 @@ index_create_copy(Relation heapRelation, uint16 flags, concurrently, /* concurrent */ indexRelation->rd_indam->amsummarizing, oldInfo->ii_WithoutOverlaps); + newInfo->ii_ExpressionsExpand = + (List *)ExpandVirtualGeneratedColumns((Node *)indexExprs, heapRelation, InvalidOid); + newInfo->ii_PredicateExpand = + (List *)ExpandVirtualGeneratedColumns((Node *)indexPreds, heapRelation, InvalidOid); /* fetch exclusion constraint info if any */ if (indexRelation->rd_index->indisexclusion) @@ -2471,6 +2475,8 @@ BuildIndexInfo(Relation index) false, index->rd_indam->amsummarizing, indexStruct->indisexclusion && indexStruct->indisunique); + ii->ii_ExpressionsExpand = (List *)RelationGetIndexExpressionsExpand(index); + ii->ii_PredicateExpand = (List *)RelationGetIndexPredicateExpand(index); /* fill in attribute numbers */ for (i = 0; i < numAtts; i++) @@ -2760,10 +2766,12 @@ FormIndexDatum(IndexInfo *indexInfo, /* First time through, set up expression evaluation state */ indexInfo->ii_ExpressionsState = ExecPrepareExprList(indexInfo->ii_Expressions, estate); + indexInfo->ii_ExpressionsExpandState = + ExecPrepareExprList(indexInfo->ii_ExpressionsExpand, estate); /* Check caller has set up context correctly */ Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot); } - indexpr_item = list_head(indexInfo->ii_ExpressionsState); + indexpr_item = list_head(indexInfo->ii_ExpressionsExpandState); for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { @@ -2775,11 +2783,40 @@ FormIndexDatum(IndexInfo *indexInfo, iDatum = slot_getsysattr(slot, keycol, &isNull); else if (keycol != 0) { - /* - * Plain index column; get the value we need directly from the - * heap tuple. - */ - iDatum = slot_getattr(slot, keycol, &isNull); + TupleDesc tupdesc = slot->tts_tupleDescriptor; + Form_pg_attribute att = TupleDescAttr(tupdesc, keycol - 1); + + if (att->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL) + { + /* + * Plain index column; get the value we need directly from the + * heap tuple. + */ + iDatum = slot_getattr(slot, keycol, &isNull); + } + else + { + TupleConstr *constr = tupdesc->constr; + + for (int j = 0; j < constr->num_defval; j++) + { + AttrDefault *defval; + Expr *expr = NULL; + ExprState *exprstate; + + defval = &constr->defval[j]; + if (defval->adnum == keycol) + { + expr = stringToNode(defval->adbin); + exprstate = ExecPrepareExpr(expr, estate); + iDatum = ExecEvalExprSwitchContext(exprstate, + GetPerTupleExprContext(estate), + &isNull); + break; + } + Assert(j + 1 < constr->num_defval); + } + } } else { @@ -2791,7 +2828,7 @@ FormIndexDatum(IndexInfo *indexInfo, iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexpr_item), GetPerTupleExprContext(estate), &isNull); - indexpr_item = lnext(indexInfo->ii_ExpressionsState, indexpr_item); + indexpr_item = lnext(indexInfo->ii_ExpressionsExpandState, indexpr_item); } values[i] = iDatum; isnull[i] = isNull; @@ -3220,7 +3257,7 @@ IndexCheckExclusion(Relation heapRelation, TableScanDesc scan; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - ExprState *predicate; + ExprState *predicateExpand; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -3246,7 +3283,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); + predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate); /* * Scan all live tuples in the base relation. @@ -3266,9 +3303,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NULL) + if (predicateExpand != NULL) { - if (!ExecQual(predicate, econtext)) + if (!ExecQual(predicateExpand, econtext)) continue; } @@ -3301,7 +3338,9 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; + indexInfo->ii_ExpressionsExpandState = NIL; indexInfo->ii_PredicateState = NULL; + indexInfo->ii_PredicateExpandState = NULL; } /* diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index fd7d2ec0..d27aaa09 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -133,7 +133,9 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple, * supported, nor exclusion constraints, nor deferred uniqueness */ Assert(indexInfo->ii_Expressions == NIL); + Assert(indexInfo->ii_ExpressionsExpand == NIL); Assert(indexInfo->ii_Predicate == NIL); + Assert(indexInfo->ii_PredicateExpand == NIL); Assert(indexInfo->ii_ExclusionOps == NULL); Assert(index->rd_index->indimmediate); Assert(indexInfo->ii_NumIndexKeyAttrs != 0); diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 4aa52a4b..daebfda1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -298,9 +298,13 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_IndexAttrNumbers[0] = 1; indexInfo->ii_IndexAttrNumbers[1] = 2; indexInfo->ii_Expressions = NIL; + indexInfo->ii_ExpressionsExpand = NIL; indexInfo->ii_ExpressionsState = NIL; + indexInfo->ii_ExpressionsExpandState = NIL; indexInfo->ii_Predicate = NIL; + indexInfo->ii_PredicateExpand = NIL; indexInfo->ii_PredicateState = NULL; + indexInfo->ii_PredicateExpandState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b7..fc67c24a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -899,7 +899,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - ExprState *predicate; + ExprState *predicateExpand; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -908,7 +908,7 @@ compute_index_stats(Relation onerel, double totalrows, double totalindexrows; /* Ignore index if no columns to analyze and not partial */ - if (attr_cnt == 0 && indexInfo->ii_Predicate == NIL) + if (attr_cnt == 0 && indexInfo->ii_PredicateExpand == NIL) continue; /* @@ -926,7 +926,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); + predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -949,9 +949,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreHeapTuple(heapTuple, slot, false); /* If index is partial, check predicate */ - if (predicate != NULL) + if (predicateExpand != NULL) { - if (!ExecQual(predicate, econtext)) + if (!ExecQual(predicateExpand, econtext)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8d..f4854911 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -932,6 +932,8 @@ DefineIndex(ParseState *pstate, concurrent, amissummarizing, stmt->iswithoutoverlaps); + indexInfo->ii_PredicateExpand = + (List *)ExpandVirtualGeneratedColumns(stmt->whereClause, rel, InvalidOid); typeIds = palloc_array(Oid, numberOfAttributes); collationIds = palloc_array(Oid, numberOfAttributes); @@ -948,6 +950,8 @@ DefineIndex(ParseState *pstate, root_save_userid, root_save_sec_context, &root_save_nestlevel); + indexInfo->ii_ExpressionsExpand = + (List *)ExpandVirtualGeneratedColumns((Node *)indexInfo->ii_ExpressionsExpand, rel, InvalidOid); /* * Extra checks when creating a PRIMARY KEY index. */ @@ -1110,7 +1114,7 @@ DefineIndex(ParseState *pstate, * We disallow indexes on system columns. They would not necessarily get * updated correctly, and they don't seem useful anyway. * - * Also disallow virtual generated columns in indexes (use expression + * Aisallow virtual generated columns in indexes (include using expression * index instead). */ for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++) @@ -1123,7 +1127,7 @@ DefineIndex(ParseState *pstate, errmsg("index creation on system columns is not supported"))); - if (attno > 0 && + /*if (attno > 0 && i < numberOfKeyAttributes && TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL) ereport(ERROR, errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -1131,17 +1135,15 @@ DefineIndex(ParseState *pstate, errmsg("primary keys on virtual generated columns are not supported") : stmt->isconstraint ? errmsg("unique constraints on virtual generated columns are not supported") : - errmsg("indexes on virtual generated columns are not supported")); + errmsg("indexes on virtual generated columns are not supported"));*/ } /* - * Also check for system and generated columns used in expressions or - * predicates. + * Also check for system columns used in expressions or predicates. */ if (indexInfo->ii_Expressions || indexInfo->ii_Predicate) { Bitmapset *indexattrs = NULL; - int j; pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs); pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &indexattrs); @@ -1154,25 +1156,6 @@ DefineIndex(ParseState *pstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("index creation on system columns is not supported"))); } - - /* - * XXX Virtual generated columns in index expressions or predicates - * could be supported, but it needs support in - * RelationGetIndexExpressions() and RelationGetIndexPredicate(). - */ - j = -1; - while ((j = bms_next_member(indexattrs, j)) >= 0) - { - AttrNumber attno = j + FirstLowInvalidHeapAttributeNumber; - - if (attno > 0 && - TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - stmt->isconstraint ? - errmsg("unique constraints on virtual generated columns are not supported") : - errmsg("indexes on virtual generated columns are not supported"))); - } } /* Is index safe for others to ignore? See set_indexsafe_procflags() */ @@ -1876,6 +1859,11 @@ CheckPredicate(Expr *predicate) * If the caller switched to the table owner, ddl_userid is the role for ACL * checks reached without traversing opaque expressions. Otherwise, it's * InvalidOid, and other ddl_* arguments are undefined. + * + * Upon returning from this function, callers must apply + * ExpandVirtualGeneratedColumns() to ii_ExpressionsExpand + * when necessary for actual expansion if ii_ExpressionsExpand + * is not NIL or dummy. */ static void ComputeIndexAttrs(ParseState *pstate, @@ -2274,6 +2262,10 @@ ComputeIndexAttrs(ParseState *pstate, attn++; } + + indexInfo->ii_ExpressionsExpand = + (List *)ExpandVirtualGeneratedColumns((Node *)copyObject(indexInfo->ii_Expressions), + NULL, relId); } /* diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index eb383812..b47daaf5 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -380,20 +380,24 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, if (indexInfo->ii_Predicate != NIL) { ExprState *predicate; + ExprState *predicateExpand; /* * If predicate state not set up yet, create it (in the estate's * per-query context) */ predicate = indexInfo->ii_PredicateState; + predicateExpand = indexInfo->ii_PredicateExpandState; if (predicate == NULL) { predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); + predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate); indexInfo->ii_PredicateState = predicate; + indexInfo->ii_PredicateExpandState = predicateExpand; } /* Skip this index-update if the predicate isn't satisfied */ - if (!ExecQual(predicate, econtext)) + if (!ExecQual(predicateExpand, econtext)) continue; } @@ -616,20 +620,24 @@ ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, if (indexInfo->ii_Predicate != NIL) { ExprState *predicate; + ExprState *predicateExpand; /* * If predicate state not set up yet, create it (in the estate's * per-query context) */ predicate = indexInfo->ii_PredicateState; + predicateExpand = indexInfo->ii_PredicateExpandState; if (predicate == NULL) { predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); + predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate); indexInfo->ii_PredicateState = predicate; + indexInfo->ii_PredicateExpandState = predicateExpand; } /* Skip this index-update if the predicate isn't satisfied */ - if (!ExecQual(predicate, econtext)) + if (!ExecQual(predicateExpand, econtext)) continue; } @@ -1102,6 +1110,7 @@ index_unchanged_by_update(ResultRelInfo *resultRelInfo, EState *estate, * pass hint. */ idxExprs = RelationGetIndexExpressions(indexRelation); + idxExprs = list_concat(idxExprs, RelationGetIndexExpressionsExpand(indexRelation)); hasexpression = index_expression_changed_walker((Node *) idxExprs, allUpdatedCols); list_free(idxExprs); diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 40b09958..946891b9 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -828,7 +828,11 @@ make_ands_implicit(Expr *clause) /* * makeIndexInfo - * create an IndexInfo node + * create an IndexInfo node. Upon returning from this function, + * callers must apply ExpandVirtualGeneratedColumns() + * or RelationGetIndexExpressionsExpand or RelationGetIndexPredicateExpand() to + * ii_ExpressionsExpand and ii_PredicateExpand as needed for actual + * expansion when they are not NIL or dummy. */ IndexInfo * makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, @@ -856,11 +860,15 @@ makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, /* expressions */ n->ii_Expressions = expressions; + n->ii_ExpressionsExpand = copyObject(expressions); n->ii_ExpressionsState = NIL; + n->ii_ExpressionsExpandState = NIL; /* predicates */ n->ii_Predicate = predicates; + n->ii_PredicateExpand = copyObject(predicates); n->ii_PredicateState = NULL; + n->ii_PredicateExpandState = NULL; /* exclusion constraints */ n->ii_ExclusionOps = NULL; diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 3f5d4fa3..b716ee92 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -1119,7 +1119,7 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel, * just scanning the predOK index alone, no OR.) */ useful_predicate = false; - if (index->indpred != NIL) + if (index->indpredExpand != NIL) { if (index->predOK) { @@ -1131,10 +1131,10 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel, if (all_clauses == NIL) all_clauses = list_concat_copy(clauses, other_clauses); - if (!predicate_implied_by(index->indpred, all_clauses, false)) + if (!predicate_implied_by(index->indpredExpand, all_clauses, false)) continue; /* can't use it at all */ - if (!predicate_implied_by(index->indpred, other_clauses, false)) + if (!predicate_implied_by(index->indpredExpand, other_clauses, false)) useful_predicate = true; } } @@ -2183,7 +2183,7 @@ find_indexpath_quals(Path *bitmapqual, List **quals, List **preds) *quals = lappend(*quals, iclause->rinfo->clause); } - *preds = list_concat(*preds, ipath->indexinfo->indpred); + *preds = list_concat(*preds, ipath->indexinfo->indpredExpand); } else elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual)); @@ -4038,11 +4038,11 @@ check_index_predicates(PlannerInfo *root, RelOptInfo *rel) IndexOptInfo *index = (IndexOptInfo *) lfirst(lc); ListCell *lcr; - if (index->indpred == NIL) + if (index->indpredExpand == NIL) continue; /* ignore non-partial indexes here */ if (!index->predOK) /* don't repeat work if already proven OK */ - index->predOK = predicate_implied_by(index->indpred, clauselist, + index->predOK = predicate_implied_by(index->indpredExpand, clauselist, false); /* If rel is an update target, leave indrestrictinfo as set above */ @@ -4068,7 +4068,7 @@ check_index_predicates(PlannerInfo *root, RelOptInfo *rel) /* predicate_implied_by() assumes first arg is immutable */ if (contain_mutable_functions((Node *) rinfo->clause) || !predicate_implied_by(list_make1(rinfo->clause), - index->indpred, false)) + index->indpredExpand, false)) index->indrestrictinfo = lappend(index->indrestrictinfo, rinfo); } } @@ -4402,14 +4402,14 @@ match_index_to_operand(Node *operand, int i; Node *indexkey; - indexpr_item = list_head(index->indexprs); + indexpr_item = list_head(index->indexprsExpand); for (i = 0; i < indexcol; i++) { if (index->indexkeys[i] == 0) { if (indexpr_item == NULL) elog(ERROR, "wrong number of index expressions"); - indexpr_item = lnext(index->indexprs, indexpr_item); + indexpr_item = lnext(index->indexprsExpand, indexpr_item); } } if (indexpr_item == NULL) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index de6a183d..2bdad7bd 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -3340,7 +3340,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, subindexECs = lappend(subindexECs, rinfo->parent_ec); } /* We can add any index predicate conditions, too */ - foreach(l, ipath->indexinfo->indpred) + foreach(l, ipath->indexinfo->indpredExpand) { Expr *pred = (Expr *) lfirst(l); @@ -5142,7 +5142,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol) } /* It's an index expression, so find and cross-check the expression */ - indexpr_item = list_head(index->indexprs); + indexpr_item = list_head(index->indexprsExpand); for (pos = 0; pos < index->ncolumns; pos++) { if (index->indexkeys[pos] == 0) @@ -5167,7 +5167,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol) else elog(ERROR, "index key does not match expected index column"); } - indexpr_item = lnext(index->indexprs, indexpr_item); + indexpr_item = lnext(index->indexprsExpand, indexpr_item); } } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 7c4be174..72846d9e 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -433,24 +433,38 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * properly reduced. */ info->indexprs = RelationGetIndexExpressions(indexRelation); + info->indexprsExpand = RelationGetIndexExpressionsExpand(indexRelation); info->indpred = RelationGetIndexPredicate(indexRelation); + info->indpredExpand = RelationGetIndexPredicateExpand(indexRelation); if (info->indexprs) { if (varno != 1) + { ChangeVarNodes((Node *) info->indexprs, 1, varno, 0); + ChangeVarNodes((Node *) info->indexprsExpand, 1, varno, 0); + } info->indexprs = (List *) eval_const_expressions(root, (Node *) info->indexprs); + info->indexprsExpand = (List *) + eval_const_expressions(root, (Node *) info->indexprsExpand); } if (info->indpred) { if (varno != 1) + { ChangeVarNodes((Node *) info->indpred, 1, varno, 0); + ChangeVarNodes((Node *) info->indpredExpand, 1, varno, 0); + } info->indpred = (List *) eval_const_expressions(root, (Node *) make_ands_explicit(info->indpred)); + info->indpredExpand = (List *) + eval_const_expressions(root, + (Node *) make_ands_explicit(info->indpredExpand)); info->indpred = make_ands_implicit((Expr *) info->indpred); + info->indpredExpand = make_ands_implicit((Expr *) info->indpredExpand); } /* Build targetlist using the completed indexprs data */ @@ -941,8 +955,8 @@ infer_arbiter_indexes(PlannerInfo *root) attno - FirstLowInvalidHeapAttributeNumber); } - inferElems = RelationGetIndexExpressions(idxRel); - inferIndexExprs = RelationGetIndexPredicate(idxRel); + inferElems = RelationGetIndexExpressionsExpand(idxRel); + inferIndexExprs = RelationGetIndexPredicateExpand(idxRel); break; } } @@ -1072,7 +1086,7 @@ infer_arbiter_indexes(PlannerInfo *root) continue; /* Expression attributes (if any) must match */ - idxExprs = RelationGetIndexExpressions(idxRel); + idxExprs = RelationGetIndexExpressionsExpand(idxRel); if (idxExprs) { if (varno != 1) @@ -1139,7 +1153,7 @@ infer_arbiter_indexes(PlannerInfo *root) if (list_difference(idxExprs, inferElems) != NIL) continue; - predExprs = RelationGetIndexPredicate(idxRel); + predExprs = RelationGetIndexPredicateExpand(idxRel); if (predExprs) { if (varno != 1) diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index d6efd070..98e7216e 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -7639,10 +7639,10 @@ add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals) List *predExtraQuals = NIL; ListCell *lc; - if (index->indpred == NIL) + if (index->indpredExpand == NIL) return indexQuals; - foreach(lc, index->indpred) + foreach(lc, index->indpredExpand) { Node *predQual = (Node *) lfirst(lc); List *oneQual = list_make1(predQual); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 0572ab42..5f8f404b 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -91,6 +91,7 @@ #include "utils/resowner.h" #include "utils/snapmgr.h" #include "utils/syscache.h" +#include "rewrite/rewriteHandler.h" #define RELCACHE_INIT_FILEMAGIC 0x573266 /* version ID value */ @@ -1583,7 +1584,9 @@ RelationInitIndexAccessInfo(Relation relation) * expressions, predicate, exclusion caches will be filled later */ relation->rd_indexprs = NIL; + relation->rd_indexprsExpand = NIL; relation->rd_indpred = NIL; + relation->rd_indpredExpand = NIL; relation->rd_exclops = NULL; relation->rd_exclprocs = NULL; relation->rd_exclstrats = NULL; @@ -5146,6 +5149,45 @@ RelationGetIndexExpressions(Relation relation) return result; } +List * +RelationGetIndexExpressionsExpand(Relation relation) +{ + List *result; + MemoryContext oldcxt; + bool isnull; + Datum heapRelidDatum; + Oid heapRelid; + + /* Quick exit if we already computed the result. */ + if (relation->rd_indexprsExpand) + return copyObject(relation->rd_indexprsExpand); + + /* Quick exit if there is nothing to do. */ + if (relation->rd_indextuple == NULL || + heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL)) + return NIL; + + result = RelationGetIndexExpressions(relation); + if (result == NIL) + return NIL; + + heapRelidDatum = heap_getattr(relation->rd_indextuple, + Anum_pg_index_indrelid, + GetPgIndexDescriptor(), + &isnull); + Assert(!isnull); + heapRelid = DatumGetObjectId(heapRelidDatum); + + result = (List *)ExpandVirtualGeneratedColumns((Node *) result, NULL, heapRelid); + + /* Now save a copy of the completed tree in the relcache entry. */ + oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt); + relation->rd_indexprsExpand = copyObject(result); + MemoryContextSwitchTo(oldcxt); + + return result; +} + /* * RelationGetDummyIndexExpressions -- get dummy expressions for an index * @@ -5266,6 +5308,45 @@ RelationGetIndexPredicate(Relation relation) return result; } +List * +RelationGetIndexPredicateExpand(Relation relation) +{ + List *result; + MemoryContext oldcxt; + bool isnull; + Datum heapRelidDatum; + Oid heapRelid; + + /* Quick exit if we already computed the result. */ + if (relation->rd_indpredExpand) + return copyObject(relation->rd_indpredExpand); + + /* Quick exit if there is nothing to do. */ + if (relation->rd_indextuple == NULL || + heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred, NULL)) + return NIL; + + result = RelationGetIndexPredicate(relation); + if (result == NIL) + return NIL; + + heapRelidDatum = heap_getattr(relation->rd_indextuple, + Anum_pg_index_indrelid, + GetPgIndexDescriptor(), + &isnull); + Assert(!isnull); + heapRelid = DatumGetObjectId(heapRelidDatum); + + result = (List *)ExpandVirtualGeneratedColumns((Node *) result, NULL, heapRelid); + + /* Now save a copy of the completed tree in the relcache entry. */ + oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt); + relation->rd_indpredExpand = copyObject(result); + MemoryContextSwitchTo(oldcxt); + + return result; +} + /* * RelationGetIndexAttrBitmap -- get a bitmap of index attribute numbers * @@ -6490,7 +6571,9 @@ load_relcache_init_file(bool shared) rel->rd_partcheckvalid = false; rel->rd_partcheckcxt = NULL; rel->rd_indexprs = NIL; + rel->rd_indexprsExpand = NIL; rel->rd_indpred = NIL; + rel->rd_indpredExpand = NIL; rel->rd_exclops = NULL; rel->rd_exclprocs = NULL; rel->rd_exclstrats = NULL; @@ -7019,3 +7102,46 @@ ResOwnerReleaseRelation(Datum res) RelationCloseCleanup((Relation) DatumGetPointer(res)); } + +Node * +ExpandVirtualGeneratedColumns(Node *node, Relation heapRelation, Oid heapRelId) +{ + bool opened_relation = false; + TupleDesc tupdesc; + + if (node == NULL || (heapRelation == NULL && heapRelId == InvalidOid)) + return node; + + if (heapRelation == NULL) + { + heapRelation = table_open(heapRelId, NoLock); + opened_relation = true; + } + + tupdesc = RelationGetDescr(heapRelation); + if ((tupdesc->constr && tupdesc->constr->has_generated_virtual)) + { + int j; + Bitmapset *indexattrs = NULL; + + pull_varattnos(node, 1, &indexattrs); + + j = -1; + while ((j = bms_next_member(indexattrs, j)) >= 0) + { + AttrNumber attno = j + FirstLowInvalidHeapAttributeNumber; + + if (attno > 0 && + TupleDescAttr(tupdesc, attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL) + { + node = expand_generated_columns_in_expr(node, heapRelation, 1); + break; + } + } + } + + if (opened_relation) + table_close(heapRelation, NoLock); + + return node; +} diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 53c13831..20a2e618 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -190,13 +190,17 @@ typedef struct IndexInfo /* expr trees for expression entries, or NIL if none */ List *ii_Expressions; /* list of Expr */ + List *ii_ExpressionsExpand; /* list of Expr */ /* exec state for expressions, or NIL if none */ List *ii_ExpressionsState; /* list of ExprState */ + List *ii_ExpressionsExpandState; /* list of ExprState */ /* partial-index predicate, or NIL if none */ List *ii_Predicate; /* list of Expr */ + List *ii_PredicateExpand; /* list of Expr */ /* exec state for expressions, or NIL if none */ ExprState *ii_PredicateState; + ExprState *ii_PredicateExpandState; /* Per-column exclusion operators, or NULL if none */ Oid *ii_ExclusionOps; /* array with one entry per column */ diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 27a2c681..9c65e123 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -1403,8 +1403,10 @@ typedef struct IndexOptInfo * print indextlist */ List *indexprs pg_node_attr(read_write_ignore); + List *indexprsExpand pg_node_attr(read_write_ignore); /* predicate if a partial index, else NIL */ List *indpred; + List *indpredExpand; /* targetlist representing index columns */ List *indextlist; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index fa07ebf8..7bd01b36 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -210,7 +210,9 @@ typedef struct RelationData struct FmgrInfo *rd_supportinfo; /* lookup info for support procedures */ int16 *rd_indoption; /* per-column AM-specific flags */ List *rd_indexprs; /* index expression trees, if any */ + List *rd_indexprsExpand; /* expanded index expression trees, if any */ List *rd_indpred; /* index predicate tree, if any */ + List *rd_indpredExpand; /* expanded index predicate tree, if any */ Oid *rd_exclops; /* OIDs of exclusion operators, if any */ Oid *rd_exclprocs; /* OIDs of exclusion ops' procs, if any */ uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */ diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 89c27aa1..6dd96fc2 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -58,8 +58,10 @@ extern List *RelationGetStatExtList(Relation relation); extern Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok); extern Oid RelationGetReplicaIndex(Relation relation); extern List *RelationGetIndexExpressions(Relation relation); +extern List *RelationGetIndexExpressionsExpand(Relation relation); extern List *RelationGetDummyIndexExpressions(Relation relation); extern List *RelationGetIndexPredicate(Relation relation); +extern List *RelationGetIndexPredicateExpand(Relation relation); extern bytea **RelationGetIndexAttOptions(Relation relation, bool copy); /* @@ -161,4 +163,6 @@ extern PGDLLIMPORT bool criticalRelcachesBuilt; /* should be used only by relcache.c and postinit.c */ extern PGDLLIMPORT bool criticalSharedRelcachesBuilt; +extern Node *ExpandVirtualGeneratedColumns(Node *node, Relation heapRelation, Oid heapRelId); + #endif /* RELCACHE_H */