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"
4042#include "utils/rel.h"
4143#include "utils/snapmgr.h"
4244#include "utils/syscache.h"
43- #include "utils/typcache.h"
4445
4546
4647typedef struct
@@ -62,14 +63,11 @@ static void transientrel_shutdown(DestReceiver *self);
6263static void transientrel_destroy (DestReceiver * self );
6364static uint64 refresh_matview_datafill (DestReceiver * dest ,Query * query ,
6465const char * queryString );
65-
6666static char * make_temptable_name_n (char * tempname ,int n );
67- static void mv_GenerateOper (StringInfo buf ,Oid opoid );
68-
6967static void refresh_by_match_merge (Oid matviewOid ,Oid tempOid ,Oid relowner ,
7068int save_sec_context );
7169static void refresh_by_heap_swap (Oid matviewOid ,Oid OIDNewHeap ,char relpersistence );
72-
70+ static bool is_usable_unique_index ( Relation indexRel );
7371static void OpenMatViewIncrementalMaintenance (void );
7472static void CloseMatViewIncrementalMaintenance (void );
7573
@@ -230,23 +228,12 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
230228{
231229Oid indexoid = lfirst_oid (indexoidscan );
232230Relation indexRel ;
233- Form_pg_index indexStruct ;
234231
235232indexRel = index_open (indexoid ,AccessShareLock );
236- indexStruct = indexRel -> rd_index ;
237-
238- if (indexStruct -> indisunique &&
239- IndexIsValid (indexStruct )&&
240- RelationGetIndexExpressions (indexRel )== NIL &&
241- RelationGetIndexPredicate (indexRel )== NIL &&
242- indexStruct -> indnatts > 0 )
243- {
244- hasUniqueIndex = true;
245- index_close (indexRel ,AccessShareLock );
246- break ;
247- }
248-
233+ hasUniqueIndex = is_usable_unique_index (indexRel );
249234index_close (indexRel ,AccessShareLock );
235+ if (hasUniqueIndex )
236+ break ;
250237}
251238
252239list_free (indexoidlist );
@@ -557,25 +544,6 @@ make_temptable_name_n(char *tempname, int n)
557544return namebuf .data ;
558545}
559546
560- static void
561- mv_GenerateOper (StringInfo buf ,Oid opoid )
562- {
563- HeapTuple opertup ;
564- Form_pg_operator operform ;
565-
566- opertup = SearchSysCache1 (OPEROID ,ObjectIdGetDatum (opoid ));
567- if (!HeapTupleIsValid (opertup ))
568- elog (ERROR ,"cache lookup failed for operator %u" ,opoid );
569- operform = (Form_pg_operator )GETSTRUCT (opertup );
570- Assert (operform -> oprkind == 'b' );
571-
572- appendStringInfo (buf ,"OPERATOR(%s.%s)" ,
573- quote_identifier (get_namespace_name (operform -> oprnamespace )),
574- NameStr (operform -> oprname ));
575-
576- ReleaseSysCache (opertup );
577- }
578-
579547/*
580548 * refresh_by_match_merge
581549 *
@@ -623,7 +591,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
623591List * indexoidlist ;
624592ListCell * indexoidscan ;
625593int16 relnatts ;
626- bool * usedForQual ;
594+ Oid * opUsedForQual ;
627595
628596initStringInfo (& querybuf );
629597matviewRel = heap_open (matviewOid ,NoLock );
@@ -635,7 +603,6 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
635603diffname = make_temptable_name_n (tempname ,2 );
636604
637605relnatts = matviewRel -> rd_rel -> relnatts ;
638- usedForQual = (bool * )palloc0 (sizeof (bool )* relnatts );
639606
640607/* Open SPI context. */
641608if (SPI_connect ()!= SPI_OK_CONNECT )
@@ -699,59 +666,98 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
699666 * include all rows.
700667 */
701668tupdesc = matviewRel -> rd_att ;
669+ opUsedForQual = (Oid * )palloc0 (sizeof (Oid )* relnatts );
702670foundUniqueIndex = false;
671+
703672indexoidlist = RelationGetIndexList (matviewRel );
704673
705674foreach (indexoidscan ,indexoidlist )
706675{
707676Oid indexoid = lfirst_oid (indexoidscan );
708677Relation indexRel ;
709- Form_pg_index indexStruct ;
710678
711679indexRel = index_open (indexoid ,RowExclusiveLock );
712- indexStruct = indexRel -> rd_index ;
713-
714- /*
715- * We're only interested if it is unique, valid, contains no
716- * expressions, and is not partial.
717- */
718- if (indexStruct -> indisunique &&
719- IndexIsValid (indexStruct )&&
720- RelationGetIndexExpressions (indexRel )== NIL &&
721- RelationGetIndexPredicate (indexRel )== NIL )
680+ if (is_usable_unique_index (indexRel ))
722681{
682+ Form_pg_index indexStruct = indexRel -> rd_index ;
723683int numatts = indexStruct -> indnatts ;
684+ oidvector * indclass ;
685+ Datum indclassDatum ;
686+ bool isnull ;
724687int i ;
725688
689+ /* Must get indclass the hard way. */
690+ indclassDatum = SysCacheGetAttr (INDEXRELID ,
691+ indexRel -> rd_indextuple ,
692+ Anum_pg_index_indclass ,
693+ & isnull );
694+ Assert (!isnull );
695+ indclass = (oidvector * )DatumGetPointer (indclassDatum );
696+
726697/* Add quals for all columns from this index. */
727698for (i = 0 ;i < numatts ;i ++ )
728699{
729700int attnum = indexStruct -> indkey .values [i ];
701+ Oid opclass = indclass -> values [i ];
730702Form_pg_attribute attr = TupleDescAttr (tupdesc ,attnum - 1 );
731- Oid type ;
703+ Oid attrtype = attr -> atttypid ;
704+ HeapTuple cla_ht ;
705+ Form_pg_opclass cla_tup ;
706+ Oid opfamily ;
707+ Oid opcintype ;
732708Oid op ;
733- const char * colname ;
709+ const char * leftop ;
710+ const char * rightop ;
734711
735712/*
736- *Only include thecolumn once regardless of how many times
737- *it shows up in how many indexes .
713+ *Identify theequality operator associated with this index
714+ *column. First we need to look up the column's opclass .
738715 */
739- if (usedForQual [attnum - 1 ])
716+ cla_ht = SearchSysCache1 (CLAOID ,ObjectIdGetDatum (opclass ));
717+ if (!HeapTupleIsValid (cla_ht ))
718+ elog (ERROR ,"cache lookup failed for opclass %u" ,opclass );
719+ cla_tup = (Form_pg_opclass )GETSTRUCT (cla_ht );
720+ Assert (cla_tup -> opcmethod == BTREE_AM_OID );
721+ opfamily = cla_tup -> opcfamily ;
722+ opcintype = cla_tup -> opcintype ;
723+ ReleaseSysCache (cla_ht );
724+
725+ op = get_opfamily_member (opfamily ,opcintype ,opcintype ,
726+ BTEqualStrategyNumber );
727+ if (!OidIsValid (op ))
728+ elog (ERROR ,"missing operator %d(%u,%u) in opfamily %u" ,
729+ BTEqualStrategyNumber ,opcintype ,opcintype ,opfamily );
730+
731+ /*
732+ * If we find the same column with the same equality semantics
733+ * in more than one index, we only need to emit the equality
734+ * clause once.
735+ *
736+ * Since we only remember the last equality operator, this
737+ * code could be fooled into emitting duplicate clauses given
738+ * multiple indexes with several different opclasses ... but
739+ * that's so unlikely it doesn't seem worth spending extra
740+ * code to avoid.
741+ */
742+ if (opUsedForQual [attnum - 1 ]== op )
740743continue ;
741- usedForQual [attnum - 1 ]= true ;
744+ opUsedForQual [attnum - 1 ]= op ;
742745
743746/*
744747 * Actually add the qual, ANDed with any others.
745748 */
746749if (foundUniqueIndex )
747750appendStringInfoString (& querybuf ," AND " );
748751
749- colname = quote_identifier (NameStr (attr -> attname ));
750- appendStringInfo (& querybuf ,"newdata.%s " ,colname );
751- type = attnumTypeId (matviewRel ,attnum );
752- op = lookup_type_cache (type ,TYPECACHE_EQ_OPR )-> eq_opr ;
753- mv_GenerateOper (& querybuf ,op );
754- appendStringInfo (& querybuf ," mv.%s" ,colname );
752+ leftop = quote_qualified_identifier ("newdata" ,
753+ NameStr (attr -> attname ));
754+ rightop = quote_qualified_identifier ("mv" ,
755+ NameStr (attr -> attname ));
756+
757+ generate_operator_clause (& querybuf ,
758+ leftop ,attrtype ,
759+ op ,
760+ rightop ,attrtype );
755761
756762foundUniqueIndex = true;
757763}
@@ -764,11 +770,11 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
764770list_free (indexoidlist );
765771
766772/*
767- * There must be at least one unique index on the matview.
773+ * There must be at least oneusable unique index on the matview.
768774 *
769775 * ExecRefreshMatView() checks that after taking the exclusive lock on the
770776 * matview. So at least one unique index is guaranteed to exist here
771- * because the lock is still being held.
777+ * because the lock is still being held; so an Assert seems sufficient .
772778 */
773779Assert (foundUniqueIndex );
774780
@@ -845,6 +851,51 @@ refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence)
845851RecentXmin ,ReadNextMultiXactId (),relpersistence );
846852}
847853
854+ /*
855+ * Check whether specified index is usable for match merge.
856+ */
857+ static bool
858+ is_usable_unique_index (Relation indexRel )
859+ {
860+ Form_pg_index indexStruct = indexRel -> rd_index ;
861+
862+ /*
863+ * Must be unique, valid, immediate, non-partial, and be defined over
864+ * plain user columns (not expressions). We also require it to be a
865+ * btree. Even if we had any other unique index kinds, we'd not know how
866+ * to identify the corresponding equality operator, nor could we be sure
867+ * that the planner could implement the required FULL JOIN with non-btree
868+ * operators.
869+ */
870+ if (indexStruct -> indisunique &&
871+ indexStruct -> indimmediate &&
872+ indexRel -> rd_rel -> relam == BTREE_AM_OID &&
873+ IndexIsValid (indexStruct )&&
874+ RelationGetIndexPredicate (indexRel )== NIL &&
875+ indexStruct -> indnatts > 0 )
876+ {
877+ /*
878+ * The point of groveling through the index columns individually is to
879+ * reject both index expressions and system columns. Currently,
880+ * matviews couldn't have OID columns so there's no way to create an
881+ * index on a system column; but maybe someday that wouldn't be true,
882+ * so let's be safe.
883+ */
884+ int numatts = indexStruct -> indnatts ;
885+ int i ;
886+
887+ for (i = 0 ;i < numatts ;i ++ )
888+ {
889+ int attnum = indexStruct -> indkey .values [i ];
890+
891+ if (attnum <=0 )
892+ return false;
893+ }
894+ return true;
895+ }
896+ return false;
897+ }
898+
848899
849900/*
850901 * This should be used to test whether the backend is in a context where it is