vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/distinctV3.go (about) 1 /* 2 Copyright 2020 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package engine 18 19 import ( 20 "context" 21 22 "vitess.io/vitess/go/mysql/collations" 23 "vitess.io/vitess/go/sqltypes" 24 querypb "vitess.io/vitess/go/vt/proto/query" 25 "vitess.io/vitess/go/vt/vtgate/evalengine" 26 ) 27 28 // DistinctV3 Primitive is used to uniqueify results 29 // It does not always work, and should be removed once the V3 planner has been removed 30 var _ Primitive = (*DistinctV3)(nil) 31 32 // Distinct Primitive is used to uniqueify results 33 type DistinctV3 struct { 34 Source Primitive 35 } 36 37 type row = []sqltypes.Value 38 39 type probeTableV3 struct { 40 m map[evalengine.HashCode][]row 41 } 42 43 func (pt *probeTableV3) exists(inputRow row) (bool, error) { 44 // calculate hashcode from all column values in the input row 45 code := evalengine.HashCode(17) 46 for _, value := range inputRow { 47 hashcode, err := evalengine.NullsafeHashcode(value, collations.Unknown, value.Type()) 48 if err != nil { 49 return false, err 50 } 51 code = code*31 + hashcode 52 } 53 54 existingRows, found := pt.m[code] 55 if !found { 56 // nothing with this hash code found, we can be sure it's a not seen row 57 pt.m[code] = []row{inputRow} 58 return false, nil 59 } 60 61 // we found something in the map - still need to check all individual values 62 // so we don't just fall for a hash collision 63 for _, existingRow := range existingRows { 64 exists, err := equalV3(existingRow, inputRow) 65 if err != nil { 66 return false, err 67 } 68 if exists { 69 return true, nil 70 } 71 } 72 73 pt.m[code] = append(existingRows, inputRow) 74 75 return false, nil 76 } 77 78 func equalV3(a, b []sqltypes.Value) (bool, error) { 79 for i, aVal := range a { 80 cmp, err := evalengine.NullsafeCompare(aVal, b[i], collations.Unknown) 81 if err != nil { 82 return false, err 83 } 84 if cmp != 0 { 85 return false, nil 86 } 87 } 88 return true, nil 89 } 90 91 func newProbeTableV3() *probeTableV3 { 92 return &probeTableV3{m: map[evalengine.HashCode][]row{}} 93 } 94 95 // TryExecute implements the Primitive interface 96 func (d *DistinctV3) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { 97 input, err := vcursor.ExecutePrimitive(ctx, d.Source, bindVars, wantfields) 98 if err != nil { 99 return nil, err 100 } 101 102 result := &sqltypes.Result{ 103 Fields: input.Fields, 104 InsertID: input.InsertID, 105 } 106 107 pt := newProbeTableV3() 108 109 for _, row := range input.Rows { 110 exists, err := pt.exists(row) 111 if err != nil { 112 return nil, err 113 } 114 if !exists { 115 result.Rows = append(result.Rows, row) 116 } 117 } 118 119 return result, err 120 } 121 122 // TryStreamExecute implements the Primitive interface 123 func (d *DistinctV3) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { 124 pt := newProbeTableV3() 125 126 err := vcursor.StreamExecutePrimitive(ctx, d.Source, bindVars, wantfields, func(input *sqltypes.Result) error { 127 result := &sqltypes.Result{ 128 Fields: input.Fields, 129 InsertID: input.InsertID, 130 } 131 for _, row := range input.Rows { 132 exists, err := pt.exists(row) 133 if err != nil { 134 return err 135 } 136 if !exists { 137 result.Rows = append(result.Rows, row) 138 } 139 } 140 return callback(result) 141 }) 142 143 return err 144 } 145 146 // RouteType implements the Primitive interface 147 func (d *DistinctV3) RouteType() string { 148 return d.Source.RouteType() 149 } 150 151 // GetKeyspaceName implements the Primitive interface 152 func (d *DistinctV3) GetKeyspaceName() string { 153 return d.Source.GetKeyspaceName() 154 } 155 156 // GetTableName implements the Primitive interface 157 func (d *DistinctV3) GetTableName() string { 158 return d.Source.GetTableName() 159 } 160 161 // GetFields implements the Primitive interface 162 func (d *DistinctV3) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 163 return d.Source.GetFields(ctx, vcursor, bindVars) 164 } 165 166 // NeedsTransaction implements the Primitive interface 167 func (d *DistinctV3) NeedsTransaction() bool { 168 return d.Source.NeedsTransaction() 169 } 170 171 // Inputs implements the Primitive interface 172 func (d *DistinctV3) Inputs() []Primitive { 173 return []Primitive{d.Source} 174 } 175 176 func (d *DistinctV3) description() PrimitiveDescription { 177 return PrimitiveDescription{ 178 OperatorType: "Distinct", 179 } 180 }