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 ,
6364const 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 ,
6967int 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
@@ -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 );
@@ -536,25 +523,6 @@ make_temptable_name_n(char *tempname, int n)
536523return namebuf .data ;
537524}
538525
539- static void
540- mv_GenerateOper (StringInfo buf ,Oid opoid )
541- {
542- HeapTuple opertup ;
543- Form_pg_operator operform ;
544-
545- opertup = SearchSysCache1 (OPEROID ,ObjectIdGetDatum (opoid ));
546- if (!HeapTupleIsValid (opertup ))
547- elog (ERROR ,"cache lookup failed for operator %u" ,opoid );
548- operform = (Form_pg_operator )GETSTRUCT (opertup );
549- Assert (operform -> oprkind == 'b' );
550-
551- appendStringInfo (buf ,"OPERATOR(%s.%s)" ,
552- quote_identifier (get_namespace_name (operform -> oprnamespace )),
553- NameStr (operform -> oprname ));
554-
555- ReleaseSysCache (opertup );
556- }
557-
558526/*
559527 * refresh_by_match_merge
560528 *
@@ -602,7 +570,7 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
602570List * indexoidlist ;
603571ListCell * indexoidscan ;
604572int16 relnatts ;
605- bool * usedForQual ;
573+ Oid * opUsedForQual ;
606574
607575initStringInfo (& querybuf );
608576matviewRel = heap_open (matviewOid ,NoLock );
@@ -614,7 +582,6 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
614582diffname = make_temptable_name_n (tempname ,2 );
615583
616584relnatts = matviewRel -> rd_rel -> relnatts ;
617- usedForQual = (bool * )palloc0 (sizeof (bool )* relnatts );
618585
619586/* Open SPI context. */
620587if (SPI_connect ()!= SPI_OK_CONNECT )
@@ -678,58 +645,98 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
678645 * include all rows.
679646 */
680647tupdesc = matviewRel -> rd_att ;
648+ opUsedForQual = (Oid * )palloc0 (sizeof (Oid )* relnatts );
681649foundUniqueIndex = false;
650+
682651indexoidlist = RelationGetIndexList (matviewRel );
683652
684653foreach (indexoidscan ,indexoidlist )
685654{
686655Oid indexoid = lfirst_oid (indexoidscan );
687656Relation indexRel ;
688- Form_pg_index indexStruct ;
689657
690658indexRel = index_open (indexoid ,RowExclusiveLock );
691- indexStruct = indexRel -> rd_index ;
692-
693- /*
694- * We're only interested if it is unique, valid, contains no
695- * expressions, and is not partial.
696- */
697- if (indexStruct -> indisunique &&
698- IndexIsValid (indexStruct )&&
699- RelationGetIndexExpressions (indexRel )== NIL &&
700- RelationGetIndexPredicate (indexRel )== NIL )
659+ if (is_usable_unique_index (indexRel ))
701660{
661+ Form_pg_index indexStruct = indexRel -> rd_index ;
702662int numatts = indexStruct -> indnatts ;
663+ oidvector * indclass ;
664+ Datum indclassDatum ;
665+ bool isnull ;
703666int i ;
704667
668+ /* Must get indclass the hard way. */
669+ indclassDatum = SysCacheGetAttr (INDEXRELID ,
670+ indexRel -> rd_indextuple ,
671+ Anum_pg_index_indclass ,
672+ & isnull );
673+ Assert (!isnull );
674+ indclass = (oidvector * )DatumGetPointer (indclassDatum );
675+
705676/* Add quals for all columns from this index. */
706677for (i = 0 ;i < numatts ;i ++ )
707678{
708679int attnum = indexStruct -> indkey .values [i ];
709- Oid type ;
680+ Oid opclass = indclass -> values [i ];
681+ Form_pg_attribute attr = TupleDescAttr (tupdesc ,attnum - 1 );
682+ Oid attrtype = attr -> atttypid ;
683+ HeapTuple cla_ht ;
684+ Form_pg_opclass cla_tup ;
685+ Oid opfamily ;
686+ Oid opcintype ;
710687Oid op ;
711- const char * colname ;
688+ const char * leftop ;
689+ const char * rightop ;
712690
713691/*
714- *Only include thecolumn once regardless of how many times
715- *it shows up in how many indexes .
692+ *Identify theequality operator associated with this index
693+ *column. First we need to look up the column's opclass .
716694 */
717- if (usedForQual [attnum - 1 ])
695+ cla_ht = SearchSysCache1 (CLAOID ,ObjectIdGetDatum (opclass ));
696+ if (!HeapTupleIsValid (cla_ht ))
697+ elog (ERROR ,"cache lookup failed for opclass %u" ,opclass );
698+ cla_tup = (Form_pg_opclass )GETSTRUCT (cla_ht );
699+ Assert (cla_tup -> opcmethod == BTREE_AM_OID );
700+ opfamily = cla_tup -> opcfamily ;
701+ opcintype = cla_tup -> opcintype ;
702+ ReleaseSysCache (cla_ht );
703+
704+ op = get_opfamily_member (opfamily ,opcintype ,opcintype ,
705+ BTEqualStrategyNumber );
706+ if (!OidIsValid (op ))
707+ elog (ERROR ,"missing operator %d(%u,%u) in opfamily %u" ,
708+ BTEqualStrategyNumber ,opcintype ,opcintype ,opfamily );
709+
710+ /*
711+ * If we find the same column with the same equality semantics
712+ * in more than one index, we only need to emit the equality
713+ * clause once.
714+ *
715+ * Since we only remember the last equality operator, this
716+ * code could be fooled into emitting duplicate clauses given
717+ * multiple indexes with several different opclasses ... but
718+ * that's so unlikely it doesn't seem worth spending extra
719+ * code to avoid.
720+ */
721+ if (opUsedForQual [attnum - 1 ]== op )
718722continue ;
719- usedForQual [attnum - 1 ]= true ;
723+ opUsedForQual [attnum - 1 ]= op ;
720724
721725/*
722726 * Actually add the qual, ANDed with any others.
723727 */
724728if (foundUniqueIndex )
725729appendStringInfoString (& querybuf ," AND " );
726730
727- colname = quote_identifier (NameStr ((tupdesc -> attrs [attnum - 1 ])-> attname ));
728- appendStringInfo (& querybuf ,"newdata.%s " ,colname );
729- type = attnumTypeId (matviewRel ,attnum );
730- op = lookup_type_cache (type ,TYPECACHE_EQ_OPR )-> eq_opr ;
731- mv_GenerateOper (& querybuf ,op );
732- appendStringInfo (& querybuf ," mv.%s" ,colname );
731+ leftop = quote_qualified_identifier ("newdata" ,
732+ NameStr (attr -> attname ));
733+ rightop = quote_qualified_identifier ("mv" ,
734+ NameStr (attr -> attname ));
735+
736+ generate_operator_clause (& querybuf ,
737+ leftop ,attrtype ,
738+ op ,
739+ rightop ,attrtype );
733740
734741foundUniqueIndex = true;
735742}
@@ -742,11 +749,11 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
742749list_free (indexoidlist );
743750
744751/*
745- * There must be at least one unique index on the matview.
752+ * There must be at least oneusable unique index on the matview.
746753 *
747754 * ExecRefreshMatView() checks that after taking the exclusive lock on the
748755 * matview. So at least one unique index is guaranteed to exist here
749- * because the lock is still being held.
756+ * because the lock is still being held; so an Assert seems sufficient .
750757 */
751758Assert (foundUniqueIndex );
752759
@@ -823,6 +830,51 @@ refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence)
823830RecentXmin ,ReadNextMultiXactId (),relpersistence );
824831}
825832
833+ /*
834+ * Check whether specified index is usable for match merge.
835+ */
836+ static bool
837+ is_usable_unique_index (Relation indexRel )
838+ {
839+ Form_pg_index indexStruct = indexRel -> rd_index ;
840+
841+ /*
842+ * Must be unique, valid, immediate, non-partial, and be defined over
843+ * plain user columns (not expressions). We also require it to be a
844+ * btree. Even if we had any other unique index kinds, we'd not know how
845+ * to identify the corresponding equality operator, nor could we be sure
846+ * that the planner could implement the required FULL JOIN with non-btree
847+ * operators.
848+ */
849+ if (indexStruct -> indisunique &&
850+ indexStruct -> indimmediate &&
851+ indexRel -> rd_rel -> relam == BTREE_AM_OID &&
852+ IndexIsValid (indexStruct )&&
853+ RelationGetIndexPredicate (indexRel )== NIL &&
854+ indexStruct -> indnatts > 0 )
855+ {
856+ /*
857+ * The point of groveling through the index columns individually is to
858+ * reject both index expressions and system columns. Currently,
859+ * matviews couldn't have OID columns so there's no way to create an
860+ * index on a system column; but maybe someday that wouldn't be true,
861+ * so let's be safe.
862+ */
863+ int numatts = indexStruct -> indnatts ;
864+ int i ;
865+
866+ for (i = 0 ;i < numatts ;i ++ )
867+ {
868+ int attnum = indexStruct -> indkey .values [i ];
869+
870+ if (attnum <=0 )
871+ return false;
872+ }
873+ return true;
874+ }
875+ return false;
876+ }
877+
826878
827879/*
828880 * This should be used to test whether the backend is in a context where it is