@@ -496,19 +496,14 @@ mv_GenerateOper(StringInfo buf, Oid opoid)
496496 * columns equal. The behavior of NULLs on equality tests and on UNIQUE
497497 * indexes turns out to be quite convenient here; the tests we need to make
498498 * are consistent with default behavior. If there is at least one UNIQUE
499- * index on the materialized view, we have exactly the guarantee we need. By
500- * joining based on equality on all columns which are part of any unique
501- * index, we identify the rows on which we can use UPDATE without any problem.
502- * If any column is NULL in either the old or new version of a row (or both),
503- * we must use DELETE and INSERT, since there could be multiple rows which are
504- * NOT DISTINCT FROM each other, and we could otherwise end up with the wrong
505- * number of occurrences in the updated relation. The temporary table used to
506- * hold the diff results contains just the TID of the old record (if matched)
507- * and the ROW from the new table as a single column of complex record type
508- * (if matched).
499+ * index on the materialized view, we have exactly the guarantee we need.
509500 *
510- * Once we have the diff table, we perform set-based DELETE, UPDATE, and
511- * INSERT operations against the materialized view, and discard both temporary
501+ * The temporary table used to hold the diff results contains just the TID of
502+ * the old record (if matched) and the ROW from the new table as a single
503+ * column of complex record type (if matched).
504+ *
505+ * Once we have the diff table, we perform set-based DELETE and INSERT
506+ * operations against the materialized view, and discard both temporary
512507 * tables.
513508 *
514509 * Everything from the generation of the new data to applying the differences
@@ -567,9 +562,12 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
567562 */
568563resetStringInfo (& querybuf );
569564appendStringInfo (& querybuf ,
570- "SELECT x FROM %s x WHERE x IS NOT NULL AND EXISTS "
571- "(SELECT * FROM %s y WHERE y IS NOT NULL "
572- "AND (y.*) = (x.*) AND y.ctid <> x.ctid) LIMIT 1" ,
565+ "SELECT newdata FROM %s newdata "
566+ "WHERE newdata IS NOT NULL AND EXISTS "
567+ "(SELECT * FROM %s newdata2 WHERE newdata2 IS NOT NULL "
568+ "AND newdata2 OPERATOR(pg_catalog.=) newdata "
569+ "AND newdata2.ctid OPERATOR(pg_catalog.<>) "
570+ "newdata.ctid) LIMIT 1" ,
573571tempname ,tempname );
574572if (SPI_execute (querybuf .data , false,1 )!= SPI_OK_SELECT )
575573elog (ERROR ,"SPI_exec failed: %s" ,querybuf .data );
@@ -587,7 +585,8 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
587585resetStringInfo (& querybuf );
588586appendStringInfo (& querybuf ,
589587"CREATE TEMP TABLE %s AS "
590- "SELECT x.ctid AS tid, y FROM %s x FULL JOIN %s y ON (" ,
588+ "SELECT mv.ctid AS tid, newdata "
589+ "FROM %s mv FULL JOIN %s newdata ON (" ,
591590diffname ,matviewname ,tempname );
592591
593592/*
@@ -603,52 +602,45 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
603602foreach (indexoidscan ,indexoidlist )
604603{
605604Oid indexoid = lfirst_oid (indexoidscan );
605+ Relation indexRel ;
606606HeapTuple indexTuple ;
607- Form_pg_index index ;
607+ Form_pg_index indexStruct ;
608608
609+ indexRel = index_open (indexoid ,RowExclusiveLock );
609610indexTuple = SearchSysCache1 (INDEXRELID ,ObjectIdGetDatum (indexoid ));
610611if (!HeapTupleIsValid (indexTuple ))/* should not happen */
611612elog (ERROR ,"cache lookup failed for index %u" ,indexoid );
612- index = (Form_pg_index )GETSTRUCT (indexTuple );
613+ indexStruct = (Form_pg_index )GETSTRUCT (indexTuple );
613614
614615/* We're only interested if it is unique and valid. */
615- if (index -> indisunique && IndexIsValid (index ))
616+ if (indexStruct -> indisunique && IndexIsValid (indexStruct ))
616617{
617- int numatts = index -> indnatts ;
618+ int numatts = indexStruct -> indnatts ;
618619int i ;
619- bool expr = false;
620- Relation indexRel ;
621620
622621/* Skip any index on an expression. */
623- for (i = 0 ;i < numatts ;i ++ )
624- {
625- if (index -> indkey .values [i ]== 0 )
626- {
627- expr = true;
628- break ;
629- }
630- }
631- if (expr )
622+ if (RelationGetIndexExpressions (indexRel )!= NIL )
632623{
624+ index_close (indexRel ,NoLock );
633625ReleaseSysCache (indexTuple );
634626continue ;
635627}
636628
637629/* Skip partial indexes. */
638- indexRel = index_open (index -> indexrelid ,RowExclusiveLock );
639630if (RelationGetIndexPredicate (indexRel )!= NIL )
640631{
641632index_close (indexRel ,NoLock );
642633ReleaseSysCache (indexTuple );
643634continue ;
644635}
636+
645637/* Hold the locks, since we're about to run DML which needs them. */
646638index_close (indexRel ,NoLock );
647639
648640/* Add quals for all columns from this index. */
649641for (i = 0 ;i < numatts ;i ++ )
650642{
651- int attnum = index -> indkey .values [i ];
643+ int attnum = indexStruct -> indkey .values [i ];
652644Oid type ;
653645Oid op ;
654646const char * colname ;
@@ -671,11 +663,11 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
671663appendStringInfoString (& querybuf ," AND " );
672664
673665colname = quote_identifier (NameStr ((tupdesc -> attrs [attnum - 1 ])-> attname ));
674- appendStringInfo (& querybuf ,"y .%s " ,colname );
666+ appendStringInfo (& querybuf ,"newdata .%s " ,colname );
675667type = attnumTypeId (matviewRel ,attnum );
676668op = lookup_type_cache (type ,TYPECACHE_EQ_OPR )-> eq_opr ;
677669mv_GenerateOper (& querybuf ,op );
678- appendStringInfo (& querybuf ,"x .%s" ,colname );
670+ appendStringInfo (& querybuf ,"mv .%s" ,colname );
679671
680672foundUniqueIndex = true;
681673}
@@ -693,7 +685,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
693685errhint ("Create a UNIQUE index with no WHERE clause on one or more columns of the materialized view." )));
694686
695687appendStringInfoString (& querybuf ,
696- " ANDy =x ) WHERE(y.*) ISDISTINCT FROM (x.*) "
688+ " ANDnewdata =mv ) WHEREnewdata ISNULL OR mv IS NULL "
697689" ORDER BY tid" );
698690
699691/* Create the temporary "diff" table. */
@@ -726,56 +718,19 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
726718/* Deletes must come before inserts; do them first. */
727719resetStringInfo (& querybuf );
728720appendStringInfo (& querybuf ,
729- "DELETE FROM %s WHERE ctidIN "
730- "(SELECTd .tid FROM %sd "
731- "WHEREd .tid IS NOT NULL "
732- "AND(d.y) IS NOT DISTINCT FROM NULL)" ,
721+ "DELETE FROM %smv WHERE ctidOPERATOR(pg_catalog.=) ANY "
722+ "(SELECTdiff .tid FROM %sdiff "
723+ "WHEREdiff .tid IS NOT NULL "
724+ "ANDdiff.newdata IS NULL)" ,
733725matviewname ,diffname );
734726if (SPI_exec (querybuf .data ,0 )!= SPI_OK_DELETE )
735727elog (ERROR ,"SPI_exec failed: %s" ,querybuf .data );
736728
737- /* Updates before inserts gives a better chance at HOT updates. */
738- resetStringInfo (& querybuf );
739- appendStringInfo (& querybuf ,"UPDATE %s x SET " ,matviewname );
740-
741- {
742- int i ;
743- bool targetColFound = false;
744-
745- for (i = 0 ;i < tupdesc -> natts ;i ++ )
746- {
747- const char * colname ;
748-
749- if (tupdesc -> attrs [i ]-> attisdropped )
750- continue ;
751-
752- if (usedForQual [i ])
753- continue ;
754-
755- if (targetColFound )
756- appendStringInfoString (& querybuf ,", " );
757- targetColFound = true;
758-
759- colname = quote_identifier (NameStr ((tupdesc -> attrs [i ])-> attname ));
760- appendStringInfo (& querybuf ,"%s = (d.y).%s" ,colname ,colname );
761- }
762-
763- if (targetColFound )
764- {
765- appendStringInfo (& querybuf ,
766- " FROM %s d "
767- "WHERE d.tid IS NOT NULL AND x.ctid = d.tid" ,
768- diffname );
769-
770- if (SPI_exec (querybuf .data ,0 )!= SPI_OK_UPDATE )
771- elog (ERROR ,"SPI_exec failed: %s" ,querybuf .data );
772- }
773- }
774-
775729/* Inserts go last. */
776730resetStringInfo (& querybuf );
777731appendStringInfo (& querybuf ,
778- "INSERT INTO %s SELECT (y).* FROM %s WHERE tid IS NULL" ,
732+ "INSERT INTO %s SELECT (diff.newdata).* "
733+ "FROM %s diff WHERE tid IS NULL" ,
779734matviewname ,diffname );
780735if (SPI_exec (querybuf .data ,0 )!= SPI_OK_INSERT )
781736elog (ERROR ,"SPI_exec failed: %s" ,querybuf .data );