61
61
* Portions Copyright (c) 1994, Regents of the University of California
62
62
*
63
63
* IDENTIFICATION
64
- * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.141 2006/06/28 17:05:49 tgl Exp $
64
+ * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.142 2006/06/28 19:40:52 tgl Exp $
65
65
*
66
66
*-------------------------------------------------------------------------
67
67
*/
@@ -223,9 +223,11 @@ static void finalize_aggregate(AggState *aggstate,
223
223
AggStatePerAgg peraggstate ,
224
224
AggStatePerGroup pergroupstate ,
225
225
Datum * resultVal , bool * resultIsNull );
226
+ static Bitmapset * find_unaggregated_cols (AggState * aggstate );
227
+ static bool find_unaggregated_cols_walker (Node * node , Bitmapset * * colnos );
226
228
static void build_hash_table (AggState * aggstate );
227
229
static AggHashEntry lookup_hash_entry (AggState * aggstate ,
228
- TupleTableSlot * slot );
230
+ TupleTableSlot * inputslot );
229
231
static TupleTableSlot * agg_retrieve_direct (AggState * aggstate );
230
232
static void agg_fill_hash_table (AggState * aggstate );
231
233
static TupleTableSlot * agg_retrieve_hash_table (AggState * aggstate );
@@ -579,6 +581,46 @@ finalize_aggregate(AggState *aggstate,
579
581
MemoryContextSwitchTo (oldContext );
580
582
}
581
583
584
+ /*
585
+ * find_unaggregated_cols
586
+ * Construct a bitmapset of the column numbers of un-aggregated Vars
587
+ * appearing in our targetlist and qual (HAVING clause)
588
+ */
589
+ static Bitmapset *
590
+ find_unaggregated_cols (AggState * aggstate )
591
+ {
592
+ Agg * node = (Agg * ) aggstate -> ss .ps .plan ;
593
+ Bitmapset * colnos ;
594
+
595
+ colnos = NULL ;
596
+ (void ) find_unaggregated_cols_walker ((Node * ) node -> plan .targetlist ,
597
+ & colnos );
598
+ (void ) find_unaggregated_cols_walker ((Node * ) node -> plan .qual ,
599
+ & colnos );
600
+ return colnos ;
601
+ }
602
+
603
+ static bool
604
+ find_unaggregated_cols_walker (Node * node , Bitmapset * * colnos )
605
+ {
606
+ if (node == NULL )
607
+ return false;
608
+ if (IsA (node , Var ))
609
+ {
610
+ Var * var = (Var * ) node ;
611
+
612
+ /* setrefs.c should have set the varno to 0 */
613
+ Assert (var -> varno == 0 );
614
+ Assert (var -> varlevelsup == 0 );
615
+ * colnos = bms_add_member (* colnos , var -> varattno );
616
+ return false;
617
+ }
618
+ if (IsA (node , Aggref )) /* do not descend into aggregate exprs */
619
+ return false;
620
+ return expression_tree_walker (node , find_unaggregated_cols_walker ,
621
+ (void * ) colnos );
622
+ }
623
+
582
624
/*
583
625
* Initialize the hash table to empty.
584
626
*
@@ -590,6 +632,9 @@ build_hash_table(AggState *aggstate)
590
632
Agg * node = (Agg * ) aggstate -> ss .ps .plan ;
591
633
MemoryContext tmpmem = aggstate -> tmpcontext -> ecxt_per_tuple_memory ;
592
634
Size entrysize ;
635
+ Bitmapset * colnos ;
636
+ List * collist ;
637
+ int i ;
593
638
594
639
Assert (node -> aggstrategy == AGG_HASHED );
595
640
Assert (node -> numGroups > 0 );
@@ -605,13 +650,48 @@ build_hash_table(AggState *aggstate)
605
650
entrysize ,
606
651
aggstate -> aggcontext ,
607
652
tmpmem );
653
+
654
+ /*
655
+ * Create a list of the tuple columns that actually need to be stored
656
+ * in hashtable entries. The incoming tuples from the child plan node
657
+ * will contain grouping columns, other columns referenced in our
658
+ * targetlist and qual, columns used to compute the aggregate functions,
659
+ * and perhaps just junk columns we don't use at all. Only columns of the
660
+ * first two types need to be stored in the hashtable, and getting rid of
661
+ * the others can make the table entries significantly smaller. To avoid
662
+ * messing up Var numbering, we keep the same tuple descriptor for
663
+ * hashtable entries as the incoming tuples have, but set unwanted columns
664
+ * to NULL in the tuples that go into the table.
665
+ *
666
+ * To eliminate duplicates, we build a bitmapset of the needed columns,
667
+ * then convert it to an integer list (cheaper to scan at runtime).
668
+ * The list is in decreasing order so that the first entry is the largest;
669
+ * lookup_hash_entry depends on this to use slot_getsomeattrs correctly.
670
+ *
671
+ * Note: at present, searching the tlist/qual is not really necessary
672
+ * since the parser should disallow any unaggregated references to
673
+ * ungrouped columns. However, the search will be needed when we add
674
+ * support for SQL99 semantics that allow use of "functionally dependent"
675
+ * columns that haven't been explicitly grouped by.
676
+ */
677
+
678
+ /* Find Vars that will be needed in tlist and qual */
679
+ colnos = find_unaggregated_cols (aggstate );
680
+ /* Add in all the grouping columns */
681
+ for (i = 0 ; i < node -> numCols ; i ++ )
682
+ colnos = bms_add_member (colnos , node -> grpColIdx [i ]);
683
+ /* Convert to list, using lcons so largest element ends up first */
684
+ collist = NIL ;
685
+ while ((i = bms_first_member (colnos )) >= 0 )
686
+ collist = lcons_int (i , collist );
687
+ aggstate -> hash_needed = collist ;
608
688
}
609
689
610
690
/*
611
691
* Estimate per-hash-table-entry overhead for the planner.
612
692
*
613
693
* Note that the estimate does not include space for pass-by-reference
614
- * transition data values.
694
+ * transition data values, nor for the representative tuple of each group .
615
695
*/
616
696
Size
617
697
hash_agg_entry_size (int numAggs )
@@ -621,9 +701,9 @@ hash_agg_entry_size(int numAggs)
621
701
/* This must match build_hash_table */
622
702
entrysize = sizeof (AggHashEntryData ) +
623
703
(numAggs - 1 ) * sizeof (AggStatePerGroupData );
624
- /* Account for hashtable overhead */
625
- entrysize += 2 * sizeof (void * );
626
704
entrysize = MAXALIGN (entrysize );
705
+ /* Account for hashtable overhead (assuming fill factor = 1) */
706
+ entrysize += 3 * sizeof (void * );
627
707
return entrysize ;
628
708
}
629
709
@@ -634,13 +714,34 @@ hash_agg_entry_size(int numAggs)
634
714
* When called, CurrentMemoryContext should be the per-query context.
635
715
*/
636
716
static AggHashEntry
637
- lookup_hash_entry (AggState * aggstate , TupleTableSlot * slot )
717
+ lookup_hash_entry (AggState * aggstate , TupleTableSlot * inputslot )
638
718
{
719
+ TupleTableSlot * hashslot = aggstate -> hashslot ;
720
+ ListCell * l ;
639
721
AggHashEntry entry ;
640
722
bool isnew ;
641
723
724
+ /* if first time through, initialize hashslot by cloning input slot */
725
+ if (hashslot -> tts_tupleDescriptor == NULL )
726
+ {
727
+ ExecSetSlotDescriptor (hashslot , inputslot -> tts_tupleDescriptor );
728
+ /* Make sure all unused columns are NULLs */
729
+ ExecStoreAllNullTuple (hashslot );
730
+ }
731
+
732
+ /* transfer just the needed columns into hashslot */
733
+ slot_getsomeattrs (inputslot , linitial_int (aggstate -> hash_needed ));
734
+ foreach (l , aggstate -> hash_needed )
735
+ {
736
+ int varNumber = lfirst_int (l ) - 1 ;
737
+
738
+ hashslot -> tts_values [varNumber ] = inputslot -> tts_values [varNumber ];
739
+ hashslot -> tts_isnull [varNumber ] = inputslot -> tts_isnull [varNumber ];
740
+ }
741
+
742
+ /* find or create the hashtable entry using the filtered tuple */
642
743
entry = (AggHashEntry ) LookupTupleHashEntry (aggstate -> hashtable ,
643
- slot ,
744
+ hashslot ,
644
745
& isnew );
645
746
646
747
if (isnew )
@@ -1063,13 +1164,14 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
1063
1164
ALLOCSET_DEFAULT_INITSIZE ,
1064
1165
ALLOCSET_DEFAULT_MAXSIZE );
1065
1166
1066
- #define AGG_NSLOTS 2
1167
+ #define AGG_NSLOTS 3
1067
1168
1068
1169
/*
1069
1170
* tuple table initialization
1070
1171
*/
1071
1172
ExecInitScanTupleSlot (estate , & aggstate -> ss );
1072
1173
ExecInitResultTupleSlot (estate , & aggstate -> ss .ps );
1174
+ aggstate -> hashslot = ExecInitExtraTupleSlot (estate );
1073
1175
1074
1176
/*
1075
1177
* initialize child expressions
0 commit comments