vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/pullout_subquery_test.go (about)

     1  /*
     2  Copyright 2019 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  	"errors"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/require"
    25  
    26  	"vitess.io/vitess/go/sqltypes"
    27  
    28  	querypb "vitess.io/vitess/go/vt/proto/query"
    29  )
    30  
    31  func TestPulloutSubqueryValueGood(t *testing.T) {
    32  	// Test one case with actual bind vars.
    33  	bindVars := map[string]*querypb.BindVariable{
    34  		"aa": sqltypes.Int64BindVariable(1),
    35  	}
    36  
    37  	sqResult := sqltypes.MakeTestResult(
    38  		sqltypes.MakeTestFields(
    39  			"col1",
    40  			"int64",
    41  		),
    42  		"1",
    43  	)
    44  	sfp := &fakePrimitive{
    45  		results: []*sqltypes.Result{sqResult},
    46  	}
    47  	underlyingResult := sqltypes.MakeTestResult(
    48  		sqltypes.MakeTestFields(
    49  			"col",
    50  			"int64",
    51  		),
    52  		"0",
    53  	)
    54  	ufp := &fakePrimitive{
    55  		results: []*sqltypes.Result{underlyingResult},
    56  	}
    57  	ps := &PulloutSubquery{
    58  		Opcode:         PulloutValue,
    59  		SubqueryResult: "sq",
    60  		Subquery:       sfp,
    61  		Underlying:     ufp,
    62  	}
    63  
    64  	result, err := ps.TryExecute(context.Background(), &noopVCursor{}, bindVars, false)
    65  	require.NoError(t, err)
    66  	sfp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" false`})
    67  	ufp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" sq: type:INT64 value:"1" false`})
    68  	expectResult(t, "ps.Execute", result, underlyingResult)
    69  }
    70  
    71  func TestPulloutSubqueryValueNone(t *testing.T) {
    72  	sqResult := sqltypes.MakeTestResult(
    73  		sqltypes.MakeTestFields(
    74  			"col1",
    75  			"int64",
    76  		),
    77  	)
    78  	sfp := &fakePrimitive{
    79  		results: []*sqltypes.Result{sqResult},
    80  	}
    81  	ufp := &fakePrimitive{}
    82  	ps := &PulloutSubquery{
    83  		Opcode:         PulloutValue,
    84  		SubqueryResult: "sq",
    85  		Subquery:       sfp,
    86  		Underlying:     ufp,
    87  	}
    88  
    89  	if _, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false); err != nil {
    90  		t.Error(err)
    91  	}
    92  	sfp.ExpectLog(t, []string{`Execute  false`})
    93  	ufp.ExpectLog(t, []string{`Execute sq:  false`})
    94  }
    95  
    96  func TestPulloutSubqueryValueBadColumns(t *testing.T) {
    97  	sqResult := sqltypes.MakeTestResult(
    98  		sqltypes.MakeTestFields(
    99  			"col1|col2",
   100  			"int64|int64",
   101  		),
   102  		"1|1",
   103  	)
   104  	sfp := &fakePrimitive{
   105  		results: []*sqltypes.Result{sqResult},
   106  	}
   107  	ps := &PulloutSubquery{
   108  		Opcode:         PulloutValue,
   109  		SubqueryResult: "sq",
   110  		Subquery:       sfp,
   111  	}
   112  
   113  	_, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false)
   114  	require.EqualError(t, err, "subquery returned more than one column")
   115  }
   116  
   117  func TestPulloutSubqueryValueBadRows(t *testing.T) {
   118  	sqResult := sqltypes.MakeTestResult(
   119  		sqltypes.MakeTestFields(
   120  			"col1",
   121  			"int64",
   122  		),
   123  		"1",
   124  		"2",
   125  	)
   126  	sfp := &fakePrimitive{
   127  		results: []*sqltypes.Result{sqResult},
   128  	}
   129  	ps := &PulloutSubquery{
   130  		Opcode:         PulloutValue,
   131  		SubqueryResult: "sq",
   132  		Subquery:       sfp,
   133  	}
   134  
   135  	_, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false)
   136  	require.EqualError(t, err, "subquery returned more than one row")
   137  }
   138  
   139  func TestPulloutSubqueryInNotinGood(t *testing.T) {
   140  	sqResult := sqltypes.MakeTestResult(
   141  		sqltypes.MakeTestFields(
   142  			"col1",
   143  			"int64",
   144  		),
   145  		"1",
   146  		"2",
   147  	)
   148  	sfp := &fakePrimitive{
   149  		results: []*sqltypes.Result{sqResult},
   150  	}
   151  	ufp := &fakePrimitive{}
   152  	ps := &PulloutSubquery{
   153  		Opcode:         PulloutIn,
   154  		SubqueryResult: "sq",
   155  		HasValues:      "has_values",
   156  		Subquery:       sfp,
   157  		Underlying:     ufp,
   158  	}
   159  
   160  	if _, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false); err != nil {
   161  		t.Error(err)
   162  	}
   163  	sfp.ExpectLog(t, []string{`Execute  false`})
   164  	ufp.ExpectLog(t, []string{`Execute has_values: type:INT64 value:"1" sq: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} false`})
   165  
   166  	// Test the NOT IN case just once even though it's common code.
   167  	sfp.rewind()
   168  	ufp.rewind()
   169  	ps.Opcode = PulloutNotIn
   170  	if _, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false); err != nil {
   171  		t.Error(err)
   172  	}
   173  	sfp.ExpectLog(t, []string{`Execute  false`})
   174  	ufp.ExpectLog(t, []string{`Execute has_values: type:INT64 value:"1" sq: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} false`})
   175  }
   176  
   177  func TestPulloutSubqueryInNone(t *testing.T) {
   178  	sqResult := sqltypes.MakeTestResult(
   179  		sqltypes.MakeTestFields(
   180  			"col1",
   181  			"int64",
   182  		),
   183  	)
   184  	sfp := &fakePrimitive{
   185  		results: []*sqltypes.Result{sqResult},
   186  	}
   187  	ufp := &fakePrimitive{}
   188  	ps := &PulloutSubquery{
   189  		Opcode:         PulloutIn,
   190  		SubqueryResult: "sq",
   191  		HasValues:      "has_values",
   192  		Subquery:       sfp,
   193  		Underlying:     ufp,
   194  	}
   195  
   196  	if _, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false); err != nil {
   197  		t.Error(err)
   198  	}
   199  	sfp.ExpectLog(t, []string{`Execute  false`})
   200  	ufp.ExpectLog(t, []string{`Execute has_values: type:INT64 value:"0" sq: type:TUPLE values:{type:INT64 value:"0"} false`})
   201  }
   202  
   203  func TestPulloutSubqueryInBadColumns(t *testing.T) {
   204  	sqResult := sqltypes.MakeTestResult(
   205  		sqltypes.MakeTestFields(
   206  			"col1|col2",
   207  			"int64|int64",
   208  		),
   209  		"1|1",
   210  	)
   211  	sfp := &fakePrimitive{
   212  		results: []*sqltypes.Result{sqResult},
   213  	}
   214  	ps := &PulloutSubquery{
   215  		Opcode:         PulloutIn,
   216  		SubqueryResult: "sq",
   217  		Subquery:       sfp,
   218  	}
   219  
   220  	_, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false)
   221  	require.EqualError(t, err, "subquery returned more than one column")
   222  }
   223  
   224  func TestPulloutSubqueryExists(t *testing.T) {
   225  	sqResult := sqltypes.MakeTestResult(
   226  		sqltypes.MakeTestFields(
   227  			"col1",
   228  			"int64",
   229  		),
   230  		"1",
   231  	)
   232  	sfp := &fakePrimitive{
   233  		results: []*sqltypes.Result{sqResult},
   234  	}
   235  	ufp := &fakePrimitive{}
   236  	ps := &PulloutSubquery{
   237  		Opcode:     PulloutExists,
   238  		HasValues:  "has_values",
   239  		Subquery:   sfp,
   240  		Underlying: ufp,
   241  	}
   242  
   243  	if _, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false); err != nil {
   244  		t.Error(err)
   245  	}
   246  	sfp.ExpectLog(t, []string{`Execute  false`})
   247  	ufp.ExpectLog(t, []string{`Execute has_values: type:INT64 value:"1" false`})
   248  }
   249  
   250  func TestPulloutSubqueryExistsNone(t *testing.T) {
   251  	sqResult := sqltypes.MakeTestResult(
   252  		sqltypes.MakeTestFields(
   253  			"col1",
   254  			"int64",
   255  		),
   256  	)
   257  	sfp := &fakePrimitive{
   258  		results: []*sqltypes.Result{sqResult},
   259  	}
   260  	ufp := &fakePrimitive{}
   261  	ps := &PulloutSubquery{
   262  		Opcode:     PulloutExists,
   263  		HasValues:  "has_values",
   264  		Subquery:   sfp,
   265  		Underlying: ufp,
   266  	}
   267  
   268  	if _, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false); err != nil {
   269  		t.Error(err)
   270  	}
   271  	sfp.ExpectLog(t, []string{`Execute  false`})
   272  	ufp.ExpectLog(t, []string{`Execute has_values: type:INT64 value:"0" false`})
   273  }
   274  
   275  func TestPulloutSubqueryError(t *testing.T) {
   276  	sfp := &fakePrimitive{
   277  		sendErr: errors.New("err"),
   278  	}
   279  	ps := &PulloutSubquery{
   280  		Opcode:         PulloutExists,
   281  		SubqueryResult: "sq",
   282  		Subquery:       sfp,
   283  	}
   284  
   285  	_, err := ps.TryExecute(context.Background(), &noopVCursor{}, make(map[string]*querypb.BindVariable), false)
   286  	require.EqualError(t, err, "err")
   287  }
   288  
   289  func TestPulloutSubqueryStream(t *testing.T) {
   290  	bindVars := map[string]*querypb.BindVariable{
   291  		"aa": sqltypes.Int64BindVariable(1),
   292  	}
   293  	sqResult := sqltypes.MakeTestResult(
   294  		sqltypes.MakeTestFields(
   295  			"col1",
   296  			"int64",
   297  		),
   298  		"1",
   299  	)
   300  	sfp := &fakePrimitive{
   301  		results: []*sqltypes.Result{sqResult},
   302  	}
   303  	underlyingResult := sqltypes.MakeTestResult(
   304  		sqltypes.MakeTestFields(
   305  			"col",
   306  			"int64",
   307  		),
   308  		"0",
   309  	)
   310  	ufp := &fakePrimitive{
   311  		results: []*sqltypes.Result{underlyingResult},
   312  	}
   313  	ps := &PulloutSubquery{
   314  		Opcode:         PulloutValue,
   315  		SubqueryResult: "sq",
   316  		Subquery:       sfp,
   317  		Underlying:     ufp,
   318  	}
   319  
   320  	result, err := wrapStreamExecute(ps, &noopVCursor{}, bindVars, true)
   321  	require.NoError(t, err)
   322  	sfp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" false`})
   323  	ufp.ExpectLog(t, []string{`StreamExecute aa: type:INT64 value:"1" sq: type:INT64 value:"1" true`})
   324  	expectResult(t, "ps.StreamExecute", result, underlyingResult)
   325  }
   326  
   327  func TestPulloutSubqueryGetFields(t *testing.T) {
   328  	bindVars := map[string]*querypb.BindVariable{
   329  		"aa": sqltypes.Int64BindVariable(1),
   330  	}
   331  	ufp := &fakePrimitive{}
   332  	ps := &PulloutSubquery{
   333  		Opcode:         PulloutValue,
   334  		SubqueryResult: "sq",
   335  		HasValues:      "has_values",
   336  		Underlying:     ufp,
   337  	}
   338  
   339  	if _, err := ps.GetFields(context.Background(), nil, bindVars); err != nil {
   340  		t.Error(err)
   341  	}
   342  	ufp.ExpectLog(t, []string{
   343  		`GetFields aa: type:INT64 value:"1" sq: `,
   344  		`Execute aa: type:INT64 value:"1" sq:  true`,
   345  	})
   346  
   347  	ufp.rewind()
   348  	ps.Opcode = PulloutIn
   349  	if _, err := ps.GetFields(context.Background(), nil, bindVars); err != nil {
   350  		t.Error(err)
   351  	}
   352  	ufp.ExpectLog(t, []string{
   353  		`GetFields aa: type:INT64 value:"1" has_values: type:INT64 value:"0" sq: type:TUPLE values:{type:INT64 value:"0"}`,
   354  		`Execute aa: type:INT64 value:"1" has_values: type:INT64 value:"0" sq: type:TUPLE values:{type:INT64 value:"0"} true`,
   355  	})
   356  
   357  	ufp.rewind()
   358  	ps.Opcode = PulloutNotIn
   359  	if _, err := ps.GetFields(context.Background(), nil, bindVars); err != nil {
   360  		t.Error(err)
   361  	}
   362  	ufp.ExpectLog(t, []string{
   363  		`GetFields aa: type:INT64 value:"1" has_values: type:INT64 value:"0" sq: type:TUPLE values:{type:INT64 value:"0"}`,
   364  		`Execute aa: type:INT64 value:"1" has_values: type:INT64 value:"0" sq: type:TUPLE values:{type:INT64 value:"0"} true`,
   365  	})
   366  
   367  	ufp.rewind()
   368  	ps.Opcode = PulloutExists
   369  	if _, err := ps.GetFields(context.Background(), nil, bindVars); err != nil {
   370  		t.Error(err)
   371  	}
   372  	ufp.ExpectLog(t, []string{
   373  		`GetFields aa: type:INT64 value:"1" has_values: type:INT64 value:"0"`,
   374  		`Execute aa: type:INT64 value:"1" has_values: type:INT64 value:"0" true`,
   375  	})
   376  }