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  }