diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index 869c586..fbf6af8 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -216,6 +216,46 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, "the rule for materialized view \"%s\" is not a single action", RelationGetRelationName(matviewRel)); + /* When CONCURRENTLY refresh, we need at least one unique index. */ + if (concurrent) + { + List *indexoidlist = RelationGetIndexList(matviewRel); + ListCell *indexoidscan; + bool hasUniqueIndex = false; + + foreach(indexoidscan, indexoidlist) + { + Oid indexoid = lfirst_oid(indexoidscan); + Relation indexRel; + Form_pg_index indexStruct; + + indexRel = index_open(indexoid, AccessShareLock); + indexStruct = indexRel->rd_index; + + if (indexStruct->indisunique && + IndexIsValid(indexStruct) && + RelationGetIndexExpressions(indexRel) == NIL && + RelationGetIndexPredicate(indexRel) == NIL) + { + hasUniqueIndex = true; + index_close(indexRel, AccessShareLock); + break; + } + + index_close(indexRel, AccessShareLock); + } + + list_free(indexoidlist); + + if (!hasUniqueIndex) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("cannot refresh materialized view \"%s\" concurrently", + quote_qualified_identifier(get_namespace_name(RelationGetNamespace(matviewRel)), + RelationGetRelationName(matviewRel))), + errhint("Create a unique index with no WHERE clause on one or more columns of the materialized view."))); + } + /* * The stored query was rewritten at the time of the MV definition, but * has not been scribbled on by the planner. @@ -523,8 +563,8 @@ mv_GenerateOper(StringInfo buf, Oid opoid) * in the face of rows which have at least one NULL value, with all non-NULL * columns equal. The behavior of NULLs on equality tests and on UNIQUE * indexes turns out to be quite convenient here; the tests we need to make - * are consistent with default behavior. If there is at least one UNIQUE - * index on the materialized view, we have exactly the guarantee we need. + * are consistent with default behavior. There must be at least one UNIQUE + * index on the materialized view, which is the guarantee we need. * * The temporary table used to hold the diff results contains just the TID of * the old record (if matched) and the ROW from the new table as a single @@ -695,12 +735,8 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, list_free(indexoidlist); - if (!foundUniqueIndex) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot refresh materialized view \"%s\" concurrently", - matviewname), - errhint("Create a unique index with no WHERE clause on one or more columns of the materialized view."))); + /* We have guarantee that materialized view has at least one UNIQUE index */ + Assert(foundUniqueIndex); appendStringInfoString(&querybuf, " AND newdata OPERATOR(pg_catalog.*=) mv) "