88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.51 2010/02/14 18:42:15 rhaas Exp $
11+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.52 2010/05/10 16:25:46 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -50,6 +50,7 @@ static bool build_minmax_path(PlannerInfo *root, RelOptInfo *rel,
5050static ScanDirection match_agg_to_index_col (MinMaxAggInfo * info ,
5151IndexOptInfo * index ,int indexcol );
5252static void make_agg_subplan (PlannerInfo * root ,MinMaxAggInfo * info );
53+ static void attach_notnull_index_qual (MinMaxAggInfo * info ,IndexScan * iplan );
5354static Node * replace_aggs_with_params_mutator (Node * node ,List * * context );
5455static Oid fetch_agg_sort_op (Oid aggfnoid );
5556
@@ -537,9 +538,6 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
537538 * The NOT NULL qual has to go on the actual indexscan; create_plan might
538539 * have stuck a gating Result atop that, if there were any pseudoconstant
539540 * quals.
540- *
541- * We can skip adding the NOT NULL qual if it duplicates either an
542- * already-given WHERE condition, or a clause of the index predicate.
543541 */
544542plan = create_plan (& subroot , (Path * )info -> path );
545543
@@ -552,21 +550,7 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
552550if (!IsA (iplan ,IndexScan ))
553551elog (ERROR ,"result of create_plan(IndexPath) isn't an IndexScan" );
554552
555- if (!list_member (iplan -> indexqualorig ,info -> notnulltest )&&
556- !list_member (info -> path -> indexinfo -> indpred ,info -> notnulltest ))
557- {
558- NullTest * ntest ;
559-
560- /* Need a "fixed" copy as well as the original */
561- ntest = copyObject (info -> notnulltest );
562- ntest -> arg = (Expr * )fix_indexqual_operand ((Node * )ntest -> arg ,
563- info -> path -> indexinfo );
564-
565- iplan -> indexqual = lappend (iplan -> indexqual ,
566- ntest );
567- iplan -> indexqualorig = lappend (iplan -> indexqualorig ,
568- info -> notnulltest );
569- }
553+ attach_notnull_index_qual (info ,iplan );
570554
571555plan = (Plan * )make_limit (plan ,
572556subparse -> limitOffset ,
@@ -586,6 +570,169 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *info)
586570root -> init_plans = subroot .init_plans ;
587571}
588572
573+ /*
574+ * Add "target IS NOT NULL" to the quals of the given indexscan.
575+ *
576+ * This is trickier than it sounds because the new qual has to be added at an
577+ * appropriate place in the qual list, to preserve the list's ordering by
578+ * index column position.
579+ */
580+ static void
581+ attach_notnull_index_qual (MinMaxAggInfo * info ,IndexScan * iplan )
582+ {
583+ NullTest * ntest ;
584+ List * newindexqual ;
585+ List * newindexqualorig ;
586+ bool done ;
587+ ListCell * lc1 ;
588+ ListCell * lc2 ;
589+ Expr * leftop ;
590+ AttrNumber targetattno ;
591+
592+ /*
593+ * We can skip adding the NOT NULL qual if it duplicates either an
594+ * already-given WHERE condition, or a clause of the index predicate.
595+ */
596+ if (list_member (iplan -> indexqualorig ,info -> notnulltest )||
597+ list_member (info -> path -> indexinfo -> indpred ,info -> notnulltest ))
598+ return ;
599+
600+ /* Need a "fixed" copy as well as the original */
601+ ntest = copyObject (info -> notnulltest );
602+ ntest -> arg = (Expr * )fix_indexqual_operand ((Node * )ntest -> arg ,
603+ info -> path -> indexinfo );
604+
605+ /* Identify the target index column from the "fixed" copy */
606+ leftop = ntest -> arg ;
607+
608+ if (leftop && IsA (leftop ,RelabelType ))
609+ leftop = ((RelabelType * )leftop )-> arg ;
610+
611+ Assert (leftop != NULL );
612+
613+ if (!IsA (leftop ,Var ))
614+ elog (ERROR ,"NullTest indexqual has wrong key" );
615+
616+ targetattno = ((Var * )leftop )-> varattno ;
617+
618+ /*
619+ * list.c doesn't expose a primitive to insert a list cell at an arbitrary
620+ * position, so our strategy is to copy the lists and insert the null test
621+ * when we reach an appropriate spot.
622+ */
623+ newindexqual = newindexqualorig = NIL ;
624+ done = false;
625+
626+ forboth (lc1 ,iplan -> indexqual ,lc2 ,iplan -> indexqualorig )
627+ {
628+ Expr * qual = (Expr * )lfirst (lc1 );
629+ Expr * qualorig = (Expr * )lfirst (lc2 );
630+ AttrNumber varattno ;
631+
632+ /*
633+ * Identify which index column this qual is for. This code should
634+ * match the qual disassembly code in ExecIndexBuildScanKeys.
635+ */
636+ if (IsA (qual ,OpExpr ))
637+ {
638+ /* indexkey op expression */
639+ leftop = (Expr * )get_leftop (qual );
640+
641+ if (leftop && IsA (leftop ,RelabelType ))
642+ leftop = ((RelabelType * )leftop )-> arg ;
643+
644+ Assert (leftop != NULL );
645+
646+ if (!IsA (leftop ,Var ))
647+ elog (ERROR ,"indexqual doesn't have key on left side" );
648+
649+ varattno = ((Var * )leftop )-> varattno ;
650+ }
651+ else if (IsA (qual ,RowCompareExpr ))
652+ {
653+ /* (indexkey, indexkey, ...) op (expression, expression, ...) */
654+ RowCompareExpr * rc = (RowCompareExpr * )qual ;
655+
656+ /*
657+ * Examine just the first column of the rowcompare, which is
658+ * what determines its placement in the overall qual list.
659+ */
660+ leftop = (Expr * )linitial (rc -> largs );
661+
662+ if (leftop && IsA (leftop ,RelabelType ))
663+ leftop = ((RelabelType * )leftop )-> arg ;
664+
665+ Assert (leftop != NULL );
666+
667+ if (!IsA (leftop ,Var ))
668+ elog (ERROR ,"indexqual doesn't have key on left side" );
669+
670+ varattno = ((Var * )leftop )-> varattno ;
671+ }
672+ else if (IsA (qual ,ScalarArrayOpExpr ))
673+ {
674+ /* indexkey op ANY (array-expression) */
675+ ScalarArrayOpExpr * saop = (ScalarArrayOpExpr * )qual ;
676+
677+ leftop = (Expr * )linitial (saop -> args );
678+
679+ if (leftop && IsA (leftop ,RelabelType ))
680+ leftop = ((RelabelType * )leftop )-> arg ;
681+
682+ Assert (leftop != NULL );
683+
684+ if (!IsA (leftop ,Var ))
685+ elog (ERROR ,"indexqual doesn't have key on left side" );
686+
687+ varattno = ((Var * )leftop )-> varattno ;
688+ }
689+ else if (IsA (qual ,NullTest ))
690+ {
691+ /* indexkey IS NULL or indexkey IS NOT NULL */
692+ NullTest * ntest = (NullTest * )qual ;
693+
694+ leftop = ntest -> arg ;
695+
696+ if (leftop && IsA (leftop ,RelabelType ))
697+ leftop = ((RelabelType * )leftop )-> arg ;
698+
699+ Assert (leftop != NULL );
700+
701+ if (!IsA (leftop ,Var ))
702+ elog (ERROR ,"NullTest indexqual has wrong key" );
703+
704+ varattno = ((Var * )leftop )-> varattno ;
705+ }
706+ else
707+ {
708+ elog (ERROR ,"unsupported indexqual type: %d" ,
709+ (int )nodeTag (qual ));
710+ varattno = 0 ;/* keep compiler quiet */
711+ }
712+
713+ /* Insert the null test at the first place it can legally go */
714+ if (!done && targetattno <=varattno )
715+ {
716+ newindexqual = lappend (newindexqual ,ntest );
717+ newindexqualorig = lappend (newindexqualorig ,info -> notnulltest );
718+ done = true;
719+ }
720+
721+ newindexqual = lappend (newindexqual ,qual );
722+ newindexqualorig = lappend (newindexqualorig ,qualorig );
723+ }
724+
725+ /* Add the null test at the end if it must follow all existing quals */
726+ if (!done )
727+ {
728+ newindexqual = lappend (newindexqual ,ntest );
729+ newindexqualorig = lappend (newindexqualorig ,info -> notnulltest );
730+ }
731+
732+ iplan -> indexqual = newindexqual ;
733+ iplan -> indexqualorig = newindexqualorig ;
734+ }
735+
589736/*
590737 * Replace original aggregate calls with subplan output Params
591738 */