Prune the list of non-unique semijoins. pgpa_semijoin
authorRobert Haas <[email protected]>
Thu, 11 Dec 2025 20:37:56 +0000 (15:37 -0500)
committerRobert Haas <[email protected]>
Thu, 11 Dec 2025 20:37:56 +0000 (15:37 -0500)
Test the list of unique semijoins.

This causes a bunch of breakage, all of which I believe to be due
to eager aggregation.

contrib/pg_plan_advice/pgpa_walker.c

index d5493bc0b801ca4143a1f596e1b5e958d571cfca..318fddc9e368aeb1c8c8d6252722531ad40c9004 100644 (file)
@@ -72,6 +72,8 @@ pgpa_plan_walker(pgpa_plan_walker_context *walker, PlannedStmt *pstmt,
                                 List *sj_unique_rels)
 {
        ListCell   *lc;
+       List       *sj_unique_rtis = NULL;
+       List       *sj_nonunique_qfs = NULL;
 
        /* Initialization. */
        memset(walker, 0, sizeof(pgpa_plan_walker_context));
@@ -88,6 +90,75 @@ pgpa_plan_walker(pgpa_plan_walker_context *walker, PlannedStmt *pstmt,
                if (plan != NULL)
                        pgpa_walk_recursively(walker, plan, 0, NULL, NIL, false);
        }
+
+       /* Adjust RTIs from sj_unique_rels for the flattened range table. */
+       foreach_ptr(pgpa_sj_unique_rel, ur, sj_unique_rels)
+       {
+               int             rtindex = -1;
+               int             rtoffset = 0;
+               bool    dummy = false;
+               Bitmapset *relids = NULL;
+
+               /* If this is a subplan, find the range table offset. */
+               if (ur->plan_name != NULL)
+               {
+                       foreach_node(SubPlanRTInfo, rtinfo, pstmt->subrtinfos)
+                       {
+                               if (strcmp(ur->plan_name, rtinfo->plan_name) == 0)
+                               {
+                                       rtoffset = rtinfo->rtoffset;
+                                       dummy = rtinfo->dummy;
+                                       break;
+                               }
+                       }
+
+                       if (rtoffset == 0)
+                               elog(ERROR, "no rtoffset for plan %s", ur->plan_name);
+               }
+
+               /* If this entry pertains to a dummy subquery, ignore it. */
+               if (dummy)
+                       continue;
+
+               /* Offset each entry from the original set. */
+               while ((rtindex = bms_next_member(ur->relids, rtindex)) >= 0)
+                       relids = bms_add_member(relids, rtindex + rtoffset);
+
+               /* Store the resulting set. */
+               sj_unique_rtis = lappend(sj_unique_rtis, relids);
+       }
+
+       /*
+        * Remove any non-unique semjoin query features for which making the
+        * rel unique wasn't considered.
+        */
+       foreach_ptr(pgpa_query_feature, qf,
+                               walker->query_features[PGPAQF_SEMIJOIN_NON_UNIQUE])
+       {
+               if (list_member(sj_unique_rtis, qf->relids))
+                       sj_nonunique_qfs = lappend(sj_nonunique_qfs, qf);
+       }
+       walker->query_features[PGPAQF_SEMIJOIN_NON_UNIQUE] = sj_nonunique_qfs;
+
+       /*
+        * If we find any cases where analysis of the Plan tree shows that the
+        * semijoin was made unique but this possibility was never observed to
+        * be considered during planning, then we have a bug somewhere.
+        */
+       foreach_ptr(pgpa_query_feature, qf,
+                               walker->query_features[PGPAQF_SEMIJOIN_UNIQUE])
+       {
+               if (!list_member(sj_unique_rtis, qf->relids))
+               {
+                       StringInfoData  buf;
+
+                       initStringInfo(&buf);
+                       outBitmapset(&buf, qf->relids);
+                       elog(ERROR,
+                                "unique semijoin found for relids %s but not observed during planning",
+                                buf.data);
+               }
+       }
 }
 
 /*