2121#include "catalog/catalog.h"
2222#include "catalog/indexing.h"
2323#include "catalog/namespace.h"
24+ #include "catalog/pg_am.h"
25+ #include "catalog/pg_opclass.h"
2426#include "catalog/pg_operator.h"
2527#include "commands/cluster.h"
2628#include "commands/matview.h"
3941#include "utils/rel.h"
4042#include "utils/snapmgr.h"
4143#include "utils/syscache.h"
42- #include "utils/typcache.h"
4344
4445
4546typedef struct
@@ -61,14 +62,11 @@ static void transientrel_shutdown(DestReceiver *self);
6162static void transientrel_destroy (DestReceiver * self );
6263static void refresh_matview_datafill (DestReceiver * dest , Query * query ,
6364 const char * queryString );
64-
6565static char * make_temptable_name_n (char * tempname , int n );
66- static void mv_GenerateOper (StringInfo buf , Oid opoid );
67-
6866static void refresh_by_match_merge (Oid matviewOid , Oid tempOid , Oid relowner ,
6967 int save_sec_context );
7068static void refresh_by_heap_swap (Oid matviewOid , Oid OIDNewHeap , char relpersistence );
71-
69+ static bool is_usable_unique_index ( Relation indexRel );
7270static void OpenMatViewIncrementalMaintenance (void );
7371static void CloseMatViewIncrementalMaintenance (void );
7472
@@ -490,25 +488,6 @@ make_temptable_name_n(char *tempname, int n)
490488 return namebuf .data ;
491489}
492490
493- static void
494- mv_GenerateOper (StringInfo buf , Oid opoid )
495- {
496- HeapTuple opertup ;
497- Form_pg_operator operform ;
498-
499- opertup = SearchSysCache1 (OPEROID , ObjectIdGetDatum (opoid ));
500- if (!HeapTupleIsValid (opertup ))
501- elog (ERROR , "cache lookup failed for operator %u" , opoid );
502- operform = (Form_pg_operator ) GETSTRUCT (opertup );
503- Assert (operform -> oprkind == 'b' );
504-
505- appendStringInfo (buf , "OPERATOR(%s.%s)" ,
506- quote_identifier (get_namespace_name (operform -> oprnamespace )),
507- NameStr (operform -> oprname ));
508-
509- ReleaseSysCache (opertup );
510- }
511-
512491/*
51
6D40
3492 * refresh_by_match_merge
514493 *
@@ -556,7 +535,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
556535 List * indexoidlist ;
557536 ListCell * indexoidscan ;
558537 int16 relnatts ;
559- bool * usedForQual ;
538+ Oid * opUsedForQual ;
560539
561540 initStringInfo (& querybuf );
562541 matviewRel = heap_open (matviewOid , NoLock );
@@ -568,7 +547,6 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
568547 diffname = make_temptable_name_n (tempname , 2 );
569548
570549 relnatts = matviewRel -> rd_rel -> relnatts ;
571- usedForQual = (bool * ) palloc0 (sizeof (bool ) * relnatts );
572550
573551 /* Open SPI context. */
574552 if (SPI_connect () != SPI_OK_CONNECT )
@@ -632,58 +610,98 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
632610 * include all rows.
633611 */
634612 tupdesc = matviewRel -> rd_att ;
613+ opUsedForQual = (Oid * ) palloc0 (sizeof (Oid ) * relnatts );
635614 foundUniqueIndex = false;
615+
636616 indexoidlist = RelationGetIndexList (matviewRel );
637617
638618 foreach (indexoidscan , indexoidlist )
639619 {
640620 Oid indexoid = lfirst_oid (indexoidscan );
641621 Relation indexRel ;
642- Form_pg_index indexStruct ;
643622
644623 indexRel = index_open (indexoid , RowExclusiveLock );
645- indexStruct = indexRel -> rd_index ;
646-
647- /*
648- * We're only interested if it is unique, valid, contains no
649- * expressions, and is not partial.
650- */
651- if (indexStruct -> indisunique &&
652- IndexIsValid (indexStruct ) &&
653- RelationGetIndexExpressions (indexRel ) == NIL &&
654- RelationGetIndexPredicate (indexRel ) == NIL )
624+ if (is_usable_unique_index (indexRel ))
655625 {
626+ Form_pg_index indexStruct = indexRel -> rd_index ;
656627 int numatts = indexStruct -> indnatts ;
628+ oidvector * indclass ;
629+ Datum indclassDatum ;
630+ bool isnull ;
657631 int i ;
658632
633+ /* Must get indclass the hard way. */
634+ indclassDatum = SysCacheGetAttr (INDEXRELID ,
635+ indexRel -> rd_indextuple ,
636+ Anum_pg_index_indclass ,
637+ & isnull );
638+ Assert (!isnull );
639+ indclass = (oidvector * ) DatumGetPointer (indclassDatum );
640+
659641 /* Add quals for all columns from this index. */
660642 for (i = 0 ; i < numatts ; i ++ )
661643 {
662644 int attnum = indexStruct -> indkey .values [i ];
663- Oid type ;
645+ Oid opclass = indclass -> values [i ];
646+ Form_pg_attribute attr = TupleDescAttr (tupdesc , attnum - 1 );
647+ Oid attrtype = attr -> atttypid ;
648+ HeapTuple cla_ht ;
649+ Form_pg_opclass cla_tup ;
650+ Oid opfamily ;
651+ Oid opcintype ;
664652 Oid op ;
665- const char * colname ;
653+ const char * leftop ;
654+ const char * rightop ;
655+
656+ /*
657+ * Identify the equality operator associated with this index
658+ * column. First we need to look up the column's opclass.
659+ */
660+ cla_ht = SearchSysCache1 (CLAOID , ObjectIdGetDatum (opclass ));
661+ if (!HeapTupleIsValid (cla_ht ))
662+ elog (ERROR , "cache lookup failed for opclass %u" , opclass );
663+ cla_tup = (Form_pg_opclass ) GETSTRUCT (cla_ht );
664+ Assert (cla_tup -> opcmethod == BTREE_AM_OID );
665+ opfamily = cla_tup -> opcfamily ;
666+ opcintype = cla_tup -> opcintype ;
667+ ReleaseSysCache (cla_ht );
668+
669+ op = get_opfamily_member (opfamily , opcintype , opcintype ,
670+ BTEqualStrategyNumber );
671+ if (!OidIsValid (op ))
672+ elog (ERROR , "missing operator %d(%u,%u) in opfamily %u" ,
673+ BTEqualStrategyNumber , opcintype , opcintype , opfamily );
666674
667675 /*
668- * Only include the column once regardless of how many times
669- * it shows up in how many indexes.
676+ * If we find the same column with the same equality semantics
677+ * in more than one index, we only need to emit the equality
678+ * clause once.
679+ *
680+ * Since we only remember the last equality operator, this
681+ * code could be fooled into emitting duplicate clauses given
682+ * multiple indexes with several different opclasses ... but
683+ * that's so unlikely it doesn't seem worth spending extra
684+ * code to avoid.
670685 */
671- if (usedForQual [attnum - 1 ])
686+ if (opUsedForQual [at
9E88
tnum - 1 ] == op )
672687 continue ;
673- usedForQual [attnum - 1 ] = true ;
688+ opUsedForQual [attnum - 1 ] = op ;
674689
675690 /*
676691 * Actually add the qual, ANDed with any others.
677692 */
678693 if (foundUniqueIndex )
679694 appendStringInfoString (& querybuf , " AND " );
680695
681- colname = quote_identifier (NameStr ((tupdesc -> attrs [attnum - 1 ])-> attname ));
682- appendStringInfo (& querybuf , "newdata.%s " , colname );
683- type = attnumTypeId (matviewRel , attnum );
684- op = lookup_type_cache (type , TYPECACHE_EQ_OPR )-> eq_opr ;
685- mv_GenerateOper (& querybuf , op );
686- appendStringInfo (& querybuf , " mv.%s" , colname );
696+ leftop = quote_qualified_identifier ("newdata" ,
697+ NameStr (attr -> attname ));
698+ rightop = quote_qualified_identifier ("mv" ,
699+ NameStr (attr -> attname ));
700+
701+ generate_operator_clause (& querybuf ,
702+ leftop , attrtype ,
703+ op ,
704+ rightop , attrtype );
687705
688706 foundUniqueIndex = true;
689707 }
@@ -775,6 +793,51 @@ refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence)
775793 RecentXmin , ReadNextMultiXactId (), relpersistence );
776794}
777795
796+ /*
797+ * Check whether specified index is usable for match merge.
798+ */
799+ static bool
800+ is_usable_unique_index (Relation indexRel )
801+ {
802+ Form_pg_index indexStruct = indexRel -> rd_index ;
803+
804+ /*
805+ * Must be unique, valid, immediate, non-partial, and be defined over
806+ * plain user columns (not expressions). We also require it to be a
807+ * btree. Even if we had any other unique index kinds, we'd not know how
808+ * to identify the corresponding equality operator, nor could we be sure
809+ * that the planner could implement the required FULL JOIN with non-btree
810+ * operators.
811+ */
812+ if (indexStruct -> indisunique &&
813+ indexStruct -> indimmediate &&
814+ indexRel -> rd_rel -> relam == BTREE_AM_OID &&
815+ IndexIsValid (indexStruct ) &&
816+ RelationGetIndexPredicate (indexRel ) == NIL &&
817+ indexStruct -> indnatts > 0 )
818+ {
819+ /*
820+ * The point of groveling through the index columns individually is to
821+ * reject both index expressions and system columns. Currently,
822+ * matviews couldn't have OID columns so there's no way to create an
823+ * index on a system column; but maybe someday that wouldn't be true,
824+ * so let's be safe.
825+ */
826+ int numatts = indexStruct -> indnatts ;
827+ int i ;
828+
829+ for (i = 0 ; i < numatts ; i ++ )
830+ {
831+ int attnum = indexStruct -> indkey .values [i ];
832+
833+ if (attnum <= 0 )
834+ return false;
835+ }
836+ return true;
837+ }
D306
838+ return false;
839+ }
840+
778841
779842/*
780843 * This should be used to test whether the backend is in a context where it is
0 commit comments