func TestTabletErrorRetriableErrorTypeOverwrite(t *testing.T) { sqlErr := sqldb.NewSQLError(mysql.ErrOptionPreventsStatement, "HY000", "read-only") tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr) if tabletErr.ErrorCode != vtrpcpb.ErrorCode_QUERY_NOT_SERVED { t.Fatalf("got: %v wanted: QUERY_NOT_SERVED", tabletErr.ErrorCode) } sqlErr = sqldb.NewSQLError(mysql.ErrDupEntry, "23000", "error") tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr) if tabletErr.ErrorCode != vtrpcpb.ErrorCode_INTEGRITY_ERROR { t.Fatalf("got: %v wanted: INTEGRITY_ERROR", tabletErr.ErrorCode) } sqlErr = sqldb.NewSQLError(mysql.ErrDataTooLong, "22001", "error") tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr) if tabletErr.ErrorCode != vtrpcpb.ErrorCode_BAD_INPUT { t.Fatalf("got: %v wanted: BAD_INPUT", tabletErr.ErrorCode) } sqlErr = sqldb.NewSQLError(mysql.ErrDataOutOfRange, "22003", "error") tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr) if tabletErr.ErrorCode != vtrpcpb.ErrorCode_BAD_INPUT { t.Fatalf("got: %v wanted: BAD_INPUT", tabletErr.ErrorCode) } }
func TestQueryExecutorPlanUpsertPk(t *testing.T) { db := setUpQueryExecutorTest() db.AddQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", &mproto.QueryResult{}) want := &mproto.QueryResult{ Rows: make([][]sqltypes.Value, 0), } query := "insert into test_table values(1) on duplicate key update val=1" ctx := context.Background() tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() checkPlanID(t, planbuilder.PLAN_UPSERT_PK, qre.plan.PlanId) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) } if !reflect.DeepEqual(got, want) { t.Fatalf("got: %v, want: %v", got, want) } db.AddRejectedQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", errRejected) _, err = qre.Execute() wantErr := "error: rejected" if err == nil || err.Error() != wantErr { t.Fatalf("qre.Execute() = %v, want %v", err, wantErr) } db.AddRejectedQuery( "insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", sqldb.NewSQLError(mysql.ErrDupEntry, "err"), ) db.AddQuery("update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &mproto.QueryResult{}) _, err = qre.Execute() wantErr = "error: err (errno 1062)" if err == nil || err.Error() != wantErr { t.Fatalf("qre.Execute() = %v, want %v", err, wantErr) } db.AddRejectedQuery( "insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", sqldb.NewSQLError(mysql.ErrDupEntry, "ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'"), ) db.AddQuery( "update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &mproto.QueryResult{RowsAffected: 1}, ) got, err = qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) } want = &mproto.QueryResult{ RowsAffected: 2, } if !reflect.DeepEqual(got, want) { t.Fatalf("got: %v, want: %v", got, want) } }
// ExecuteFetch executes the query on the connection func (conn *Connection) ExecuteFetch(query string, maxrows int, wantfields bool) (qr *sqltypes.Result, err error) { if conn.IsClosed() { return nil, sqldb.NewSQLError(2006, "Connection is closed") } if C.vt_execute(&conn.c, (*C.char)(hack.StringPointer(query)), C.ulong(len(query)), 0) != 0 { return nil, conn.lastError(query) } defer conn.CloseResult() qr = &sqltypes.Result{} qr.RowsAffected = uint64(conn.c.affected_rows) qr.InsertID = uint64(conn.c.insert_id) if conn.c.num_fields == 0 { return qr, nil } if qr.RowsAffected > uint64(maxrows) { return nil, &sqldb.SQLError{ Num: 0, Message: fmt.Sprintf("Row count exceeded %d", maxrows), Query: string(query), } } if wantfields { qr.Fields = conn.Fields() } qr.Rows, err = conn.fetchAll() return qr, err }
func TestTabletErrorRetriableErrorTypeOverwrite(t *testing.T) { sqlErr := sqldb.NewSQLError(mysql.ErrOptionPreventsStatement, "read-only") tabletErr := NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_INTERNAL_ERROR, sqlErr) if tabletErr.ErrorType != ErrRetry || tabletErr.ErrorCode != vtrpc.ErrorCode_QUERY_NOT_SERVED { t.Fatalf("tablet error should have error type ErrRetry and error code %v", vtrpc.ErrorCode_QUERY_NOT_SERVED) } }
func TestTabletErrorRetriableErrorTypeOverwrite(t *testing.T) { sqlErr := sqldb.NewSQLError(mysql.ErrOptionPreventsStatement, "HY000", "read-only") tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr) if tabletErr.ErrorCode != vtrpcpb.ErrorCode_QUERY_NOT_SERVED { t.Fatalf("got: %v wanted: QUERY_NOT_SERVED", tabletErr.ErrorCode) } }
func TestTabletErrorPrefix(t *testing.T) { tabletErr := NewTabletErrorSQL(ErrRetry, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(2000, "test")) if tabletErr.Prefix() != "retry: " { t.Fatalf("tablet error with error type: ErrRetry should has prefix: 'retry: '") } tabletErr = NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(2000, "test")) if tabletErr.Prefix() != "fatal: " { t.Fatalf("tablet error with error type: ErrFatal should has prefix: 'fatal: '") } tabletErr = NewTabletErrorSQL(ErrTxPoolFull, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(2000, "test")) if tabletErr.Prefix() != "tx_pool_full: " { t.Fatalf("tablet error with error type: ErrTxPoolFull should has prefix: 'tx_pool_full: '") } tabletErr = NewTabletErrorSQL(ErrNotInTx, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(2000, "test")) if tabletErr.Prefix() != "not_in_tx: " { t.Fatalf("tablet error with error type: ErrNotInTx should has prefix: 'not_in_tx: '") } }
func TestTabletErrorPrefix(t *testing.T) { tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_QUERY_NOT_SERVED, sqldb.NewSQLError(2000, "HY000", "test")) if tabletErr.Prefix() != "retry: " { t.Fatalf("tablet error with error code: QUERY_NOT_SERVED should has prefix: 'retry: '") } tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(2000, "HY000", "test")) if tabletErr.Prefix() != "fatal: " { t.Fatalf("tablet error with error code: INTERNAL_ERROR should has prefix: 'fatal: '") } tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, sqldb.NewSQLError(2000, "HY000", "test")) if tabletErr.Prefix() != "tx_pool_full: " { t.Fatalf("tablet error with error code: RESOURCE_EXHAUSTED should has prefix: 'tx_pool_full: '") } tabletErr = NewTabletErrorSQL(vtrpcpb.ErrorCode_NOT_IN_TX, sqldb.NewSQLError(2000, "HY000", "test")) if tabletErr.Prefix() != "not_in_tx: " { t.Fatalf("tablet error with error code: NOT_IN_TX should has prefix: 'not_in_tx: '") } }
// ExecuteStreamFetch starts a streaming query to mysql. Use FetchNext // on the Connection until it returns nil or error func (conn *Connection) ExecuteStreamFetch(query string) (err error) { if conn.IsClosed() { return sqldb.NewSQLError(2006, "Connection is closed") } if C.vt_execute(&conn.c, (*C.char)(hack.StringPointer(query)), C.ulong(len(query)), 1) != 0 { return conn.lastError(query) } return nil }
func TestTabletErrorPrefix(t *testing.T) { tabletErr := NewTabletErrorSQL(ErrRetry, vtrpc.ErrorCode_QUERY_NOT_SERVED, sqldb.NewSQLError(2000, "test")) if tabletErr.Prefix() != "retry: " { t.Fatalf("tablet error with error type: ErrRetry should has prefix: 'retry: '") } tabletErr = NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(2000, "test")) if tabletErr.Prefix() != "fatal: " { t.Fatalf("tablet error with error type: ErrFatal should has prefix: 'fatal: '") } tabletErr = NewTabletErrorSQL(ErrTxPoolFull, vtrpc.ErrorCode_RESOURCE_EXHAUSTED, sqldb.NewSQLError(2000, "test")) if tabletErr.Prefix() != "tx_pool_full: " { t.Fatalf("tablet error with error type: ErrTxPoolFull should has prefix: 'tx_pool_full: '") } tabletErr = NewTabletErrorSQL(ErrNotInTx, vtrpc.ErrorCode_NOT_IN_TX, sqldb.NewSQLError(2000, "test")) if tabletErr.Prefix() != "not_in_tx: " { t.Fatalf("tablet error with error type: ErrNotInTx should has prefix: 'not_in_tx: '") } }
func TestTabletErrorTxPoolFull(t *testing.T) { tabletErr := NewTabletErrorSQL(ErrTxPoolFull, vtrpc.ErrorCode_RESOURCE_EXHAUSTED, sqldb.NewSQLError(1000, "test")) queryServiceStats := NewQueryServiceStats("", false) defer func() { err := recover() if err != nil { t.Fatalf("error should have been handled already") } }() defer logError(queryServiceStats) panic(tabletErr) }
func TestTabletErrorFatal(t *testing.T) { tabletErr := NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(1000, "test")) queryServiceStats := NewQueryServiceStats("", false) defer func() { err := recover() if err != nil { t.Fatalf("error should have been handled already") } }() defer logError(queryServiceStats) panic(tabletErr) }
func TestTabletErrorHandleRetryError(t *testing.T) { var err error tabletErr := NewTabletErrorSQL(ErrRetry, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(1000, "test")) logStats := newSqlQueryStats("TestTabletErrorHandleError", context.Background()) queryServiceStats := NewQueryServiceStats("", false) defer func() { _, ok := err.(*TabletError) if !ok { t.Fatalf("error should be a TabletError, but got error: %v", err) } }() defer handleError(&err, logStats, queryServiceStats) panic(tabletErr) }
func TestTabletErrorHandleTxPoolFullError(t *testing.T) { var err error tabletErr := NewTabletErrorSQL(ErrTxPoolFull, vtrpc.ErrorCode_RESOURCE_EXHAUSTED, sqldb.NewSQLError(1000, "test")) logStats := newLogStats("TestTabletErrorHandleError", context.Background()) queryServiceStats := NewQueryServiceStats("", false) defer func() { _, ok := err.(*TabletError) if !ok { t.Fatalf("error should be a TabletError, but got error: %v", err) } }() defer handleError(&err, logStats, queryServiceStats) panic(tabletErr) }
func TestTabletErrorMsgTooLong(t *testing.T) { buf := make([]byte, 2*maxErrLen) for i := 0; i < 2*maxErrLen; i++ { buf[i] = 'a' } msg := string(buf) sqlErr := sqldb.NewSQLError(mysql.ErrDupEntry, "23000", msg) tabletErr := NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, sqlErr) if tabletErr.ErrorCode != vtrpcpb.ErrorCode_INTEGRITY_ERROR { t.Fatalf("got %v wanted INTEGRITY_ERROR", tabletErr.ErrorCode) } if tabletErr.Message != string(buf[:maxErrLen]) { t.Fatalf("message should be capped, only %d character will be shown", maxErrLen) } }
func TestTabletErrorConnError(t *testing.T) { tabletErr := NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(1999, "test")) if IsConnErr(tabletErr) { t.Fatalf("table error: %v is not a connection error", tabletErr) } tabletErr = NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(2000, "test")) if !IsConnErr(tabletErr) { t.Fatalf("table error: %v is a connection error", tabletErr) } tabletErr = NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(mysql.ErrServerLost, "test")) if IsConnErr(tabletErr) { t.Fatalf("table error: %v is not a connection error", tabletErr) } want := "fatal: the query was killed either because it timed out or was canceled: test (errno 2013)" if tabletErr.Error() != want { t.Fatalf("tablet error: %v, want %s", tabletErr, want) } sqlErr := sqldb.NewSQLError(1998, "test") if IsConnErr(sqlErr) { t.Fatalf("sql error: %v is not a connection error", sqlErr) } sqlErr = sqldb.NewSQLError(2001, "test") if !IsConnErr(sqlErr) { t.Fatalf("sql error: %v is a connection error", sqlErr) } err := fmt.Errorf("(errno 2005)") if !IsConnErr(err) { t.Fatalf("error: %v is a connection error", err) } err = fmt.Errorf("(errno 123456789012345678901234567890)") if IsConnErr(err) { t.Fatalf("error: %v is not a connection error", err) } }
func TestTabletErrorMsgTooLong(t *testing.T) { buf := make([]byte, 2*maxErrLen) for i := 0; i < 2*maxErrLen; i++ { buf[i] = 'a' } msg := string(buf) sqlErr := sqldb.NewSQLError(mysql.ErrDupEntry, msg) tabletErr := NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_INTERNAL_ERROR, sqlErr) if tabletErr.ErrorType != ErrFatal || tabletErr.ErrorCode != vtrpc.ErrorCode_INTEGRITY_ERROR { t.Fatalf("tablet error should have error type ErrFatal and error code %v", vtrpc.ErrorCode_INTEGRITY_ERROR) } if tabletErr.Message != string(buf[:maxErrLen]) { t.Fatalf("message should be capped, only %s character will be shown", maxErrLen) } }
func TestTabletErrorRecordStats(t *testing.T) { tabletErr := NewTabletErrorSQL(ErrRetry, vtrpc.ErrorCode_QUERY_NOT_SERVED, sqldb.NewSQLError(2000, "test")) queryServiceStats := NewQueryServiceStats("", false) retryCounterBefore := queryServiceStats.InfoErrors.Counts()["Retry"] tabletErr.RecordStats(queryServiceStats) retryCounterAfter := queryServiceStats.InfoErrors.Counts()["Retry"] if retryCounterAfter-retryCounterBefore != 1 { t.Fatalf("tablet error with error type ErrRetry should increase Retry error count by 1") } tabletErr = NewTabletErrorSQL(ErrFatal, vtrpc.ErrorCode_INTERNAL_ERROR, sqldb.NewSQLError(2000, "test")) fatalCounterBefore := queryServiceStats.InfoErrors.Counts()["Fatal"] tabletErr.RecordStats(queryServiceStats) fatalCounterAfter := queryServiceStats.InfoErrors.Counts()["Fatal"] if fatalCounterAfter-fatalCounterBefore != 1 { t.Fatalf("tablet error with error type ErrFatal should increase Fatal error count by 1") } tabletErr = NewTabletErrorSQL(ErrTxPoolFull, vtrpc.ErrorCode_RESOURCE_EXHAUSTED, sqldb.NewSQLError(2000, "test")) txPoolFullCounterBefore := queryServiceStats.ErrorStats.Counts()["TxPoolFull"] tabletErr.RecordStats(queryServiceStats) txPoolFullCounterAfter := queryServiceStats.ErrorStats.Counts()["TxPoolFull"] if txPoolFullCounterAfter-txPoolFullCounterBefore != 1 { t.Fatalf("tablet error with error type ErrTxPoolFull should increase TxPoolFull error count by 1") } tabletErr = NewTabletErrorSQL(ErrNotInTx, vtrpc.ErrorCode_NOT_IN_TX, sqldb.NewSQLError(2000, "test")) notInTxCounterBefore := queryServiceStats.ErrorStats.Counts()["NotInTx"] tabletErr.RecordStats(queryServiceStats) notInTxCounterAfter := queryServiceStats.ErrorStats.Counts()["NotInTx"] if notInTxCounterAfter-notInTxCounterBefore != 1 { t.Fatalf("tablet error with error type ErrNotInTx should increase NotInTx error count by 1") } tabletErr = NewTabletErrorSQL(ErrFail, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(mysql.ErrDupEntry, "test")) dupKeyCounterBefore := queryServiceStats.InfoErrors.Counts()["DupKey"] tabletErr.RecordStats(queryServiceStats) dupKeyCounterAfter := queryServiceStats.InfoErrors.Counts()["DupKey"] if dupKeyCounterAfter-dupKeyCounterBefore != 1 { t.Fatalf("sql error with error type mysql.ErrDupEntry should increase DupKey error count by 1") } tabletErr = NewTabletErrorSQL(ErrFail, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(mysql.ErrLockWaitTimeout, "test")) lockWaitTimeoutCounterBefore := queryServiceStats.ErrorStats.Counts()["Deadlock"] tabletErr.RecordStats(queryServiceStats) lockWaitTimeoutCounterAfter := queryServiceStats.ErrorStats.Counts()["Deadlock"] if lockWaitTimeoutCounterAfter-lockWaitTimeoutCounterBefore != 1 { t.Fatalf("sql error with error type mysql.ErrLockWaitTimeout should increase Deadlock error count by 1") } tabletErr = NewTabletErrorSQL(ErrFail, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(mysql.ErrLockDeadlock, "test")) deadlockCounterBefore := queryServiceStats.ErrorStats.Counts()["Deadlock"] tabletErr.RecordStats(queryServiceStats) deadlockCounterAfter := queryServiceStats.ErrorStats.Counts()["Deadlock"] if deadlockCounterAfter-deadlockCounterBefore != 1 { t.Fatalf("sql error with error type mysql.ErrLockDeadlock should increase Deadlock error count by 1") } tabletErr = NewTabletErrorSQL(ErrFail, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSQLError(mysql.ErrOptionPreventsStatement, "test")) failCounterBefore := queryServiceStats.ErrorStats.Counts()["Fail"] tabletErr.RecordStats(queryServiceStats) failCounterAfter := queryServiceStats.ErrorStats.Counts()["Fail"] if failCounterAfter-failCounterBefore != 1 { t.Fatalf("sql error with error type mysql.ErrOptionPreventsStatement should increase Fail error count by 1") } }
func TestQueryExecutorPlanUpsertPk(t *testing.T) { db := setUpQueryExecutorTest() db.AddQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) want := &sqltypes.Result{ Rows: make([][]sqltypes.Value, 0), } query := "insert into test_table values(1) on duplicate key update val=1" ctx := context.Background() tsv := newTestTabletServer(ctx, enableStrict, db) txid := newTransaction(tsv) qre := newTestQueryExecutor(ctx, tsv, query, txid) defer tsv.StopService() checkPlanID(t, planbuilder.PlanUpsertPK, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) } if !reflect.DeepEqual(got, want) { t.Fatalf("got: %v, want: %v", got, want) } wantqueries := []string{"insert into test_table values (1) /* _stream test_table (pk ) (1 ); */"} gotqueries := fetchRecordedQueries(qre) if !reflect.DeepEqual(gotqueries, wantqueries) { t.Errorf("queries: %v, want %v", gotqueries, wantqueries) } testCommitHelper(t, tsv, qre) db.AddRejectedQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", errRejected) txid = newTransaction(tsv) qre = newTestQueryExecutor(ctx, tsv, query, txid) _, err = qre.Execute() wantErr := "error: rejected" if err == nil || err.Error() != wantErr { t.Errorf("qre.Execute() = %v, want %v", err, wantErr) } wantqueries = []string{} gotqueries = fetchRecordedQueries(qre) if !reflect.DeepEqual(gotqueries, wantqueries) { t.Errorf("queries: %v, want %v", gotqueries, wantqueries) } testCommitHelper(t, tsv, qre) db.AddRejectedQuery( "insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", sqldb.NewSQLError(mysql.ErrDupEntry, "23000", "err"), ) db.AddQuery("update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) txid = newTransaction(tsv) qre = newTestQueryExecutor(ctx, tsv, query, txid) _, err = qre.Execute() wantErr = "error: err (errno 1062) (sqlstate 23000)" if err == nil || err.Error() != wantErr { t.Errorf("qre.Execute() = %v, want %v", err, wantErr) } wantqueries = []string{} gotqueries = fetchRecordedQueries(qre) if !reflect.DeepEqual(gotqueries, wantqueries) { t.Errorf("queries: %v, want %v", gotqueries, wantqueries) } testCommitHelper(t, tsv, qre) db.AddRejectedQuery( "insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", sqldb.NewSQLError(mysql.ErrDupEntry, "23000", "ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'"), ) db.AddQuery( "update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{RowsAffected: 1}, ) txid = newTransaction(tsv) qre = newTestQueryExecutor(ctx, tsv, query, txid) got, err = qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) } want = &sqltypes.Result{ RowsAffected: 2, } if !reflect.DeepEqual(got, want) { t.Errorf("got: %v, want: %v", got, want) } wantqueries = []string{"update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */"} gotqueries = fetchRecordedQueries(qre) if !reflect.DeepEqual(gotqueries, wantqueries) { t.Errorf("queries: %v, want %v", gotqueries, wantqueries) } testCommitHelper(t, tsv, qre) }