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() sqlQuery := newTestSQLQuery(ctx, enableRowCache|enableStrict) qre := newTestQueryExecutor(ctx, sqlQuery, query, 0) defer sqlQuery.disallowQueries() 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) } }
func TestTabletErrorRetriableErrorTypeOverwrite(t *testing.T) { sqlErr := sqldb.NewSqlError(mysql.ErrOptionPreventsStatement, "read-only") tabletErr := NewTabletErrorSql(ErrFatal, vtrpc.ErrorCode_UNKNOWN_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) } }
// ExecuteFetch executes the query on the connection func (conn *Connection) ExecuteFetch(query string, maxrows int, wantfields bool) (qr *proto.QueryResult, 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 = &proto.QueryResult{} 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, sqlErr) if tabletErr.ErrorType != ErrRetry { t.Fatalf("tablet error should have error type: ErrRetry") } }
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: '") } }
// 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 TestTabletErrorFatal(t *testing.T) { tabletErr := NewTabletErrorSql(ErrFatal, vtrpc.ErrorCode_UNKNOWN_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 TestTabletErrorHandleTxPoolFullError(t *testing.T) { var err error tabletErr := NewTabletErrorSql(ErrTxPoolFull, 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 TestTabletErrorConnError(t *testing.T) { tabletErr := NewTabletErrorSql(ErrFatal, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSqlError(1999, "test")) if IsConnErr(tabletErr) { t.Fatalf("table error: %v is not a connection error", tabletErr) } tabletErr = NewTabletErrorSql(ErrFatal, vtrpc.ErrorCode_UNKNOWN_ERROR, sqldb.NewSqlError(2000, "test")) if !IsConnErr(tabletErr) { t.Fatalf("table error: %v is a connection error", tabletErr) } tabletErr = NewTabletErrorSql(ErrFatal, vtrpc.ErrorCode_UNKNOWN_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_UNKNOWN_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 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, sqlErr) if tabletErr.ErrorType != ErrFatal { t.Fatalf("tablet error should have error type: ErrFatal") } 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_UNKNOWN_ERROR, 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_UNKNOWN_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_UNKNOWN_ERROR, 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_UNKNOWN_ERROR, 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") } }