@@ -496,19 +496,14 @@ mv_GenerateOper(StringInfo buf, Oid opoid)
496
496
* columns equal. The behavior of NULLs on equality tests and on UNIQUE
497
497
* indexes turns out to be quite convenient here; the tests we need to make
498
498
* 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.
509
500
*
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
512
507
* tables.
513
508
*
514
509
* Everything from the generation of the new data to applying the differences
@@ -567,9 +562,12 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
567
562
*/
568
563
resetStringInfo (& querybuf );
569
564
appendStringInfo (& 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" ,
573
571
tempname , tempname );
574
572
if (SPI_execute (querybuf .data , false, 1 ) != SPI_OK_SELECT )
575
573
elog (ERROR , "SPI_exec failed: %s" , querybuf .data );
@@ -587,7 +585,8 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
587
585
resetStringInfo (& querybuf );
588
586
appendStringInfo (& querybuf ,
589
587
"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 (" ,
591
590
diffname , matviewname , tempname );
592
591
593
592
/*
@@ -603,52 +602,45 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
603
602
foreach (indexoidscan , indexoidlist )
604
603
{
605
604
Oid indexoid = lfirst_oid (indexoidscan );
605
+ Relation indexRel ;
606
606
HeapTuple indexTuple ;
607
- Form_pg_index index ;
607
+ Form_pg_index indexStruct ;
608
608
609
+ indexRel = index_open (indexoid , RowExclusiveLock );
609
610
indexTuple = SearchSysCache1 (INDEXRELID , ObjectIdGetDatum (indexoid ));
610
611
if (!HeapTupleIsValid (indexTuple )) /* should not happen */
611
612
elog (ERROR , "cache lookup failed for index %u" , indexoid );
612
- index = (Form_pg_index ) GETSTRUCT (indexTuple );
613
+ indexStruct = (Form_pg_index ) GETSTRUCT (indexTuple );
613
614
614
615
/* We're only interested if it is unique and valid. */
615
- if (index -> indisunique && IndexIsValid (index ))
616
+ if (indexStruct -> indisunique && IndexIsValid (indexStruct ))
616
617
{
617
- int numatts = index -> indnatts ;
618
+ int numatts = indexStruct -> indnatts ;
618
619
int i ;
619
- bool expr = false;
620
- Relation indexRel ;
621
620
622
621
/* 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 )
632
623
{
624
+ index_close (indexRel , NoLock );
633
625
ReleaseSysCache (indexTuple );
634
626
continue ;
635
627
}
636
628
637
629
/* Skip partial indexes. */
638
- indexRel = index_open (index -> indexrelid , RowExclusiveLock );
639
630
if (RelationGetIndexPredicate (indexRel ) != NIL )
640
631
{
641
632
index_close (indexRel , NoLock );
642
633
ReleaseSysCache (indexTuple );
643
634
continue ;
644
635
}
636
+
645
637
/* Hold the locks, since we're about to run DML which needs them. */
646
638
index_close (indexRel , NoLock );
647
639
648
640
/* Add quals for all columns from this index. */
649
641
for (i = 0 ; i < numatts ; i ++ )
650
642
{
651
- int attnum = index -> indkey .values [i ];
643
+ int attnum = indexStruct -> indkey .values [i ];
652
644
Oid type ;
653
645
Oid op ;
654
646
const char * colname ;
@@ -671,11 +663,11 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
671
663
appendStringInfoString (& querybuf , " AND " );
672
664
673
665
colname = quote_identifier (NameStr ((tupdesc -> attrs [attnum - 1 ])-> attname ));
674
- appendStringInfo (& querybuf , "y .%s " , colname );
666
+ appendStringInfo (& querybuf , "newdata .%s " , colname );
675
667
type = attnumTypeId (matviewRel , attnum );
676
668
op = lookup_type_cache (type , TYPECACHE_EQ_OPR )-> eq_opr ;
677
669
mv_GenerateOper (& querybuf , op );
678
- appendStringInfo (& querybuf , " x .%s" , colname );
670
+ appendStringInfo (& querybuf , " mv .%s" , colname );
679
671
680
672
foundUniqueIndex = true;
681
673
}
@@ -693,7 +685,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
693
685
errhint ("Create a UNIQUE index with no WHERE clause on one or more columns of the materialized view." )));
694
686
695
687
appendStringInfoString (& querybuf ,
696
- " AND y = x ) WHERE (y.*) IS DISTINCT FROM (x.*) "
688
+ " AND newdata = mv ) WHERE newdata IS NULL OR mv IS NULL "
697
689
" ORDER BY tid" );
698
690
699
691
/* Create the temporary "diff" table. */
@@ -726,56 +718,19 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid)
726
718
/* Deletes must come before inserts; do them first. */
727
719
resetStringInfo (& querybuf );
728
720
appendStringInfo (& querybuf ,
729
- "DELETE FROM %s WHERE ctid IN "
730
- "(SELECT d .tid FROM %s d "
731
- "WHERE d .tid IS NOT NULL "
732
- "AND (d.y) IS NOT DISTINCT FROM NULL)" ,
721
+ "DELETE FROM %s mv WHERE ctid OPERATOR(pg_catalog.=) ANY "
722
+ "(SELECT diff .tid FROM %s diff "
723
+ "WHERE diff .tid IS NOT NULL "
724
+ "AND diff.newdata IS NULL)" ,
733
725
matviewname , diffname );
734
726
if (SPI_exec (querybuf .data , 0 ) != SPI_OK_DELETE )
735
727
elog (ERROR , "SPI_exec failed: %s" , querybuf .data );
736
728
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
-
775
729
/* Inserts go last. */
776
730
resetStringInfo (& querybuf );
777
731
appendStringInfo (& 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" ,
779
734
matviewname , diffname );
780
735
if (SPI_exec (querybuf .data , 0 ) != SPI_OK_INSERT )
781
736
elog (ERROR , "SPI_exec failed: %s" , querybuf .data );
0 commit comments