github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/table/keyless_reader_test.go (about) 1 // Copyright 2020 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package table_test 16 17 import ( 18 "context" 19 "fmt" 20 "io" 21 "testing" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 27 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 28 dtu "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" 29 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 30 "github.com/dolthub/dolt/go/libraries/doltcore/schema/encoding" 31 "github.com/dolthub/dolt/go/libraries/doltcore/table" 32 "github.com/dolthub/dolt/go/store/types" 33 ) 34 35 func TestKeylessTableReader(t *testing.T) { 36 sch := dtu.CreateSchema( 37 schema.NewColumn("c0", 0, types.IntKind, false), 38 schema.NewColumn("c1", 1, types.IntKind, false)) 39 40 type bagRow struct { 41 vals sql.Row 42 cardinality uint64 43 } 44 45 makeBag := func(vrw types.ValueReadWriter, sch schema.Schema, rows ...bagRow) types.Map { 46 var tups []types.Value 47 for _, r := range rows { 48 k, v, err := encodeKeylessSqlRows(vrw, sch, r.vals, r.cardinality) 49 require.NoError(t, err) 50 require.NotNil(t, k) 51 require.NotNil(t, v) 52 53 tups = append(tups, k, v) 54 } 55 return dtu.MustMap(t, vrw, tups...) 56 } 57 58 tests := []struct { 59 name string 60 sch schema.Schema 61 rows []bagRow 62 // read order is pseudorandom, based on hash of values 63 expected []sql.Row 64 }{ 65 { 66 name: "read empty map", 67 sch: sch, 68 }, 69 { 70 name: "read non-duplicate map", 71 sch: sch, 72 rows: []bagRow{ 73 {sql.NewRow(int64(0), int64(0)), 1}, 74 {sql.NewRow(int64(1), int64(1)), 1}, 75 {sql.NewRow(int64(2), int64(2)), 1}, 76 }, 77 expected: []sql.Row{ 78 sql.NewRow(int64(2), int64(2)), 79 sql.NewRow(int64(0), int64(0)), 80 sql.NewRow(int64(1), int64(1)), 81 }, 82 }, 83 { 84 name: "read duplicate map", 85 sch: sch, 86 rows: []bagRow{ 87 {sql.NewRow(int64(0), int64(0)), 1}, 88 {sql.NewRow(int64(1), int64(1)), 2}, 89 {sql.NewRow(int64(2), int64(2)), 3}, 90 }, 91 expected: []sql.Row{ 92 sql.NewRow(int64(2), int64(2)), 93 sql.NewRow(int64(2), int64(2)), 94 sql.NewRow(int64(2), int64(2)), 95 sql.NewRow(int64(0), int64(0)), 96 sql.NewRow(int64(1), int64(1)), 97 sql.NewRow(int64(1), int64(1)), 98 }, 99 }, 100 { 101 name: "read order independent of write order", 102 sch: sch, 103 rows: []bagRow{ 104 {sql.NewRow(int64(2), int64(2)), 1}, 105 {sql.NewRow(int64(1), int64(1)), 1}, 106 {sql.NewRow(int64(0), int64(0)), 1}, 107 }, 108 expected: []sql.Row{ 109 sql.NewRow(int64(2), int64(2)), 110 sql.NewRow(int64(0), int64(0)), 111 sql.NewRow(int64(1), int64(1)), 112 }, 113 }, 114 } 115 116 dEnv := dtu.CreateTestEnv() 117 ctx := context.Background() 118 vrw := dEnv.DoltDB.ValueReadWriter() 119 schVal, err := encoding.MarshalSchemaAsNomsValue(ctx, vrw, sch) 120 require.NoError(t, err) 121 empty := dtu.MustMap(t, vrw) 122 123 compareRows := func(t *testing.T, expected []sql.Row, rdr table.SqlTableReader) { 124 for _, exp := range expected { 125 act, err := rdr.ReadSqlRow(ctx) 126 assert.NoError(t, err) 127 assert.Equal(t, exp, act) 128 } 129 r, err := rdr.ReadSqlRow(ctx) 130 assert.Equal(t, io.EOF, err) 131 assert.Nil(t, r) 132 } 133 134 for _, test := range tests { 135 t.Run(test.name, func(t *testing.T) { 136 rowMap := makeBag(vrw, sch, test.rows...) 137 tbl, err := doltdb.NewTable(ctx, vrw, schVal, rowMap, empty, nil) 138 require.NoError(t, err) 139 rdr, err := table.NewTableReader(ctx, tbl) 140 require.NoError(t, err) 141 compareRows(t, test.expected, rdr) 142 }) 143 t.Run(test.name+"_buffered", func(t *testing.T) { 144 rowMap := makeBag(vrw, sch, test.rows...) 145 tbl, err := doltdb.NewTable(ctx, vrw, schVal, rowMap, empty, nil) 146 require.NoError(t, err) 147 rdr, err := table.NewBufferedTableReader(ctx, tbl) 148 require.NoError(t, err) 149 compareRows(t, test.expected, rdr) 150 }) 151 } 152 } 153 154 func encodeKeylessSqlRows(vrw types.ValueReadWriter, sch schema.Schema, r sql.Row, cardinality uint64) (key, val types.Tuple, err error) { 155 if len(r) != sch.GetAllCols().Size() { 156 rl, sl := len(r), sch.GetAllCols().Size() 157 return key, val, fmt.Errorf("row length (%d) != schema length (%d)", rl, sl) 158 } 159 160 size := 0 161 for _, v := range r { 162 // skip NULLS 163 if v != nil { 164 size++ 165 } 166 } 167 168 // { Uint(count), Uint(tag1), Value(val1), ..., Uint(tagN), Value(valN) } 169 vals := make([]types.Value, 2+(size*2)) 170 vals[0] = types.Uint(schema.KeylessRowCardinalityTag) 171 vals[1] = types.Uint(cardinality) 172 173 idx := 0 174 err = sch.GetAllCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 175 v := r[idx] 176 if v != nil { 177 vals[2*idx+2] = types.Uint(tag) 178 vals[2*idx+3], err = col.TypeInfo.ConvertValueToNomsValue(context.Background(), vrw, v) 179 } 180 idx++ 181 182 stop = err != nil 183 return 184 }) 185 if err != nil { 186 return key, val, err 187 } 188 189 id, err := types.UUIDHashedFromValues(vrw.Format(), vals[2:]...) 190 if err != nil { 191 return key, val, err 192 } 193 194 key, err = types.NewTuple(vrw.Format(), id) 195 if err != nil { 196 return key, val, err 197 } 198 199 val, err = types.NewTuple(vrw.Format(), vals...) 200 if err != nil { 201 return key, val, err 202 } 203 204 return key, val, nil 205 }