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

     1  // Copyright 2015 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  package sql
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"go/constant"
    17  	"reflect"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/cockroachdb/apd"
    22  	"github.com/cockroachdb/cockroach/pkg/base"
    23  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    24  	"github.com/cockroachdb/cockroach/pkg/security"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    28  	"github.com/cockroachdb/cockroach/pkg/util/bitarray"
    29  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    30  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    31  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    32  	"github.com/cockroachdb/errors"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func makeTestPlanner() *planner {
    37  	// Initialize an Executorconfig sufficiently for the purposes of creating a
    38  	// planner.
    39  	execCfg := ExecutorConfig{
    40  		NodeInfo: NodeInfo{
    41  			NodeID: base.TestingIDContainer,
    42  			ClusterID: func() uuid.UUID {
    43  				return uuid.MakeV4()
    44  			},
    45  		},
    46  	}
    47  
    48  	// TODO(andrei): pass the cleanup along to the caller.
    49  	p, _ /* cleanup */ := newInternalPlanner(
    50  		"test", nil /* txn */, security.RootUser, &MemoryMetrics{}, &execCfg,
    51  	)
    52  	return p
    53  }
    54  
    55  func TestValues(t *testing.T) {
    56  	defer leaktest.AfterTest(t)()
    57  
    58  	p := makeTestPlanner()
    59  
    60  	vInt := int64(5)
    61  	vNum := 3.14159
    62  	vStr := "two furs one cub"
    63  	vBool := true
    64  
    65  	unsupp := &tree.RangeCond{}
    66  
    67  	intVal := func(v int64) *tree.NumVal {
    68  		return tree.NewNumVal(
    69  			constant.MakeInt64(v),
    70  			"", /* origString */
    71  			false /* negative */)
    72  	}
    73  	floatVal := func(f float64) *tree.CastExpr {
    74  		return &tree.CastExpr{
    75  			Expr: tree.NewNumVal(
    76  				constant.MakeFloat64(f),
    77  				"", /* origString */
    78  				false /* negative */),
    79  			Type: types.Float,
    80  		}
    81  	}
    82  	asRow := func(datums ...tree.Datum) []tree.Datums {
    83  		return []tree.Datums{datums}
    84  	}
    85  
    86  	makeValues := func(tuples ...tree.Exprs) *tree.ValuesClause {
    87  		return &tree.ValuesClause{Rows: tuples}
    88  	}
    89  	makeTuple := func(exprs ...tree.Expr) tree.Exprs {
    90  		return tree.Exprs(exprs)
    91  	}
    92  
    93  	testCases := []struct {
    94  		stmt *tree.ValuesClause
    95  		rows []tree.Datums
    96  		ok   bool
    97  	}{
    98  		{
    99  			makeValues(makeTuple(intVal(vInt))),
   100  			asRow(tree.NewDInt(tree.DInt(vInt))),
   101  			true,
   102  		},
   103  		{
   104  			makeValues(makeTuple(intVal(vInt), intVal(vInt))),
   105  			asRow(tree.NewDInt(tree.DInt(vInt)), tree.NewDInt(tree.DInt(vInt))),
   106  			true,
   107  		},
   108  		{
   109  			makeValues(makeTuple(floatVal(vNum))),
   110  			asRow(tree.NewDFloat(tree.DFloat(vNum))),
   111  			true,
   112  		},
   113  		{
   114  			makeValues(makeTuple(tree.NewDString(vStr))),
   115  			asRow(tree.NewDString(vStr)),
   116  			true,
   117  		},
   118  		{
   119  			makeValues(makeTuple(tree.NewDBytes(tree.DBytes(vStr)))),
   120  			asRow(tree.NewDBytes(tree.DBytes(vStr))),
   121  			true,
   122  		},
   123  		{
   124  			makeValues(makeTuple(tree.MakeDBool(tree.DBool(vBool)))),
   125  			asRow(tree.MakeDBool(tree.DBool(vBool))),
   126  			true,
   127  		},
   128  		{
   129  			makeValues(makeTuple(unsupp)),
   130  			nil,
   131  			false,
   132  		},
   133  	}
   134  
   135  	ctx := context.Background()
   136  	for i, tc := range testCases {
   137  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   138  			plan, err := func() (_ planNode, err error) {
   139  				defer func() {
   140  					if r := recover(); r != nil {
   141  						err = errors.Errorf("%v", r)
   142  					}
   143  				}()
   144  				return p.Values(context.Background(), tc.stmt, nil)
   145  			}()
   146  			if plan != nil {
   147  				defer plan.Close(ctx)
   148  			}
   149  			if err == nil != tc.ok {
   150  				t.Errorf("%d: error_expected=%t, but got error %v", i, tc.ok, err)
   151  			}
   152  			if plan == nil {
   153  				return
   154  			}
   155  			params := runParams{ctx: ctx, p: p, extendedEvalCtx: &p.extendedEvalCtx}
   156  			if err := startExec(params, plan); err != nil {
   157  				t.Fatalf("%d: unexpected error in Start: %v", i, err)
   158  			}
   159  			var rows []tree.Datums
   160  			next, err := plan.Next(params)
   161  			for ; next; next, err = plan.Next(params) {
   162  				rows = append(rows, plan.Values())
   163  			}
   164  			if err != nil {
   165  				t.Fatal(err)
   166  			}
   167  			if !reflect.DeepEqual(rows, tc.rows) {
   168  				t.Errorf("%d: expected rows:\n%+v\nactual rows:\n%+v", i, tc.rows, rows)
   169  			}
   170  		})
   171  	}
   172  }
   173  
   174  type floatAlias float32
   175  type boolAlias bool
   176  type stringAlias string
   177  
   178  func TestGolangQueryArgs(t *testing.T) {
   179  	defer leaktest.AfterTest(t)()
   180  
   181  	// Each test case pairs an arbitrary value and tree.Datum which has the same
   182  	// type
   183  	testCases := []struct {
   184  		value        interface{}
   185  		expectedType *types.T
   186  	}{
   187  		// Null type.
   188  		{nil, types.Unknown},
   189  		{[]byte(nil), types.Unknown},
   190  
   191  		// Bool type.
   192  		{true, types.Bool},
   193  
   194  		// Primitive Integer types.
   195  		{int(1), types.Int},
   196  		{int8(1), types.Int},
   197  		{int16(1), types.Int},
   198  		{int32(1), types.Int},
   199  		{int64(1), types.Int},
   200  		{uint(1), types.Int},
   201  		{uint8(1), types.Int},
   202  		{uint16(1), types.Int},
   203  		{uint32(1), types.Int},
   204  		{uint64(1), types.Int},
   205  
   206  		// Primitive Float types.
   207  		{float32(1.0), types.Float},
   208  		{float64(1.0), types.Float},
   209  
   210  		// Decimal type.
   211  		{apd.New(55, 1), types.Decimal},
   212  
   213  		// String type.
   214  		{"test", types.String},
   215  
   216  		// Bytes type.
   217  		{[]byte("abc"), types.Bytes},
   218  
   219  		// Interval and timestamp.
   220  		{time.Duration(1), types.Interval},
   221  		{timeutil.Now(), types.Timestamp},
   222  
   223  		// Primitive type aliases.
   224  		{roachpb.NodeID(1), types.Int},
   225  		{sqlbase.ID(1), types.Int},
   226  		{floatAlias(1), types.Float},
   227  		{boolAlias(true), types.Bool},
   228  		{stringAlias("string"), types.String},
   229  
   230  		// Byte slice aliases.
   231  		{roachpb.Key("key"), types.Bytes},
   232  		{roachpb.RKey("key"), types.Bytes},
   233  
   234  		// Bit array.
   235  		{bitarray.MakeBitArrayFromInt64(8, 58, 7), types.VarBit},
   236  	}
   237  
   238  	for i, tcase := range testCases {
   239  		datums, err := golangFillQueryArguments(tcase.value)
   240  		require.NoError(t, err)
   241  		if len(datums) != 1 {
   242  			t.Fatalf("expected 1 datum, got: %d", len(datums))
   243  		}
   244  		d := datums[0]
   245  		if a, e := d.ResolvedType(), tcase.expectedType; !a.Equal(e) {
   246  			t.Errorf("case %d failed: expected type %s, got %s", i, e.String(), a.String())
   247  		}
   248  	}
   249  }