diff options
Diffstat (limited to 'src/backend/commands/matview.c')
-rw-r--r-- | src/backend/commands/matview.c | 161 |
1 files changed, 112 insertions, 49 deletions
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index 9a8893e17bb..ea6321fc132 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -21,6 +21,8 @@ #include "catalog/catalog.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/pg_am.h" +#include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "commands/cluster.h" #include "commands/matview.h" @@ -39,7 +41,6 @@ #include "utils/rel.h" #include "utils/snapmgr.h" #include "utils/syscache.h" -#include "utils/typcache.h" typedef struct @@ -61,14 +62,11 @@ static void transientrel_shutdown(DestReceiver *self); static void transientrel_destroy(DestReceiver *self); static void refresh_matview_datafill(DestReceiver *dest, Query *query, const char *queryString); - static char *make_temptable_name_n(char *tempname, int n); -static void mv_GenerateOper(StringInfo buf, Oid opoid); - static void refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, int save_sec_context); static void refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence); - +static bool is_usable_unique_index(Relation indexRel); static void OpenMatViewIncrementalMaintenance(void); static void CloseMatViewIncrementalMaintenance(void); @@ -490,25 +488,6 @@ make_temptable_name_n(char *tempname, int n) return namebuf.data; } -static void -mv_GenerateOper(StringInfo buf, Oid opoid) -{ - HeapTuple opertup; - Form_pg_operator operform; - - opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid)); - if (!HeapTupleIsValid(opertup)) - elog(ERROR, "cache lookup failed for operator %u", opoid); - operform = (Form_pg_operator) GETSTRUCT(opertup); - Assert(operform->oprkind == 'b'); - - appendStringInfo(buf, "OPERATOR(%s.%s)", - quote_identifier(get_namespace_name(operform->oprnamespace)), - NameStr(operform->oprname)); - - ReleaseSysCache(opertup); -} - /* * refresh_by_match_merge * @@ -556,7 +535,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, List *indexoidlist; ListCell *indexoidscan; int16 relnatts; - bool *usedForQual; + Oid *opUsedForQual; initStringInfo(&querybuf); matviewRel = heap_open(matviewOid, NoLock); @@ -568,7 +547,6 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, diffname = make_temptable_name_n(tempname, 2); relnatts = matviewRel->rd_rel->relnatts; - usedForQual = (bool *) palloc0(sizeof(bool) * relnatts); /* Open SPI context. */ if (SPI_connect() != SPI_OK_CONNECT) @@ -632,45 +610,82 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, * include all rows. */ tupdesc = matviewRel->rd_att; + opUsedForQual = (Oid *) palloc0(sizeof(Oid) * relnatts); foundUniqueIndex = false; + indexoidlist = RelationGetIndexList(matviewRel); foreach(indexoidscan, indexoidlist) { Oid indexoid = lfirst_oid(indexoidscan); Relation indexRel; - Form_pg_index indexStruct; indexRel = index_open(indexoid, RowExclusiveLock); - indexStruct = indexRel->rd_index; - - /* - * We're only interested if it is unique, valid, contains no - * expressions, and is not partial. - */ - if (indexStruct->indisunique && - IndexIsValid(indexStruct) && - RelationGetIndexExpressions(indexRel) == NIL && - RelationGetIndexPredicate(indexRel) == NIL) + if (is_usable_unique_index(indexRel)) { + Form_pg_index indexStruct = indexRel->rd_index; int numatts = indexStruct->indnatts; + oidvector *indclass; + Datum indclassDatum; + bool isnull; int i; + /* Must get indclass the hard way. */ + indclassDatum = SysCacheGetAttr(INDEXRELID, + indexRel->rd_indextuple, + Anum_pg_index_indclass, + &isnull); + Assert(!isnull); + indclass = (oidvector *) DatumGetPointer(indclassDatum); + /* Add quals for all columns from this index. */ for (i = 0; i < numatts; i++) { int attnum = indexStruct->indkey.values[i]; - Oid type; + Oid opclass = indclass->values[i]; + Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); + Oid attrtype = attr->atttypid; + HeapTuple cla_ht; + Form_pg_opclass cla_tup; + Oid opfamily; + Oid opcintype; Oid op; - const char *colname; + const char *leftop; + const char *rightop; + + /* + * Identify the equality operator associated with this index + * column. First we need to look up the column's opclass. + */ + cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass)); + if (!HeapTupleIsValid(cla_ht)) + elog(ERROR, "cache lookup failed for opclass %u", opclass); + cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht); + Assert(cla_tup->opcmethod == BTREE_AM_OID); + opfamily = cla_tup->opcfamily; + opcintype = cla_tup->opcintype; + ReleaseSysCache(cla_ht); + + op = get_opfamily_member(opfamily, opcintype, opcintype, + BTEqualStrategyNumber); + if (!OidIsValid(op)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + BTEqualStrategyNumber, opcintype, opcintype, opfamily); /* - * Only include the column once regardless of how many times - * it shows up in how many indexes. + * If we find the same column with the same equality semantics + * in more than one index, we only need to emit the equality + * clause once. + * + * Since we only remember the last equality operator, this + * code could be fooled into emitting duplicate clauses given + * multiple indexes with several different opclasses ... but + * that's so unlikely it doesn't seem worth spending extra + * code to avoid. */ - if (usedForQual[attnum - 1]) + if (opUsedForQual[attnum - 1] == op) continue; - usedForQual[attnum - 1] = true; + opUsedForQual[attnum - 1] = op; /* * Actually add the qual, ANDed with any others. @@ -678,12 +693,15 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, if (foundUniqueIndex) appendStringInfoString(&querybuf, " AND "); - colname = quote_identifier(NameStr((tupdesc->attrs[attnum - 1])->attname)); - appendStringInfo(&querybuf, "newdata.%s ", colname); - type = attnumTypeId(matviewRel, attnum); - op = lookup_type_cache(type, TYPECACHE_EQ_OPR)->eq_opr; - mv_GenerateOper(&querybuf, op); - appendStringInfo(&querybuf, " mv.%s", colname); + leftop = quote_qualified_identifier("newdata", + NameStr(attr->attname)); + rightop = quote_qualified_identifier("mv", + NameStr(attr->attname)); + + generate_operator_clause(&querybuf, + leftop, attrtype, + op, + rightop, attrtype); foundUniqueIndex = true; } @@ -775,6 +793,51 @@ refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence) RecentXmin, ReadNextMultiXactId(), relpersistence); } +/* + * Check whether specified index is usable for match merge. + */ +static bool +is_usable_unique_index(Relation indexRel) +{ + Form_pg_index indexStruct = indexRel->rd_index; + + /* + * Must be unique, valid, immediate, non-partial, and be defined over + * plain user columns (not expressions). We also require it to be a + * btree. Even if we had any other unique index kinds, we'd not know how + * to identify the corresponding equality operator, nor could we be sure + * that the planner could implement the required FULL JOIN with non-btree + * operators. + */ + if (indexStruct->indisunique && + indexStruct->indimmediate && + indexRel->rd_rel->relam == BTREE_AM_OID && + IndexIsValid(indexStruct) && + RelationGetIndexPredicate(indexRel) == NIL && + indexStruct->indnatts > 0) + { + /* + * The point of groveling through the index columns individually is to + * reject both index expressions and system columns. Currently, + * matviews couldn't have OID columns so there's no way to create an + * index on a system column; but maybe someday that wouldn't be true, + * so let's be safe. + */ + int numatts = indexStruct->indnatts; + int i; + + for (i = 0; i < numatts; i++) + { + int attnum = indexStruct->indkey.values[i]; + + if (attnum <= 0) + return false; + } + return true; + } + return false; +} + /* * This should be used to test whether the backend is in a context where it is |