@@ -105,7 +105,8 @@ MultiExecHash(HashState *node)
105
105
break ;
106
106
/* We have to compute the hash value */
107
107
econtext -> ecxt_innertuple = slot ;
108
- if (ExecHashGetHashValue (hashtable , econtext , hashkeys , false, false,
108
+ if (ExecHashGetHashValue (hashtable , econtext , hashkeys ,
109
+ false, hashtable -> keepNulls ,
109
110
& hashvalue ))
110
111
{
111
112
int bucketNumber ;
@@ -231,7 +232,7 @@ ExecEndHash(HashState *node)
231
232
* ----------------------------------------------------------------
232
233
*/
233
234
HashJoinTable
234
- ExecHashTableCreate (Hash * node , List * hashOperators )
235
+ ExecHashTableCreate (Hash * node , List * hashOperators , bool keepNulls )
235
236
{
236
237
HashJoinTable hashtable ;
237
238
Plan * outerNode ;
@@ -273,6 +274,7 @@ ExecHashTableCreate(Hash *node, List *hashOperators)
273
274
hashtable -> nbuckets = nbuckets ;
274
275
hashtable -> log2_nbuckets = log2_nbuckets ;
275
276
hashtable -> buckets = NULL ;
277
+ hashtable -> keepNulls = keepNulls ;
276
278
hashtable -> skewEnabled = false;
277
279
hashtable -> skewBucket = NULL ;
278
280
hashtable -> skewBucketLen = 0 ;
@@ -712,13 +714,26 @@ ExecHashTableInsert(HashJoinTable hashtable,
712
714
HashJoinTuple hashTuple ;
713
715
int hashTupleSize ;
714
716
717
+ /* Create the HashJoinTuple */
715
718
hashTupleSize = HJTUPLE_OVERHEAD + tuple -> t_len ;
716
719
hashTuple = (HashJoinTuple ) MemoryContextAlloc (hashtable -> batchCxt ,
717
720
hashTupleSize );
718
721
hashTuple -> hashvalue = hashvalue ;
719
722
memcpy (HJTUPLE_MINTUPLE (hashTuple ), tuple , tuple -> t_len );
723
+
724
+ /*
725
+ * We always reset the tuple-matched flag on insertion. This is okay
726
+ * even when reloading a tuple from a batch file, since the tuple
727
+ * could not possibly have been matched to an outer tuple before it
728
+ * went into the batch file.
729
+ */
730
+ HeapTupleHeaderClearMatch (HJTUPLE_MINTUPLE (hashTuple ));
731
+
732
+ /* Push it onto the front of the bucket's list */
720
733
hashTuple -> next = hashtable -> buckets [bucketno ];
721
734
hashtable -> buckets [bucketno ] = hashTuple ;
735
+
736
+ /* Account for space used, and back off if we've used too much */
722
737
hashtable -> spaceUsed += hashTupleSize ;
723
738
if (hashtable -> spaceUsed > hashtable -> spacePeak )
724
739
hashtable -> spacePeak = hashtable -> spaceUsed ;
@@ -878,8 +893,12 @@ ExecHashGetBucketAndBatch(HashJoinTable hashtable,
878
893
* scan a hash bucket for matches to the current outer tuple
879
894
*
880
895
* The current outer tuple must be stored in econtext->ecxt_outertuple.
896
+ *
897
+ * On success, the inner tuple is stored into hjstate->hj_CurTuple and
898
+ * econtext->ecxt_innertuple, using hjstate->hj_HashTupleSlot as the slot
899
+ * for the latter.
881
900
*/
882
- HashJoinTuple
901
+ bool
883
902
ExecScanHashBucket (HashJoinState * hjstate ,
884
903
ExprContext * econtext )
885
904
{
@@ -920,7 +939,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
920
939
if (ExecQual (hjclauses , econtext , false))
921
940
{
922
941
hjstate -> hj_CurTuple = hashTuple ;
923
- return hashTuple ;
942
+ return true ;
924
943
}
925
944
}
926
945
@@ -930,7 +949,99 @@ ExecScanHashBucket(HashJoinState *hjstate,
930
949
/*
931
950
* no match
932
951
*/
933
- return NULL ;
952
+ return false;
953
+ }
954
+
955
+ /*
956
+ * ExecPrepHashTableForUnmatched
957
+ * set up for a series of ExecScanHashTableForUnmatched calls
958
+ */
959
+ void
960
+ ExecPrepHashTableForUnmatched (HashJoinState * hjstate )
961
+ {
962
+ /*
963
+ *----------
964
+ * During this scan we use the HashJoinState fields as follows:
965
+ *
966
+ * hj_CurBucketNo: next regular bucket to scan
967
+ * hj_CurSkewBucketNo: next skew bucket (an index into skewBucketNums)
968
+ * hj_CurTuple: last tuple returned, or NULL to start next bucket
969
+ *----------
970
+ */
971
+ hjstate -> hj_CurBucketNo = 0 ;
972
+ hjstate -> hj_CurSkewBucketNo = 0 ;
973
+ hjstate -> hj_CurTuple = NULL ;
974
+ }
975
+
976
+ /*
977
+ * ExecScanHashTableForUnmatched
978
+ * scan the hash table for unmatched inner tuples
979
+ *
980
+ * On success, the inner tuple is stored into hjstate->hj_CurTuple and
981
+ * econtext->ecxt_innertuple, using hjstate->hj_HashTupleSlot as the slot
982
+ * for the latter.
983
+ */
984
+ bool
985
+ ExecScanHashTableForUnmatched (HashJoinState * hjstate , ExprContext * econtext )
986
+ {
987
+ HashJoinTable hashtable = hjstate -> hj_HashTable ;
988
+ HashJoinTuple hashTuple = hjstate -> hj_CurTuple ;
989
+
990
+ for (;;)
991
+ {
992
+ /*
993
+ * hj_CurTuple is the address of the tuple last returned from the
994
+ * current bucket, or NULL if it's time to start scanning a new
995
+ * bucket.
996
+ */
997
+ if (hashTuple != NULL )
998
+ hashTuple = hashTuple -> next ;
999
+ else if (hjstate -> hj_CurBucketNo < hashtable -> nbuckets )
1000
+ {
1001
+ hashTuple = hashtable -> buckets [hjstate -> hj_CurBucketNo ];
1002
+ hjstate -> hj_CurBucketNo ++ ;
1003
+ }
1004
+ else if (hjstate -> hj_CurSkewBucketNo < hashtable -> nSkewBuckets )
1005
+ {
1006
+ int j = hashtable -> skewBucketNums [hjstate -> hj_CurSkewBucketNo ];
1007
+
1008
+ hashTuple = hashtable -> skewBucket [j ]-> tuples ;
1009
+ hjstate -> hj_CurSkewBucketNo ++ ;
1010
+ }
1011
+ else
1012
+ break ; /* finished all buckets */
1013
+
1014
+ while (hashTuple != NULL )
1015
+ {
1016
+ if (!HeapTupleHeaderHasMatch (HJTUPLE_MINTUPLE (hashTuple )))
1017
+ {
1018
+ TupleTableSlot * inntuple ;
1019
+
1020
+ /* insert hashtable's tuple into exec slot */
1021
+ inntuple = ExecStoreMinimalTuple (HJTUPLE_MINTUPLE (hashTuple ),
1022
+ hjstate -> hj_HashTupleSlot ,
1023
+ false); /* do not pfree */
1024
+ econtext -> ecxt_innertuple = inntuple ;
1025
+
1026
+ /*
1027
+ * Reset temp memory each time; although this function doesn't
1028
+ * do any qual eval, the caller will, so let's keep it
1029
+ * parallel to ExecScanHashBucket.
1030
+ */
1031
+ ResetExprContext (econtext );
1032
+
1033
+ hjstate -> hj_CurTuple = hashTuple ;
1034
+ return true;
1035
+ }
1036
+
1037
+ hashTuple = hashTuple -> next ;
1038
+ }
1039
+ }
1040
+
1041
+ /*
1042
+ * no more unmatched tuples
1043
+ */
1044
+ return false;
934
1045
}
935
1046
936
1047
/*
@@ -960,6 +1071,35 @@ ExecHashTableReset(HashJoinTable hashtable)
960
1071
MemoryContextSwitchTo (oldcxt );
961
1072
}
962
1073
1074
+ /*
1075
+ * ExecHashTableResetMatchFlags
1076
+ * Clear all the HeapTupleHeaderHasMatch flags in the table
1077
+ */
1078
+ void
1079
+ ExecHashTableResetMatchFlags (HashJoinTable hashtable )
1080
+ {
1081
+ HashJoinTuple tuple ;
1082
+ int i ;
1083
+
1084
+ /* Reset all flags in the main table ... */
1085
+ for (i = 0 ; i < hashtable -> nbuckets ; i ++ )
1086
+ {
1087
+ for (tuple = hashtable -> buckets [i ]; tuple != NULL ; tuple = tuple -> next )
1088
+ HeapTupleHeaderClearMatch (HJTUPLE_MINTUPLE (tuple ));
1089
+ }
1090
+
1091
+ /* ... and the same for the skew buckets, if any */
1092
+ for (i = 0 ; i < hashtable -> nSkewBuckets ; i ++ )
1093
+ {
1094
+ int j = hashtable -> skewBucketNums [i ];
1095
+ HashSkewBucket * skewBucket = hashtable -> skewBucket [j ];
1096
+
1097
+ for (tuple = skewBucket -> tuples ; tuple != NULL ; tuple = tuple -> next )
1098
+ HeapTupleHeaderClearMatch (HJTUPLE_MINTUPLE (tuple ));
1099
+ }
1100
+ }
1101
+
1102
+
963
1103
void
964
1104
ExecReScanHash (HashState * node )
965
1105
{
@@ -1203,6 +1343,7 @@ ExecHashSkewTableInsert(HashJoinTable hashtable,
1203
1343
hashTupleSize );
1204
1344
hashTuple -> hashvalue = hashvalue ;
1205
1345
memcpy (HJTUPLE_MINTUPLE (hashTuple ), tuple , tuple -> t_len );
1346
+ HeapTupleHeaderClearMatch (HJTUPLE_MINTUPLE (hashTuple ));
1206
1347
1207
1348
/* Push it onto the front of the skew bucket's list */
1208
1349
hashTuple -> next = hashtable -> skewBucket [bucketNumber ]-> tuples ;
0 commit comments