github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/hashjoiner_tmpl.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  // {{/*
    12  // +build execgen_template
    13  //
    14  // This file is the execgen template for hashjoiner.eg.go. It's formatted in a
    15  // special way, so it's both valid Go and a valid text/template input. This
    16  // permits editing this file with editor support.
    17  //
    18  // */}}
    19  
    20  package colexec
    21  
    22  import (
    23  	"github.com/cockroachdb/cockroach/pkg/col/coldata"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    25  )
    26  
    27  // {{/*
    28  
    29  func _COLLECT_PROBE_OUTER(
    30  	hj *hashJoiner, batchSize int, nResults int, batch coldata.Batch, _USE_SEL bool,
    31  ) int { // */}}
    32  	// {{define "collectProbeOuter" -}}
    33  	// Early bounds checks.
    34  	_ = hj.ht.probeScratch.headID[batchSize-1]
    35  	// {{if .UseSel}}
    36  	_ = sel[batchSize-1]
    37  	// {{end}}
    38  	for i := hj.probeState.prevBatchResumeIdx; i < batchSize; i++ {
    39  		currentID := hj.ht.probeScratch.headID[i]
    40  
    41  		for {
    42  			if nResults >= hj.outputBatchSize {
    43  				hj.probeState.prevBatch = batch
    44  				hj.probeState.prevBatchResumeIdx = i
    45  				return nResults
    46  			}
    47  
    48  			hj.probeState.probeRowUnmatched[nResults] = currentID == 0
    49  			if currentID > 0 {
    50  				hj.probeState.buildIdx[nResults] = int(currentID - 1)
    51  			} else {
    52  				// If currentID == 0, then probeRowUnmatched will have been set - and
    53  				// we set the corresponding buildIdx to zero so that (as long as the
    54  				// build hash table has at least one row) we can copy the values vector
    55  				// without paying attention to probeRowUnmatched.
    56  				hj.probeState.buildIdx[nResults] = 0
    57  			}
    58  			// {{if .UseSel}}
    59  			hj.probeState.probeIdx[nResults] = sel[i]
    60  			// {{else}}
    61  			hj.probeState.probeIdx[nResults] = i
    62  			// {{end}}
    63  			currentID = hj.ht.same[currentID]
    64  			hj.ht.probeScratch.headID[i] = currentID
    65  			nResults++
    66  
    67  			if currentID == 0 {
    68  				break
    69  			}
    70  		}
    71  	}
    72  	// {{end}}
    73  	// {{/*
    74  	// Dummy return value that is never used.
    75  	return 0
    76  }
    77  
    78  func _COLLECT_PROBE_NO_OUTER(
    79  	hj *hashJoiner, batchSize int, nResults int, batch coldata.Batch, _USE_SEL bool,
    80  ) int { // */}}
    81  	// {{define "collectProbeNoOuter" -}}
    82  	// Early bounds checks.
    83  	_ = hj.ht.probeScratch.headID[batchSize-1]
    84  	// {{if .UseSel}}
    85  	_ = sel[batchSize-1]
    86  	// {{end}}
    87  	for i := hj.probeState.prevBatchResumeIdx; i < batchSize; i++ {
    88  		currentID := hj.ht.probeScratch.headID[i]
    89  		for currentID != 0 {
    90  			if nResults >= hj.outputBatchSize {
    91  				hj.probeState.prevBatch = batch
    92  				hj.probeState.prevBatchResumeIdx = i
    93  				return nResults
    94  			}
    95  
    96  			hj.probeState.buildIdx[nResults] = int(currentID - 1)
    97  			// {{if .UseSel}}
    98  			hj.probeState.probeIdx[nResults] = sel[i]
    99  			// {{else}}
   100  			hj.probeState.probeIdx[nResults] = i
   101  			// {{end}}
   102  			currentID = hj.ht.same[currentID]
   103  			hj.ht.probeScratch.headID[i] = currentID
   104  			nResults++
   105  		}
   106  	}
   107  	// {{end}}
   108  	// {{/*
   109  	// Dummy return value that is never used.
   110  	return 0
   111  }
   112  
   113  // This code snippet collects the "matches" for LEFT ANTI and EXCEPT ALL joins.
   114  // "Matches" are in quotes because we're actually interested in non-matches
   115  // from the left side.
   116  func _COLLECT_ANTI(
   117  	hj *hashJoiner, batchSize int, nResults int, batch coldata.Batch, _USE_SEL bool,
   118  ) int { // */}}
   119  	// {{define "collectAnti" -}}
   120  	// Early bounds checks.
   121  	_ = hj.ht.probeScratch.headID[batchSize-1]
   122  	// {{if .UseSel}}
   123  	_ = sel[batchSize-1]
   124  	// {{end}}
   125  	for i := int(0); i < batchSize; i++ {
   126  		currentID := hj.ht.probeScratch.headID[i]
   127  		if currentID == 0 {
   128  			// currentID of 0 indicates that ith probing row didn't have a match, so
   129  			// we include it into the output.
   130  			// {{if .UseSel}}
   131  			hj.probeState.probeIdx[nResults] = sel[i]
   132  			// {{else}}
   133  			hj.probeState.probeIdx[nResults] = i
   134  			// {{end}}
   135  			nResults++
   136  		}
   137  	}
   138  	// {{end}}
   139  	// {{/*
   140  	// Dummy return value that is never used.
   141  	return 0
   142  }
   143  
   144  func _DISTINCT_COLLECT_PROBE_OUTER(hj *hashJoiner, batchSize int, _USE_SEL bool) { // */}}
   145  	// {{define "distinctCollectProbeOuter" -}}
   146  	// Early bounds checks.
   147  	_ = hj.ht.probeScratch.groupID[batchSize-1]
   148  	_ = hj.probeState.probeRowUnmatched[batchSize-1]
   149  	_ = hj.probeState.buildIdx[batchSize-1]
   150  	_ = hj.probeState.probeIdx[batchSize-1]
   151  	// {{if .UseSel}}
   152  	_ = sel[batchSize-1]
   153  	// {{end}}
   154  	for i := int(0); i < batchSize; i++ {
   155  		// Index of keys and outputs in the hash table is calculated as ID - 1.
   156  		id := hj.ht.probeScratch.groupID[i]
   157  		rowUnmatched := id == 0
   158  		hj.probeState.probeRowUnmatched[i] = rowUnmatched
   159  		if !rowUnmatched {
   160  			hj.probeState.buildIdx[i] = int(id - 1)
   161  		}
   162  		// {{if .UseSel}}
   163  		hj.probeState.probeIdx[i] = sel[i]
   164  		// {{else}}
   165  		hj.probeState.probeIdx[i] = i
   166  		// {{end}}
   167  	}
   168  	// {{end}}
   169  	// {{/*
   170  }
   171  
   172  func _DISTINCT_COLLECT_PROBE_NO_OUTER(hj *hashJoiner, batchSize int, nResults int, _USE_SEL bool) { // */}}
   173  	// {{define "distinctCollectProbeNoOuter" -}}
   174  	// Early bounds checks.
   175  	_ = hj.ht.probeScratch.groupID[batchSize-1]
   176  	_ = hj.probeState.buildIdx[batchSize-1]
   177  	_ = hj.probeState.probeIdx[batchSize-1]
   178  	// {{if .UseSel}}
   179  	_ = sel[batchSize-1]
   180  	// {{end}}
   181  	for i := int(0); i < batchSize; i++ {
   182  		if hj.ht.probeScratch.groupID[i] != 0 {
   183  			// Index of keys and outputs in the hash table is calculated as ID - 1.
   184  			hj.probeState.buildIdx[nResults] = int(hj.ht.probeScratch.groupID[i] - 1)
   185  			// {{if .UseSel}}
   186  			hj.probeState.probeIdx[nResults] = sel[i]
   187  			// {{else}}
   188  			hj.probeState.probeIdx[nResults] = i
   189  			// {{end}}
   190  			nResults++
   191  		}
   192  	}
   193  	// {{end}}
   194  	// {{/*
   195  }
   196  
   197  // */}}
   198  
   199  // collect prepares the buildIdx and probeIdx arrays where the buildIdx and
   200  // probeIdx at each index are joined to make an output row. The total number of
   201  // resulting rows is returned.
   202  func (hj *hashJoiner) collect(batch coldata.Batch, batchSize int, sel []int) int {
   203  	nResults := int(0)
   204  
   205  	if hj.spec.left.outer {
   206  		if sel != nil {
   207  			_COLLECT_PROBE_OUTER(hj, batchSize, nResults, batch, true)
   208  		} else {
   209  			_COLLECT_PROBE_OUTER(hj, batchSize, nResults, batch, false)
   210  		}
   211  	} else {
   212  		if sel != nil {
   213  			switch hj.spec.joinType {
   214  			case sqlbase.LeftAntiJoin, sqlbase.ExceptAllJoin:
   215  				_COLLECT_ANTI(hj, batchSize, nResults, batch, true)
   216  			default:
   217  				_COLLECT_PROBE_NO_OUTER(hj, batchSize, nResults, batch, true)
   218  			}
   219  		} else {
   220  			switch hj.spec.joinType {
   221  			case sqlbase.LeftAntiJoin, sqlbase.ExceptAllJoin:
   222  				_COLLECT_ANTI(hj, batchSize, nResults, batch, false)
   223  			default:
   224  				_COLLECT_PROBE_NO_OUTER(hj, batchSize, nResults, batch, false)
   225  			}
   226  		}
   227  	}
   228  
   229  	return nResults
   230  }
   231  
   232  // distinctCollect prepares the batch with the joined output columns where the build
   233  // row index for each probe row is given in the groupID slice. This function
   234  // requires assumes a N-1 hash join.
   235  func (hj *hashJoiner) distinctCollect(batch coldata.Batch, batchSize int, sel []int) int {
   236  	nResults := int(0)
   237  
   238  	if hj.spec.left.outer {
   239  		nResults = batchSize
   240  
   241  		if sel != nil {
   242  			_DISTINCT_COLLECT_PROBE_OUTER(hj, batchSize, true)
   243  		} else {
   244  			_DISTINCT_COLLECT_PROBE_OUTER(hj, batchSize, false)
   245  		}
   246  	} else {
   247  		if sel != nil {
   248  			switch hj.spec.joinType {
   249  			case sqlbase.LeftAntiJoin, sqlbase.ExceptAllJoin:
   250  				// {{/* For LEFT ANTI and EXCEPT ALL joins we don't care whether the build
   251  				// (right) side was distinct, so we only have single variation of COLLECT
   252  				// method. */}}
   253  				_COLLECT_ANTI(hj, batchSize, nResults, batch, true)
   254  			default:
   255  				_DISTINCT_COLLECT_PROBE_NO_OUTER(hj, batchSize, nResults, true)
   256  			}
   257  		} else {
   258  			switch hj.spec.joinType {
   259  			case sqlbase.LeftAntiJoin, sqlbase.ExceptAllJoin:
   260  				// {{/* For LEFT ANTI and EXCEPT ALL joins we don't care whether the build
   261  				// (right) side was distinct, so we only have single variation of COLLECT
   262  				// method. */}}
   263  				_COLLECT_ANTI(hj, batchSize, nResults, batch, false)
   264  			default:
   265  				_DISTINCT_COLLECT_PROBE_NO_OUTER(hj, batchSize, nResults, false)
   266  			}
   267  		}
   268  	}
   269  
   270  	return nResults
   271  }