github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/compile/compile_test.go (about)

     1  // Copyright 2021 Matrix Origin
     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 compile
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"github.com/matrixorigin/matrixone/pkg/txn/client"
    21  	"os"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/matrixorigin/matrixone/pkg/catalog"
    26  	"github.com/matrixorigin/matrixone/pkg/defines"
    27  
    28  	"github.com/matrixorigin/matrixone/pkg/common/reuse"
    29  
    30  	"github.com/golang/mock/gomock"
    31  	"github.com/matrixorigin/matrixone/pkg/cnservice/cnclient"
    32  	"github.com/matrixorigin/matrixone/pkg/common/buffer"
    33  	"github.com/matrixorigin/matrixone/pkg/container/batch"
    34  	mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test"
    35  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    36  	"github.com/matrixorigin/matrixone/pkg/pb/txn"
    37  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect/mysql"
    38  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    39  	plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan"
    40  	"github.com/matrixorigin/matrixone/pkg/testutil"
    41  	"github.com/matrixorigin/matrixone/pkg/testutil/testengine"
    42  	"github.com/matrixorigin/matrixone/pkg/util/fault"
    43  	"github.com/matrixorigin/matrixone/pkg/vm/engine"
    44  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    45  	"github.com/stretchr/testify/require"
    46  )
    47  
    48  type compileTestCase struct {
    49  	sql  string
    50  	pn   *plan.Plan
    51  	e    engine.Engine
    52  	stmt tree.Statement
    53  	proc *process.Process
    54  }
    55  
    56  var (
    57  	tcs []compileTestCase
    58  )
    59  
    60  func init() {
    61  	tcs = []compileTestCase{
    62  		newTestCase("select 1", new(testing.T)),
    63  		newTestCase("select * from R", new(testing.T)),
    64  		newTestCase("select * from R where uid > 1", new(testing.T)),
    65  		newTestCase("select * from R order by uid", new(testing.T)),
    66  		newTestCase("select * from R order by uid limit 1", new(testing.T)),
    67  		newTestCase("select * from R limit 1", new(testing.T)),
    68  		newTestCase("select * from R limit 2, 1", new(testing.T)),
    69  		newTestCase("select count(*) from R", new(testing.T)),
    70  		newTestCase("select * from R join S on R.uid = S.uid", new(testing.T)),
    71  		newTestCase("select * from R left join S on R.uid = S.uid", new(testing.T)),
    72  		newTestCase("select * from R right join S on R.uid = S.uid", new(testing.T)),
    73  		newTestCase("select * from R join S on R.uid > S.uid", new(testing.T)),
    74  		newTestCase("select * from R limit 10", new(testing.T)),
    75  		newTestCase("select count(*) from R group by uid", new(testing.T)),
    76  		newTestCase("select count(distinct uid) from R", new(testing.T)),
    77  		// xxx because memEngine can not handle Halloween Problem
    78  		// newTestCase("insert into R values('991', '992', '993')", new(testing.T)),
    79  		// newTestCase("insert into R select * from S", new(testing.T)),
    80  		// newTestCase("update R set uid=110 where orderid='abcd'", new(testing.T)),
    81  		newTestCase(fmt.Sprintf("load data infile {\"filepath\"=\"%s/../../../test/distributed/resources/load_data/parallel.txt.gz\", \"compression\"=\"gzip\"} into table pressTbl FIELDS TERMINATED BY '|' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n' parallel 'true';", GetFilePath()), new(testing.T)),
    82  	}
    83  }
    84  
    85  func testPrint(_ *batch.Batch) error {
    86  	return nil
    87  }
    88  
    89  type Ws struct {
    90  }
    91  
    92  func (w *Ws) IncrStatementID(ctx context.Context, commit bool) error {
    93  	return nil
    94  }
    95  
    96  func (w *Ws) RollbackLastStatement(ctx context.Context) error {
    97  	return nil
    98  }
    99  
   100  func (w *Ws) Commit(ctx context.Context) ([]txn.TxnRequest, error) {
   101  	return nil, nil
   102  }
   103  
   104  func (w *Ws) Rollback(ctx context.Context) error {
   105  	return nil
   106  }
   107  
   108  func (w *Ws) UpdateSnapshotWriteOffset() {
   109  }
   110  
   111  func (w *Ws) GetSnapshotWriteOffset() int {
   112  	return 0
   113  }
   114  
   115  func (w *Ws) Adjust(_ uint64) error {
   116  	return nil
   117  }
   118  
   119  func (w *Ws) StartStatement()     {}
   120  func (w *Ws) EndStatement()       {}
   121  func (w *Ws) IncrSQLCount()       {}
   122  func (w *Ws) GetSQLCount() uint64 { return 0 }
   123  
   124  func (w *Ws) CloneSnapshotWS() client.Workspace {
   125  	return nil
   126  }
   127  
   128  func (w *Ws) BindTxnOp(op client.TxnOperator) {
   129  }
   130  
   131  func TestCompile(t *testing.T) {
   132  	cnclient.NewCNClient("test", new(cnclient.ClientConfig))
   133  	ctrl := gomock.NewController(t)
   134  	ctx := defines.AttachAccountId(context.TODO(), catalog.System_Account)
   135  	txnOperator := mock_frontend.NewMockTxnOperator(ctrl)
   136  	txnOperator.EXPECT().Commit(gomock.Any()).Return(nil).AnyTimes()
   137  	txnOperator.EXPECT().Rollback(ctx).Return(nil).AnyTimes()
   138  	txnOperator.EXPECT().GetWorkspace().Return(&Ws{}).AnyTimes()
   139  	txnOperator.EXPECT().Txn().Return(txn.TxnMeta{}).AnyTimes()
   140  	txnOperator.EXPECT().ResetRetry(gomock.Any()).AnyTimes()
   141  	txnOperator.EXPECT().TxnOptions().Return(txn.TxnOptions{}).AnyTimes()
   142  	txnOperator.EXPECT().NextSequence().Return(uint64(0)).AnyTimes()
   143  	txnOperator.EXPECT().EnterRunSql().Return().AnyTimes()
   144  	txnOperator.EXPECT().ExitRunSql().Return().AnyTimes()
   145  	txnClient := mock_frontend.NewMockTxnClient(ctrl)
   146  	txnClient.EXPECT().New(gomock.Any(), gomock.Any()).Return(txnOperator, nil).AnyTimes()
   147  	for _, tc := range tcs {
   148  		tc.proc.TxnClient = txnClient
   149  		tc.proc.TxnOperator = txnOperator
   150  		tc.proc.Ctx = ctx
   151  		c := NewCompile("test", "test", tc.sql, "", "", ctx, tc.e, tc.proc, tc.stmt, false, nil, time.Now())
   152  		err := c.Compile(ctx, tc.pn, testPrint)
   153  		require.NoError(t, err)
   154  		c.getAffectedRows()
   155  		_, err = c.Run(0)
   156  		require.NoError(t, err)
   157  		// Enable memory check
   158  		tc.proc.FreeVectors()
   159  		require.Equal(t, int64(0), tc.proc.Mp().CurrNB())
   160  		tc.proc.SessionInfo.Buf.Free()
   161  	}
   162  }
   163  
   164  func TestCompileWithFaults(t *testing.T) {
   165  	// Enable this line to trigger the Hung.
   166  	// fault.Enable()
   167  	var ctx = defines.AttachAccountId(context.Background(), catalog.System_Account)
   168  	cnclient.NewCNClient("test", new(cnclient.ClientConfig))
   169  	fault.AddFaultPoint(ctx, "panic_in_batch_append", ":::", "panic", 0, "")
   170  	tc := newTestCase("select * from R join S on R.uid = S.uid", t)
   171  	tc.proc.Ctx = ctx
   172  	c := NewCompile("test", "test", tc.sql, "", "", ctx, tc.e, tc.proc, nil, false, nil, time.Now())
   173  	err := c.Compile(ctx, tc.pn, testPrint)
   174  	require.NoError(t, err)
   175  	c.getAffectedRows()
   176  	_, err = c.Run(0)
   177  	require.NoError(t, err)
   178  }
   179  
   180  func newTestCase(sql string, t *testing.T) compileTestCase {
   181  	proc := testutil.NewProcess()
   182  	proc.SessionInfo.Buf = buffer.New()
   183  	e, _, compilerCtx := testengine.New(defines.AttachAccountId(context.Background(), catalog.System_Account))
   184  	stmts, err := mysql.Parse(compilerCtx.GetContext(), sql, 1, 0)
   185  	require.NoError(t, err)
   186  	pn, err := plan2.BuildPlan(compilerCtx, stmts[0], false)
   187  	if err != nil {
   188  		panic(err)
   189  	}
   190  	require.NoError(t, err)
   191  	return compileTestCase{
   192  		e:    e,
   193  		sql:  sql,
   194  		proc: proc,
   195  		pn:   pn,
   196  		stmt: stmts[0],
   197  	}
   198  }
   199  
   200  func TestCompileShouldReturnCtxError(t *testing.T) {
   201  	{
   202  		c := reuse.Alloc[Compile](nil)
   203  		c.proc = &process.Process{}
   204  		ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
   205  		c.proc.Ctx = ctx
   206  		time.Sleep(time.Second)
   207  		require.True(t, c.shouldReturnCtxErr())
   208  		cancel()
   209  		require.True(t, c.shouldReturnCtxErr())
   210  	}
   211  
   212  	{
   213  		c := reuse.Alloc[Compile](nil)
   214  		c.proc = &process.Process{}
   215  		ctx, cancel := context.WithTimeout(context.TODO(), 500*time.Millisecond)
   216  		c.proc.Ctx = ctx
   217  		cancel()
   218  		require.False(t, c.shouldReturnCtxErr())
   219  		time.Sleep(time.Second)
   220  		require.False(t, c.shouldReturnCtxErr())
   221  	}
   222  }
   223  
   224  func GetFilePath() string {
   225  	dir, _ := os.Getwd()
   226  	return dir
   227  }