// Test that rando commands while in COMMIT_WAIT return a particular error. func TestCommitWaitState(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, _ := serverutils.StartServer(t, params) defer s.Stopper().Stop() if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k INT PRIMARY KEY, v TEXT); `); err != nil { t.Fatal(err) } tx, err := sqlDB.Begin() if err != nil { t.Fatal(err) } if _, err := tx.Exec( "SAVEPOINT cockroach_restart; RELEASE cockroach_restart"); err != nil { t.Fatal(err) } if _, err := tx.Exec("INSERT INTO t.test(k, v) VALUES (0, 'sentinel')"); !testutils.IsError(err, "current transaction is committed") { t.Fatalf("unexpected error: %v", err) } // Rollback should respond with a COMMIT command tag. if err := tx.Rollback(); !testutils.IsError(err, "unexpected command tag COMMIT") { t.Fatalf("unexpected error: %v", err) } }
// TestRollbackToSavepointStatement tests that issuing a RESTART outside of a // txn produces the proper error. func TestRollbackToSavepointStatement(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, _ := serverutils.StartServer(t, params) defer s.Stopper().Stop() // ROLLBACK TO SAVEPOINT without a transaction _, err := sqlDB.Exec("ROLLBACK TO SAVEPOINT cockroach_restart") if !testutils.IsError(err, "the transaction is not in a retriable state") { t.Fatalf("unexpected error: %v", err) } // ROLLBACK TO SAVEPOINT with a wrong name _, err = sqlDB.Exec("ROLLBACK TO SAVEPOINT foo") if !testutils.IsError(err, "SAVEPOINT not supported except for COCKROACH_RESTART") { t.Fatalf("unexpected error: %v", err) } // ROLLBACK TO SAVEPOINT in a non-retriable transaction tx, err := sqlDB.Begin() if err != nil { t.Fatal(err) } if _, err := tx.Exec("SAVEPOINT cockroach_restart"); err != nil { t.Fatal(err) } if _, err := tx.Exec("BOGUS SQL STATEMENT"); !testutils.IsError(err, `syntax error at or near "BOGUS"`) { t.Fatalf("unexpected error: %v", err) } if _, err := tx.Exec("ROLLBACK TO SAVEPOINT cockroach_restart"); !testutils.IsError( err, "SAVEPOINT COCKROACH_RESTART has not been used or a non-retriable error was encountered", ) { t.Fatalf("unexpected error: %v", err) } }
func TestBadRequest(t *testing.T) { defer leaktest.AfterTest(t)() s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() db := createTestClient(t, s.Stopper(), s.ServingAddr()) ctx := context.TODO() // Write key "a". if err := db.Put(ctx, "a", "value"); err != nil { t.Fatal(err) } if _, err := db.Scan(ctx, "a", "a", 0); !testutils.IsError(err, "truncation resulted in empty batch") { t.Fatalf("unexpected error on scan with startkey == endkey: %v", err) } if _, err := db.ReverseScan(ctx, "a", "a", 0); !testutils.IsError(err, "truncation resulted in empty batch") { t.Fatalf("unexpected error on reverse scan with startkey == endkey: %v", err) } if err := db.DelRange(ctx, "x", "a"); !testutils.IsError(err, "truncation resulted in empty batch") { t.Fatalf("unexpected error on deletion on [x, a): %v", err) } if err := db.DelRange(ctx, "", "z"); !testutils.IsError(err, "must be greater than LocalMax") { t.Fatalf("unexpected error on deletion on [KeyMin, z): %v", err) } }
// TestBatchError verifies that Range returns an error if a request has an invalid range. func TestBatchError(t *testing.T) { testCases := []struct { req [2]string errMsg string }{ { req: [2]string{"\xff\xff\xff\xff", "a"}, errMsg: "must be less than KeyMax", }, { req: [2]string{"a", "\xff\xff\xff\xff"}, errMsg: "must be less than or equal to KeyMax", }, } for i, c := range testCases { var ba roachpb.BatchRequest ba.Add(&roachpb.ScanRequest{Span: roachpb.Span{Key: roachpb.Key(c.req[0]), EndKey: roachpb.Key(c.req[1])}}) if _, err := Range(ba); !testutils.IsError(err, c.errMsg) { t.Errorf("%d: unexpected error %v", i, err) } } // Test a case where a non-range request has an end key. var ba roachpb.BatchRequest ba.Add(&roachpb.GetRequest{Span: roachpb.Span{Key: roachpb.Key("a"), EndKey: roachpb.Key("b")}}) if _, err := Range(ba); !testutils.IsError(err, "end key specified for non-range operation") { t.Errorf("unexpected error %v", err) } }
// Test commands while a table is being dropped. func TestCommandsWhileTableBeingDropped(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() // Block schema changers so that the table we're about to DROP is not // actually dropped; it will be left in the "deleted" state. params.Knobs = base.TestingKnobs{ SQLSchemaChanger: &sql.SchemaChangerTestingKnobs{ SyncFilter: func(tscc sql.TestingSchemaChangerCollection) { tscc.ClearSchemaChangers() }, AsyncExecNotification: asyncSchemaChangerDisabled, }, } s, db, _ := serverutils.StartServer(t, params) defer s.Stopper().Stop() sql := ` CREATE DATABASE test; CREATE TABLE test.t(a INT PRIMARY KEY); ` if _, err := db.Exec(sql); err != nil { t.Fatal(err) } // DROP the table if _, err := db.Exec(`DROP TABLE test.t`); err != nil { t.Fatal(err) } // Check that SHOW TABLES marks a dropped table with the " (dropped)" // suffix. rows, err := db.Query(`SHOW TABLES FROM test`) if err != nil { t.Fatal(err) } defer rows.Close() if !rows.Next() { t.Fatal("table invisible through SHOW TABLES") } var val string if err := rows.Scan(&val); err != nil { t.Errorf("row scan failed: %s", err) } if val != "t (dropped)" { t.Fatalf("table = %s", val) } // Check that CREATE TABLE with the same name returns a proper error. if _, err := db.Exec(`CREATE TABLE test.t(a INT PRIMARY KEY)`); !testutils.IsError(err, `relation "t" already exists`) { t.Fatal(err) } // Check that DROP TABLE with the same name returns a proper error. if _, err := db.Exec(`DROP TABLE test.t`); !testutils.IsError(err, `table "t" is being dropped`) { t.Fatal(err) } }
// Returns true on retriable errors. func runTestTxn( t *testing.T, magicVals *filterVals, expectedErr string, sqlDB *gosql.DB, tx *gosql.Tx, sentinelInsert string, ) bool { retriesNeeded := (magicVals.restartCounts["boulanger"] + magicVals.abortCounts["boulanger"]) > 0 if retriesNeeded { _, err := tx.Exec("INSERT INTO t.test(k, v) VALUES (1, 'boulanger')") if !testutils.IsError(err, expectedErr) { t.Fatalf("unexpected error: %v", err) } return isRetryableErr(err) } // Now the INSERT should succeed. if _, err := tx.Exec( "DELETE FROM t.test WHERE true;" + sentinelInsert, ); err != nil { t.Fatal(err) } _, err := tx.Exec("RELEASE SAVEPOINT cockroach_restart") return isRetryableErr(err) }
// TestNonRetryableErrorOnCommit verifies that a non-retryable error from the // execution of EndTransactionRequests is propagated to the client. func TestNonRetryableErrorOnCommit(t *testing.T) { defer leaktest.AfterTest(t)() params, cmdFilters := createTestServerParams() s, sqlDB, _ := serverutils.StartServer(t, params) defer s.Stopper().Stop() hitError := false cleanupFilter := cmdFilters.AppendFilter( func(args storagebase.FilterArgs) *roachpb.Error { if req, ok := args.Req.(*roachpb.EndTransactionRequest); ok { if bytes.Contains(req.Key, []byte(keys.DescIDGenerator)) { hitError = true return roachpb.NewErrorWithTxn(fmt.Errorf("testError"), args.Hdr.Txn) } } return nil }, false) defer cleanupFilter() if _, err := sqlDB.Exec("CREATE DATABASE t"); !testutils.IsError(err, "pq: testError") { t.Errorf("unexpected error %v", err) } if !hitError { t.Errorf("expected to hit error, but it didn't happen") } }
// TestRocksDBOpenWithVersions verifies the version checking in Open() // functions correctly. func TestRocksDBOpenWithVersions(t *testing.T) { defer leaktest.AfterTest(t)() testCases := []struct { hasFile bool ver Version expectedErr string }{ {false, Version{}, ""}, {true, Version{versionCurrent}, ""}, {true, Version{versionMinimum}, ""}, {true, Version{-1}, "incompatible rocksdb data version, current:1, on disk:-1, minimum:0"}, {true, Version{2}, "incompatible rocksdb data version, current:1, on disk:2, minimum:0"}, } for i, testCase := range testCases { err := openRocksDBWithVersion(t, testCase.hasFile, testCase.ver) if err == nil && len(testCase.expectedErr) == 0 { continue } if !testutils.IsError(err, testCase.expectedErr) { t.Errorf("%d: expected error '%s', actual '%v'", i, testCase.expectedErr, err) } } }
// TestTxnCoordIdempotentCleanup verifies that cleanupTxnLocked is idempotent. func TestTxnCoordIdempotentCleanup(t *testing.T) { defer leaktest.AfterTest(t)() s, sender := createTestDB(t) defer s.Stop() defer teardownHeartbeats(sender) txn := client.NewTxn(context.Background(), *s.DB) ba := txn.NewBatch() ba.Put(roachpb.Key("a"), []byte("value")) if err := txn.Run(ba); err != nil { t.Fatal(err) } sender.Lock() // Clean up twice successively. sender.cleanupTxnLocked(context.Background(), txn.Proto) sender.cleanupTxnLocked(context.Background(), txn.Proto) sender.Unlock() // For good measure, try to commit (which cleans up once more if it // succeeds, which it may not if the previous cleanup has already // terminated the heartbeat goroutine) ba = txn.NewBatch() ba.AddRawRequest(&roachpb.EndTransactionRequest{}) err := txn.Run(ba) if err != nil && !testutils.IsError(err, errNoState.Error()) { t.Fatal(err) } }
func TestTxnAbandonCount(t *testing.T) { defer leaktest.AfterTest(t)() manual, sender, cleanupFn := setupMetricsTest(t) defer cleanupFn() value := []byte("value") db := client.NewDB(sender) // Test abandoned transaction by making the client timeout ridiculously short. We also set // the sender to heartbeat very frequently, because the heartbeat detects and tears down // abandoned transactions. sender.heartbeatInterval = 2 * time.Millisecond sender.clientTimeout = 1 * time.Millisecond if err := db.Txn(context.TODO(), func(txn *client.Txn) error { key := []byte("key-abandon") if err := txn.SetIsolation(enginepb.SNAPSHOT); err != nil { return err } if err := txn.Put(key, value); err != nil { return err } manual.Increment(int64(sender.clientTimeout + sender.heartbeatInterval*2)) checkTxnMetrics(t, sender, "abandon txn", 0, 0, 1, 0, 0) return nil }); !testutils.IsError(err, "writing transaction timed out") { t.Fatalf("unexpected error: %v", err) } }
func TestVerifyClockOffset(t *testing.T) { defer leaktest.AfterTest(t)() clock := hlc.NewClock(hlc.NewManualClock(123).UnixNano, 50*time.Nanosecond) monitor := newRemoteClockMonitor(clock, time.Hour) for idx, tc := range []struct { offsets []RemoteOffset expectedError bool }{ // no error if no offsets. {[]RemoteOffset{}, false}, // no error when a majority of offsets are under the maximum tolerated offset. {[]RemoteOffset{{Offset: 20, Uncertainty: 10}, {Offset: 48, Uncertainty: 20}, {Offset: 61, Uncertainty: 25}, {Offset: 91, Uncertainty: 31}}, false}, // error when less than a majority of offsets are under the maximum tolerated offset. {[]RemoteOffset{{Offset: 20, Uncertainty: 10}, {Offset: 58, Uncertainty: 20}, {Offset: 85, Uncertainty: 25}, {Offset: 91, Uncertainty: 31}}, true}, } { monitor.mu.offsets = make(map[string]RemoteOffset) for i, offset := range tc.offsets { monitor.mu.offsets[strconv.Itoa(i)] = offset } if tc.expectedError { if err := monitor.VerifyClockOffset(context.TODO()); !testutils.IsError(err, errOffsetGreaterThanMaxOffset) { t.Errorf("%d: unexpected error %v", idx, err) } } else { if err := monitor.VerifyClockOffset(context.TODO()); err != nil { t.Errorf("%d: unexpected error %s", idx, err) } } } }
func TestHealthAPI(t *testing.T) { defer leaktest.AfterTest(t)() s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() // We need to retry because the node ID isn't set until after // bootstrapping. testutils.SucceedsSoon(t, func() error { var resp serverpb.HealthResponse return getAdminJSONProto(s, "health", &resp) }) // Expire this node's liveness record by pausing heartbeats and advancing the // server's clock. ts := s.(*TestServer) ts.nodeLiveness.PauseHeartbeat(true) self, err := ts.nodeLiveness.Self() if err != nil { t.Fatal(err) } s.Clock().Update(self.Expiration.Add(1, 0)) expected := "node is not live" var resp serverpb.HealthResponse if err := getAdminJSONProto(s, "health", &resp); !testutils.IsError(err, expected) { t.Errorf("expected %q error, got %v", expected, err) } }
func TestSendNext_AllRetryableApplicationErrors(t *testing.T) { defer leaktest.AfterTest(t)() doneChans, sendChan, stopper := setupSendNextTest(t) defer stopper.Stop() // All replicas finish with a retryable error. for _, ch := range doneChans { ch <- BatchCall{ Reply: &roachpb.BatchResponse{ BatchResponse_Header: roachpb.BatchResponse_Header{ Error: roachpb.NewError(roachpb.NewRangeNotFoundError(1)), }, }, } } // The client send finishes with one of the errors, wrapped in a SendError. bc := <-sendChan if bc.Err == nil { t.Fatalf("expected SendError, got err=nil and reply=%s", bc.Reply) } else if _, ok := bc.Err.(*roachpb.SendError); !ok { t.Fatalf("expected SendError, got err=%s", bc.Err) } else if exp := "range 1 was not found"; !testutils.IsError(bc.Err, exp) { t.Errorf("expected SendError to contain %q, but got %v", exp, bc.Err) } }
// Tests that a retryable error for an inner txn doesn't cause the outer txn to // be retried. func TestWrongTxnRetry(t *testing.T) { defer leaktest.AfterTest(t)() db := NewDB(newTestSender(nil, nil)) var retries int txnClosure := func(outerTxn *Txn) error { log.Infof(context.Background(), "outer retry") retries++ // Ensure the KV transaction is created. if err := outerTxn.Put("a", "b"); err != nil { t.Fatal(err) } var execOpt TxnExecOptions execOpt.AutoRetry = false err := outerTxn.Exec( execOpt, func(innerTxn *Txn, opt *TxnExecOptions) error { // Ensure the KV transaction is created. if err := innerTxn.Put("x", "y"); err != nil { t.Fatal(err) } return roachpb.NewErrorWithTxn(&roachpb.TransactionPushError{ PusheeTxn: outerTxn.Proto}, &innerTxn.Proto).GoError() }) return err } if err := db.Txn(context.TODO(), txnClosure); !testutils.IsError(err, "failed to push") { t.Fatal(err) } if retries != 1 { t.Fatalf("unexpected retries: %d", retries) } }
func runCommandAndExpireLease( t *testing.T, leaseManager *csql.LeaseManager, clock *hlc.Clock, sqlDB *gosql.DB, sql string, ) { // Run a transaction that lets its table lease expire. txn, err := sqlDB.Begin() if err != nil { t.Fatal(err) } // Use snapshot isolation so that the transaction is pushed without being // restarted. if _, err := txn.Exec("SET TRANSACTION ISOLATION LEVEL SNAPSHOT"); err != nil { t.Fatal(err) } if _, err := txn.Exec(sql); err != nil { t.Fatal(err) } leaseManager.ExpireLeases(clock) // Run another transaction that pushes the above transaction. if _, err := sqlDB.Query("SELECT * FROM t.kv"); err != nil { t.Fatal(err) } // Commit and see the aborted txn. if err := txn.Commit(); !testutils.IsError(err, "pq: restart transaction: txn aborted") { t.Fatalf("%s, err = %v", sql, err) } }
// TestAuthentication tests authentication for the KV endpoint. func TestAuthentication(t *testing.T) { defer leaktest.AfterTest(t)() s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() b1 := &client.Batch{} b1.Put("a", "b") // Create a node user client and call Run() on it which lets us build our own // request, specifying the user. db1 := createTestClientForUser(t, s, security.NodeUser) if err := db1.Run(context.TODO(), b1); err != nil { t.Fatal(err) } b2 := &client.Batch{} b2.Put("c", "d") // Try again, but this time with certs for a non-node user (even the root // user has no KV permissions). db2 := createTestClientForUser(t, s, security.RootUser) if err := db2.Run(context.TODO(), b2); !testutils.IsError(err, "is not allowed") { t.Fatal(err) } }
// TestClientForwardUnresolved verifies that a client does not resolve a forward // address prematurely. func TestClientForwardUnresolved(t *testing.T) { defer leaktest.AfterTest(t)() stopper := stop.NewStopper() defer stopper.Stop() const nodeID = 1 local := startGossip(nodeID, stopper, t, metric.NewRegistry()) addr := local.GetNodeAddr() client := newClient(log.AmbientContext{}, addr, makeMetrics()) // never started newAddr := util.UnresolvedAddr{ NetworkField: "tcp", AddressField: "localhost:2345", } reply := &Response{ NodeID: nodeID, Addr: *addr, AlternateNodeID: nodeID + 1, AlternateAddr: &newAddr, } if err := client.handleResponse( context.TODO(), local, reply, ); !testutils.IsError(err, "received forward") { t.Fatal(err) } if !proto.Equal(client.forwardAddr, &newAddr) { t.Fatalf("unexpected forward address %v, expected %v", client.forwardAddr, &newAddr) } }
func TestTxnAbortCount(t *testing.T) { defer leaktest.AfterTest(t)() _, sender, cleanupFn := setupMetricsTest(t) defer cleanupFn() value := []byte("value") db := client.NewDB(sender) intentionalErrText := "intentional error to cause abort" // Test aborted transaction. if err := db.Txn(context.TODO(), func(txn *client.Txn) error { key := []byte("key-abort") if err := txn.SetIsolation(enginepb.SNAPSHOT); err != nil { return err } if err := txn.Put(key, value); err != nil { t.Fatal(err) } return errors.New(intentionalErrText) }); !testutils.IsError(err, intentionalErrText) { t.Fatalf("unexpected error: %v", err) } teardownHeartbeats(sender) checkTxnMetrics(t, sender, "abort txn", 0, 0, 0, 1, 0) }
func TestScanError(t *testing.T) { testData := []struct { sql string err string }{ {`1e`, "invalid floating point literal"}, {`1e-`, "invalid floating point literal"}, {`1e+`, "invalid floating point literal"}, {`0x`, "invalid hexadecimal numeric literal"}, {`1x`, "invalid hexadecimal numeric literal"}, {`1.x`, "invalid hexadecimal numeric literal"}, {`1.0x`, "invalid hexadecimal numeric literal"}, {`0x0x`, "invalid hexadecimal numeric literal"}, {`00x0x`, "invalid hexadecimal numeric literal"}, {`08`, "could not make constant int from literal \"08\""}, {`x'zzz'`, "invalid hexadecimal string literal"}, {`X'zzz'`, "invalid hexadecimal string literal"}, {`x'beef\x41'`, "invalid hexadecimal string literal"}, {`X'beef\x41\x41'`, "invalid hexadecimal string literal"}, {`x'''1'''`, "invalid hexadecimal string literal"}, {`$9223372036854775809`, "integer value out of range"}, } for _, d := range testData { s := MakeScanner(d.sql, Traditional) var lval sqlSymType id := s.Lex(&lval) if id != ERROR { t.Errorf("%s: expected ERROR, but found %d", d.sql, id) } if !testutils.IsError(errors.New(lval.str), d.err) { t.Errorf("%s: expected %s, but found %v", d.sql, d.err, lval.str) } } }
func TestAcquireAndRelease(t *testing.T) { defer leaktest.AfterTest(t)() s, db := setup(t) defer s.Stopper().Stop() ctx := context.Background() manual := hlc.NewManualClock(123) clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) lm := client.NewLeaseManager(db, clock, client.LeaseManagerOptions{ClientID: clientID1}) l, err := lm.AcquireLease(ctx, leaseKey) if err != nil { t.Fatal(err) } if err := lm.ReleaseLease(ctx, l); err != nil { t.Fatal(err) } if err := lm.ReleaseLease(ctx, l); !testutils.IsError(err, "unexpected value") { t.Fatal(err) } l, err = lm.AcquireLease(ctx, leaseKey) if err != nil { t.Fatal(err) } if err := lm.ReleaseLease(ctx, l); err != nil { t.Fatal(err) } }
func TestDropTableInTxn(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, _ := serverutils.StartServer(t, params) defer s.Stopper().Stop() if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.kv (k CHAR PRIMARY KEY, v CHAR); `); err != nil { t.Fatal(err) } tx, err := sqlDB.Begin() if err != nil { t.Fatal(err) } if _, err := tx.Exec(`DROP TABLE t.kv`); err != nil { t.Fatal(err) } // We might still be able to read/write in the table inside this transaction // until the schema changer runs, but we shouldn't be able to ALTER it. if _, err := tx.Exec(`ALTER TABLE t.kv ADD COLUMN w CHAR`); !testutils.IsError(err, `table "kv" is being dropped`) { t.Fatalf("different error than expected: %v", err) } // Can't commit after ALTER errored, so we ROLLBACK. if err := tx.Rollback(); err != nil { t.Fatal(err) } }
// TestDropTableInterleaved tests dropping a table that is interleaved within // another table. func TestDropTableInterleaved(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() numRows := 2*sql.TableTruncateChunkSize + 1 createKVInterleavedTable(t, sqlDB, numRows) tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "kv") tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) checkKeyCount(t, kvDB, tablePrefix, 3*numRows) if _, err := sqlDB.Exec(`DROP TABLE t.intlv`); err != nil { t.Fatal(err) } checkKeyCount(t, kvDB, tablePrefix, numRows) // Test that deleted table cannot be used. This prevents regressions where // name -> descriptor ID caches might make this statement erronously work. if _, err := sqlDB.Exec(`SELECT * FROM t.intlv`); !testutils.IsError( err, `table "t.intlv" does not exist`, ) { t.Fatalf("different error than expected: %v", err) } }
// verifyError checks that either no error was found where none was // expected, or that an error was found when one was expected. Returns // "true" to indicate the behavior was as expected. func (t *logicTest) verifyError(sql, pos, expectErr, expectErrCode string, err error) bool { if expectErr == "" && expectErrCode == "" && err != nil { return t.unexpectedError(sql, pos, err) } if expectErr != "" && !testutils.IsError(err, expectErr) { t.Errorf("%s: expected %q, but found %v", pos, expectErr, err) return false } if expectErrCode != "" { if err != nil { pqErr, ok := err.(*pq.Error) if !ok { t.Errorf("%s: expected error code %q, but the error we found is not "+ "a libpq error: %s", pos, expectErrCode, err) return false } if pqErr.Code != pq.ErrorCode(expectErrCode) { t.Errorf("%s: expected error code %q, but found code %q (%s)", pos, expectErrCode, pqErr.Code, pqErr.Code.Name()) return false } } else { t.Errorf("%s: expected error code %q, but found success", pos, expectErrCode) return false } } return true }
// TestClientRunTransaction verifies some simple transaction isolation // semantics. func TestClientRunTransaction(t *testing.T) { defer leaktest.AfterTest(t)() s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() dbCtx := client.DefaultDBContext() dbCtx.TxnRetryOptions.InitialBackoff = 1 * time.Millisecond db := createTestClientForUser(t, s, security.NodeUser, dbCtx) for _, commit := range []bool{true, false} { value := []byte("value") key := []byte(fmt.Sprintf("%s/key-%t", testUser, commit)) // Use snapshot isolation so non-transactional read can always push. err := db.Txn(context.TODO(), func(txn *client.Txn) error { if err := txn.SetIsolation(enginepb.SNAPSHOT); err != nil { return err } // Put transactional value. if err := txn.Put(key, value); err != nil { return err } // Attempt to read outside of txn. if gr, err := db.Get(context.TODO(), key); err != nil { return err } else if gr.Value != nil { return errors.Errorf("expected nil value; got %+v", gr.Value) } // Read within the transaction. if gr, err := txn.Get(key); err != nil { return err } else if gr.Value == nil || !bytes.Equal(gr.ValueBytes(), value) { return errors.Errorf("expected value %q; got %q", value, gr.ValueBytes()) } if !commit { return errors.Errorf("purposefully failing transaction") } return nil }) if commit != (err == nil) { t.Errorf("expected success? %t; got %s", commit, err) } else if !commit && !testutils.IsError(err, "purposefully failing transaction") { t.Errorf("unexpected failure with !commit: %v", err) } // Verify the value is now visible on commit == true, and not visible otherwise. gr, err := db.Get(context.TODO(), key) if commit { if err != nil || gr.Value == nil || !bytes.Equal(gr.ValueBytes(), value) { t.Errorf("expected success reading value: %+v, %s", gr.Value, err) } } else { if err != nil || gr.Value != nil { t.Errorf("expected success and nil value: %+v, %s", gr.Value, err) } } } }
// TestTxnDBBasics verifies that a simple transaction can be run and // either committed or aborted. On commit, mutations are visible; on // abort, mutations are never visible. During the txn, verify that // uncommitted writes cannot be read outside of the txn but can be // read from inside the txn. func TestTxnDBBasics(t *testing.T) { defer leaktest.AfterTest(t)() s, _ := createTestDB(t) defer s.Stop() value := []byte("value") for _, commit := range []bool{true, false} { key := []byte(fmt.Sprintf("key-%t", commit)) err := s.DB.Txn(context.TODO(), func(txn *client.Txn) error { // Use snapshot isolation so non-transactional read can always push. if err := txn.SetIsolation(enginepb.SNAPSHOT); err != nil { return err } // Put transactional value. if err := txn.Put(key, value); err != nil { return err } // Attempt to read outside of txn. if gr, err := s.DB.Get(context.TODO(), key); err != nil { return err } else if gr.Exists() { return errors.Errorf("expected nil value; got %v", gr.Value) } // Read within the transaction. if gr, err := txn.Get(key); err != nil { return err } else if !gr.Exists() || !bytes.Equal(gr.ValueBytes(), value) { return errors.Errorf("expected value %q; got %q", value, gr.Value) } if !commit { return errors.Errorf("purposefully failing transaction") } return nil }) if commit != (err == nil) { t.Errorf("expected success? %t; got %s", commit, err) } else if !commit && !testutils.IsError(err, "purposefully failing transaction") { t.Errorf("unexpected failure with !commit: %v", err) } // Verify the value is now visible on commit == true, and not visible otherwise. gr, err := s.DB.Get(context.TODO(), key) if commit { if err != nil || !gr.Exists() || !bytes.Equal(gr.ValueBytes(), value) { t.Errorf("expected success reading value: %+v, %s", gr.ValueBytes(), err) } } else { if err != nil || gr.Exists() { t.Errorf("expected success and nil value: %s, %s", gr, err) } } } }
// TestClientPermissions verifies permission enforcement. func TestClientPermissions(t *testing.T) { defer leaktest.AfterTest(t)() s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() // NodeUser certs are required for all KV operations. // RootUser has no KV privileges whatsoever. nodeClient := createTestClientForUser(t, s.Stopper(), s.ServingAddr(), security.NodeUser, client.DefaultDBContext()) rootClient := createTestClientForUser(t, s.Stopper(), s.ServingAddr(), security.RootUser, client.DefaultDBContext()) testCases := []struct { path string client *client.DB allowed bool }{ {"foo", rootClient, false}, {"foo", nodeClient, true}, {testUser + "/foo", rootClient, false}, {testUser + "/foo", nodeClient, true}, {testUser + "foo", rootClient, false}, {testUser + "foo", nodeClient, true}, {testUser, rootClient, false}, {testUser, nodeClient, true}, {"unknown/foo", rootClient, false}, {"unknown/foo", nodeClient, true}, } value := []byte("value") const matchErr = "is not allowed" for tcNum, tc := range testCases { err := tc.client.Put(context.TODO(), tc.path, value) if (err == nil) != tc.allowed || (!tc.allowed && !testutils.IsError(err, matchErr)) { t.Errorf("#%d: expected allowed=%t, got err=%v", tcNum, tc.allowed, err) } _, err = tc.client.Get(context.TODO(), tc.path) if (err == nil) != tc.allowed || (!tc.allowed && !testutils.IsError(err, matchErr)) { t.Errorf("#%d: expected allowed=%t, got err=%v", tcNum, tc.allowed, err) } } }
func TestLeasesMultipleClients(t *testing.T) { defer leaktest.AfterTest(t)() s, db := setup(t) defer s.Stopper().Stop() ctx := context.Background() manual1 := hlc.NewManualClock(123) clock1 := hlc.NewClock(manual1.UnixNano, time.Nanosecond) manual2 := hlc.NewManualClock(123) clock2 := hlc.NewClock(manual2.UnixNano, time.Nanosecond) lm1 := client.NewLeaseManager(db, clock1, client.LeaseManagerOptions{ClientID: clientID1}) lm2 := client.NewLeaseManager(db, clock2, client.LeaseManagerOptions{ClientID: clientID2}) l1, err := lm1.AcquireLease(ctx, leaseKey) if err != nil { t.Fatal(err) } _, err = lm2.AcquireLease(ctx, leaseKey) if !testutils.IsError(err, "is not available until") { t.Fatalf("didn't get expected error trying to acquire already held lease: %v", err) } if _, ok := err.(*client.LeaseNotAvailableError); !ok { t.Fatalf("expected LeaseNotAvailableError, got %v", err) } // Ensure a lease can be "stolen" after it's expired. manual2.Increment(int64(client.DefaultLeaseDuration) + 1) l2, err := lm2.AcquireLease(ctx, leaseKey) if err != nil { t.Fatal(err) } // lm1's clock indicates that its lease should still be valid, but it doesn't // own it anymore. manual1.Increment(int64(client.DefaultLeaseDuration) / 2) if err := lm1.ExtendLease(ctx, l1); !testutils.IsError(err, "out of sync with DB state") { t.Fatalf("didn't get expected error trying to extend expired lease: %v", err) } if err := lm1.ReleaseLease(ctx, l1); !testutils.IsError(err, "unexpected value") { t.Fatalf("didn't get expected error trying to release stolen lease: %v", err) } if err := lm2.ReleaseLease(ctx, l2); err != nil { t.Fatal(err) } }
func TestAbortCountConflictingWrites(t *testing.T) { defer leaktest.AfterTest(t)() params, cmdFilters := createTestServerParams() s, sqlDB, _ := serverutils.StartServer(t, params) defer s.Stopper().Stop() if _, err := sqlDB.Exec("CREATE DATABASE db"); err != nil { t.Fatal(err) } if _, err := sqlDB.Exec("CREATE TABLE db.t (k TEXT PRIMARY KEY, v TEXT)"); err != nil { t.Fatal(err) } // Inject errors on the INSERT below. restarted := false cmdFilters.AppendFilter(func(args storagebase.FilterArgs) *roachpb.Error { switch req := args.Req.(type) { // SQL INSERT generates ConditionalPuts for unique indexes (such as the PK). case *roachpb.ConditionalPutRequest: if bytes.Contains(req.Value.RawBytes, []byte("marker")) && !restarted { restarted = true return roachpb.NewErrorWithTxn( roachpb.NewTransactionAbortedError(), args.Hdr.Txn) } } return nil }, false) txn, err := sqlDB.Begin() if err != nil { t.Fatal(err) } _, err = txn.Exec("INSERT INTO db.t VALUES ('key', 'marker')") if !testutils.IsError(err, "aborted") { t.Fatal(err) } if err = txn.Rollback(); err != nil { t.Fatal(err) } if err := checkCounterEQ(s, sql.MetaTxnAbort, 1); err != nil { t.Error(err) } if err := checkCounterEQ(s, sql.MetaTxnBegin, 1); err != nil { t.Error(err) } if err := checkCounterEQ(s, sql.MetaTxnRollback, 0); err != nil { t.Error(err) } if err := checkCounterEQ(s, sql.MetaTxnCommit, 0); err != nil { t.Error(err) } if err := checkCounterEQ(s, sql.MetaInsert, 1); err != nil { t.Error(err) } }
func TestAdminAPITableDoesNotExist(t *testing.T) { defer leaktest.AfterTest(t)() s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() const fakename = "I_DO_NOT_EXIST" const badDBPath = "databases/" + fakename + "/tables/foo" const dbErrPattern = `database \\"` + fakename + `\\" does not exist` if err := getAdminJSONProto(s, badDBPath, nil); !testutils.IsError(err, dbErrPattern) { t.Fatalf("unexpected error: %v\nexpected: %s", err, dbErrPattern) } const badTablePath = "databases/system/tables/" + fakename const tableErrPattern = `table \\"system.` + fakename + `\\" does not exist` if err := getAdminJSONProto(s, badTablePath, nil); !testutils.IsError(err, tableErrPattern) { t.Fatalf("unexpected error: %v\nexpected: %s", err, tableErrPattern) } }
func TestNormalizeNameInExpr(t *testing.T) { testCases := []struct { in, out string err string }{ {`foo`, `foo`, ``}, {`*`, `*`, ``}, {`foo.bar`, `foo.bar`, ``}, {`foo.*`, `foo.*`, ``}, {`test.foo.*`, `test.foo.*`, ``}, {`foo.bar[blah]`, `foo.bar`, ``}, {`foo[bar]`, `foo`, ``}, {`test.*[foo]`, `test.*`, ``}, {`"".foo`, ``, `empty table name`}, {`"".*`, ``, `empty table name`}, {`""`, ``, `empty column name`}, {`foo.*.bar`, ``, `invalid table name: "foo.*"`}, {`foo.*.bar[baz]`, ``, `invalid table name: "foo.*"`}, {`test.foo.*.bar[foo]`, ``, `invalid table name: "test.foo.*"`}, } for _, tc := range testCases { stmt, err := ParseOneTraditional("SELECT " + tc.in) if err != nil { t.Fatalf("%s: %v", tc.in, err) } var vBase VarName startExpr := stmt.(*Select).Select.(*SelectClause).Exprs[0].Expr for { switch e := startExpr.(type) { case VarName: vBase = e case *IndirectionExpr: startExpr = e.Expr continue default: t.Fatalf("%s does not parse to a VarName or IndirectionExpr", tc.in) } break } v, err := vBase.NormalizeVarName() if tc.err != "" { if !testutils.IsError(err, tc.err) { t.Fatalf("%s: expected %s, but found %v", tc.in, tc.err, err) } continue } if err != nil { t.Fatalf("%s: expected success, but found %v", tc.in, err) } if out := v.String(); tc.out != out { t.Errorf("%s: expected %s, but found %s", tc.in, tc.out, out) } } }