* 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. By
- * joining based on equality on all columns which are part of any unique
- * index, we identify the rows on which we can use UPDATE without any problem.
- * If any column is NULL in either the old or new version of a row (or both),
- * we must use DELETE and INSERT, since there could be multiple rows which are
- * NOT DISTINCT FROM each other, and we could otherwise end up with the wrong
- * number of occurrences in the updated relation. 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 column of complex record type
- * (if matched).
+ * index on the materialized view, we have exactly the guarantee we need.
*
- * Once we have the diff table, we perform set-based DELETE, UPDATE, and
- * INSERT operations against the materialized view, and discard both temporary
+ * 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
+ * column of complex record type (if matched).
+ *
+ * Once we have the diff table, we perform set-based DELETE and INSERT
+ * operations against the materialized view, and discard both temporary
* tables.
*
* Everything from the generation of the new data to applying the differences
*/
resetStringInfo(&querybuf);
appendStringInfo(&querybuf,
- "SELECT x FROM %s x WHERE x IS NOT NULL AND EXISTS "
- "(SELECT * FROM %s y WHERE y IS NOT NULL "
- "AND (y.*) = (x.*) AND y.ctid <> x.ctid) LIMIT 1",
+ "SELECT newdata FROM %s newdata "
+ "WHERE newdata IS NOT NULL AND EXISTS "
+ "(SELECT * FROM %s newdata2 WHERE newdata2 IS NOT NULL "
+ "AND newdata2 OPERATOR(pg_catalog.=) newdata "
+ "AND newdata2.ctid OPERATOR(pg_catalog.<>) "
+ "newdata.ctid) LIMIT 1",
tempname, tempname);
if (SPI_execute(querybuf.data, false, 1) != SPI_OK_SELECT)
elog(ERROR, "SPI_exec failed: %s", querybuf.data);
resetStringInfo(&querybuf);
appendStringInfo(&querybuf,
"CREATE TEMP TABLE %s AS "
- "SELECT x.ctid AS tid, y FROM %s x FULL JOIN %s y ON (",
+ "SELECT mv.ctid AS tid, newdata "
+ "FROM %s mv FULL JOIN %s newdata ON (",
diffname, matviewname, tempname);
/*
foreach(indexoidscan, indexoidlist)
{
Oid indexoid = lfirst_oid(indexoidscan);
+ Relation indexRel;
HeapTuple indexTuple;
- Form_pg_index index;
+ Form_pg_index indexStruct;
+ indexRel = index_open(indexoid, RowExclusiveLock);
indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
if (!HeapTupleIsValid(indexTuple)) /* should not happen */
elog(ERROR, "cache lookup failed for index %u", indexoid);
- index = (Form_pg_index) GETSTRUCT(indexTuple);
+ indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
/* We're only interested if it is unique and valid. */
- if (index->indisunique && IndexIsValid(index))
+ if (indexStruct->indisunique && IndexIsValid(indexStruct))
{
- int numatts = index->indnatts;
+ int numatts = indexStruct->indnatts;
int i;
- bool expr = false;
- Relation indexRel;
/* Skip any index on an expression. */
- for (i = 0; i < numatts; i++)
- {
- if (index->indkey.values[i] == 0)
- {
- expr = true;
- break;
- }
- }
- if (expr)
+ if (RelationGetIndexExpressions(indexRel) != NIL)
{
+ index_close(indexRel, NoLock);
ReleaseSysCache(indexTuple);
continue;
}
/* Skip partial indexes. */
- indexRel = index_open(index->indexrelid, RowExclusiveLock);
if (RelationGetIndexPredicate(indexRel) != NIL)
{
index_close(indexRel, NoLock);
ReleaseSysCache(indexTuple);
continue;
}
+
/* Hold the locks, since we're about to run DML which needs them. */
index_close(indexRel, NoLock);
/* Add quals for all columns from this index. */
for (i = 0; i < numatts; i++)
{
- int attnum = index->indkey.values[i];
+ int attnum = indexStruct->indkey.values[i];
Oid type;
Oid op;
const char *colname;
appendStringInfoString(&querybuf, " AND ");
colname = quote_identifier(NameStr((tupdesc->attrs[attnum - 1])->attname));
- appendStringInfo(&querybuf, "y.%s ", colname);
+ 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, " x.%s", colname);
+ appendStringInfo(&querybuf, " mv.%s", colname);
foundUniqueIndex = true;
}
errhint("Create a UNIQUE index with no WHERE clause on one or more columns of the materialized view.")));
appendStringInfoString(&querybuf,
- " AND y = x) WHERE (y.*) IS DISTINCT FROM (x.*)"
+ " AND newdata = mv) WHERE newdata IS NULL OR mv IS NULL"
" ORDER BY tid");
/* Create the temporary "diff" table. */
/* Deletes must come before inserts; do them first. */
resetStringInfo(&querybuf);
appendStringInfo(&querybuf,
- "DELETE FROM %s WHERE ctid IN "
- "(SELECT d.tid FROM %s d "
- "WHERE d.tid IS NOT NULL "
- "AND (d.y) IS NOT DISTINCT FROM NULL)",
+ "DELETE FROM %s mv WHERE ctid OPERATOR(pg_catalog.=) ANY "
+ "(SELECT diff.tid FROM %s diff "
+ "WHERE diff.tid IS NOT NULL "
+ "AND diff.newdata IS NULL)",
matviewname, diffname);
if (SPI_exec(querybuf.data, 0) != SPI_OK_DELETE)
elog(ERROR, "SPI_exec failed: %s", querybuf.data);
- /* Updates before inserts gives a better chance at HOT updates. */
- resetStringInfo(&querybuf);
- appendStringInfo(&querybuf, "UPDATE %s x SET ", matviewname);
-
- {
- int i;
- bool targetColFound = false;
-
- for (i = 0; i < tupdesc->natts; i++)
- {
- const char *colname;
-
- if (tupdesc->attrs[i]->attisdropped)
- continue;
-
- if (usedForQual[i])
- continue;
-
- if (targetColFound)
- appendStringInfoString(&querybuf, ", ");
- targetColFound = true;
-
- colname = quote_identifier(NameStr((tupdesc->attrs[i])->attname));
- appendStringInfo(&querybuf, "%s = (d.y).%s", colname, colname);
- }
-
- if (targetColFound)
- {
- appendStringInfo(&querybuf,
- " FROM %s d "
- "WHERE d.tid IS NOT NULL AND x.ctid = d.tid",
- diffname);
-
- if (SPI_exec(querybuf.data, 0) != SPI_OK_UPDATE)
- elog(ERROR, "SPI_exec failed: %s", querybuf.data);
- }
- }
-
/* Inserts go last. */
resetStringInfo(&querybuf);
appendStringInfo(&querybuf,
- "INSERT INTO %s SELECT (y).* FROM %s WHERE tid IS NULL",
+ "INSERT INTO %s SELECT (diff.newdata).* "
+ "FROM %s diff WHERE tid IS NULL",
matviewname, diffname);
if (SPI_exec(querybuf.data, 0) != SPI_OK_INSERT)
elog(ERROR, "SPI_exec failed: %s", querybuf.data);