func (t *parallelTest) setup(spec *parTestSpec) { if spec.ClusterSize == 0 { spec.ClusterSize = 1 } if testing.Verbose() || log.V(1) { log.Infof(t.ctx, "Cluster Size: %d", spec.ClusterSize) } args := base.TestClusterArgs{ ServerArgs: base.TestServerArgs{ Knobs: base.TestingKnobs{ SQLExecutor: &sql.ExecutorTestingKnobs{ WaitForGossipUpdate: true, CheckStmtStringChange: true, }, }, }, } t.cluster = serverutils.StartTestCluster(t, spec.ClusterSize, args) t.clients = make([][]*gosql.DB, spec.ClusterSize) for i := range t.clients { t.clients[i] = append(t.clients[i], t.cluster.ServerConn(i)) } r0 := sqlutils.MakeSQLRunner(t, t.clients[0][0]) if spec.RangeSplitSize != 0 { if testing.Verbose() || log.V(1) { log.Infof(t.ctx, "Setting range split size: %d", spec.RangeSplitSize) } zoneCfg := config.DefaultZoneConfig() zoneCfg.RangeMaxBytes = int64(spec.RangeSplitSize) zoneCfg.RangeMinBytes = zoneCfg.RangeMaxBytes / 2 buf, err := protoutil.Marshal(&zoneCfg) if err != nil { t.Fatal(err) } objID := keys.RootNamespaceID r0.Exec(`UPDATE system.zones SET config = $2 WHERE id = $1`, objID, buf) } if testing.Verbose() || log.V(1) { log.Infof(t.ctx, "Creating database") } r0.Exec("CREATE DATABASE test") for i := range t.clients { sqlutils.MakeSQLRunner(t, t.clients[i][0]).Exec("SET DATABASE = test") } if testing.Verbose() || log.V(1) { log.Infof(t.ctx, "Test setup done") } }
func TestSplitAtTableBoundary(t *testing.T) { defer leaktest.AfterTest(t)() testClusterArgs := base.TestClusterArgs{ ReplicationMode: base.ReplicationAuto, } tc := testcluster.StartTestCluster(t, 3, testClusterArgs) defer tc.Stopper().Stop() runner := sqlutils.MakeSQLRunner(t, tc.Conns[0]) runner.Exec(`CREATE DATABASE test`) runner.Exec(`CREATE TABLE test.t (k SERIAL PRIMARY KEY, v INT)`) const tableIDQuery = ` SELECT tables.id FROM system.namespace tables JOIN system.namespace dbs ON dbs.id = tables.parentid WHERE dbs.name = $1 AND tables.name = $2 ` var tableID uint32 runner.QueryRow(tableIDQuery, "test", "t").Scan(&tableID) tableStartKey := keys.MakeTablePrefix(tableID) // Wait for new table to split. testutils.SucceedsSoon(t, func() error { desc, err := tc.LookupRange(keys.MakeRowSentinelKey(tableStartKey)) if err != nil { t.Fatal(err) } if !desc.StartKey.Equal(tableStartKey) { log.Infof(context.TODO(), "waiting on split results") return errors.Errorf("expected range start key %s; got %s", tableStartKey, desc.StartKey) } return nil }) }
func makeMutationTest( t *testing.T, kvDB *client.DB, db *gosql.DB, tableDesc *sqlbase.TableDescriptor, ) mutationTest { return mutationTest{ SQLRunner: sqlutils.MakeSQLRunner(t, db), kvDB: kvDB, tableDesc: tableDesc, } }
func backupRestoreTestSetup( t testing.TB, numAccounts int, ) ( ctx context.Context, tempDir string, tc *testcluster.TestCluster, kvDB *client.DB, sqlDB *sqlutils.SQLRunner, cleanup func(), ) { ctx = context.Background() dir, dirCleanupFn := testutils.TempDir(t, 1) // Use ReplicationManual so we can force full replication, which is needed // to later move the leases around. tc = testcluster.StartTestCluster(t, backupRestoreClusterSize, base.TestClusterArgs{ ReplicationMode: base.ReplicationManual, }) sqlDB = sqlutils.MakeSQLRunner(t, tc.Conns[0]) kvDB = tc.Server(0).KVClient().(*client.DB) sqlDB.Exec(bankCreateDatabase) sqlDB.Exec(bankCreateTable) for _, insert := range bankDataInsertStmts(numAccounts) { sqlDB.Exec(insert) } for _, split := range bankSplitStmts(numAccounts, backupRestoreDefaultRanges) { sqlDB.Exec(split) } targets := make([]testcluster.ReplicationTarget, backupRestoreClusterSize-1) for i := 1; i < backupRestoreClusterSize; i++ { targets[i-1] = tc.Target(i) } txn := client.NewTxn(ctx, *kvDB) rangeDescs, err := sql.AllRangeDescriptors(txn) if err != nil { t.Fatal(err) } for _, r := range rangeDescs { if _, err := tc.AddReplicas(r.StartKey.AsRawKey(), targets...); err != nil { t.Fatal(err) } } cleanupFn := func() { tc.Stopper().Stop() dirCleanupFn() } return ctx, dir, tc, kvDB, sqlDB, cleanupFn }
func TestBackupRestoreOnce(t *testing.T) { defer leaktest.AfterTest(t)() // TODO(dan): Actually invalidate the descriptor cache and delete this line. defer sql.TestDisableTableLeases()() const numAccounts = 1000 ctx, dir, tc, kvDB, _, cleanupFn := backupRestoreTestSetup(t, numAccounts) defer cleanupFn() { desc, err := sql.Backup(ctx, *kvDB, dir, tc.Server(0).Clock().Now()) if err != nil { t.Fatal(err) } approxDataSize := int64(backupRestoreRowPayloadSize) * numAccounts if max := approxDataSize * 2; desc.DataSize < approxDataSize || desc.DataSize > 2*max { t.Errorf("expected data size in [%d,%d] but was %d", approxDataSize, max, desc.DataSize) } } // Start a new cluster to restore into. { tcRestore := testcluster.StartTestCluster(t, backupRestoreClusterSize, base.TestClusterArgs{}) defer tcRestore.Stopper().Stop() sqlDBRestore := sqlutils.MakeSQLRunner(t, tcRestore.Conns[0]) kvDBRestore := tcRestore.Server(0).KVClient().(*client.DB) // Restore assumes the database exists. sqlDBRestore.Exec(bankCreateDatabase) table := parser.TableName{DatabaseName: "bench", TableName: "bank"} if _, err := sql.Restore(ctx, *kvDBRestore, dir, table); err != nil { t.Fatal(err) } var rowCount int sqlDBRestore.QueryRow(`SELECT COUNT(*) FROM bench.bank`).Scan(&rowCount) if rowCount != numAccounts { t.Fatalf("expected %d rows but found %d", numAccounts, rowCount) } } }
func (t *parallelTest) getClient(nodeIdx, clientIdx int) *gosql.DB { for len(t.clients[nodeIdx]) <= clientIdx { // Add a client. pgURL, cleanupFunc := sqlutils.PGUrl(t.T, t.cluster.Server(nodeIdx).ServingAddr(), "TestParallel", url.User(security.RootUser)) db, err := gosql.Open("postgres", pgURL.String()) if err != nil { t.Fatal(err) } sqlutils.MakeSQLRunner(t, db).Exec("SET DATABASE = test") t.cluster.Stopper().AddCloser( stop.CloserFn(func() { _ = db.Close() cleanupFunc() })) t.clients[nodeIdx] = append(t.clients[nodeIdx], db) } return t.clients[nodeIdx][clientIdx] }
func TestDistSQLPlanner(t *testing.T) { defer leaktest.AfterTest(t)() args := base.TestClusterArgs{ReplicationMode: base.ReplicationManual} tc := serverutils.StartTestCluster(t, 1, args) defer tc.Stopper().Stop() sqlutils.CreateTable( t, tc.ServerConn(0), "t", "num INT PRIMARY KEY, str STRING, mod INT, INDEX(mod)", 10, sqlutils.ToRowFn(sqlutils.RowIdxFn, sqlutils.RowEnglishFn, sqlutils.RowModuloFn(3)), ) r := sqlutils.MakeSQLRunner(t, tc.ServerConn(0)) r.DB.SetMaxOpenConns(1) r.Exec("SET DIST_SQL = ALWAYS") r.CheckQueryResults( "SELECT 5, 2 + num, * FROM test.t ORDER BY str", [][]string{ strings.Fields("5 10 8 eight 2"), strings.Fields("5 7 5 five 2"), strings.Fields("5 6 4 four 1"), strings.Fields("5 11 9 nine 0"), strings.Fields("5 3 1 one 1"), strings.Fields("5 12 10 one-zero 1"), strings.Fields("5 9 7 seven 1"), strings.Fields("5 8 6 six 0"), strings.Fields("5 5 3 three 0"), strings.Fields("5 4 2 two 2"), }, ) r.CheckQueryResults( "SELECT str FROM test.t WHERE mod=0", [][]string{ {"three"}, {"six"}, {"nine"}, }, ) }
func TestSplitAt(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, db, _ := serverutils.StartServer(t, params) defer s.Stopper().Stop() r := sqlutils.MakeSQLRunner(t, db) r.Exec("CREATE DATABASE d") r.Exec(`CREATE TABLE d.t ( i INT, s STRING, PRIMARY KEY (i, s), INDEX s_idx (s) )`) r.Exec(`CREATE TABLE d.i (k INT PRIMARY KEY)`) tests := []struct { in string error string args []interface{} }{ { in: "ALTER TABLE d.t SPLIT AT (2, 'b')", }, { in: "ALTER TABLE d.t SPLIT AT (2, 'b')", error: "range is already split", }, { in: "ALTER TABLE d.t SPLIT AT ('c', 3)", error: "argument of SPLIT AT must be type int, not type string", }, { in: "ALTER TABLE d.t SPLIT AT (4)", error: "expected 2 expressions, got 1", }, { in: "ALTER TABLE d.t SPLIT AT (5, 'e')", }, { in: "ALTER TABLE d.t SPLIT AT (i, s)", error: `name "i" is not defined`, }, { in: "ALTER INDEX d.t@s_idx SPLIT AT ('f')", }, { in: "ALTER INDEX d.t@not_present SPLIT AT ('g')", error: `index "not_present" does not exist`, }, { in: "ALTER TABLE d.i SPLIT AT (avg(1))", error: "unknown signature: avg(int) (desired <int>)", }, { in: "ALTER TABLE d.i SPLIT AT (avg(k))", error: `avg(): name "k" is not defined`, }, { in: "ALTER TABLE d.i SPLIT AT ($1)", args: []interface{}{8}, }, { in: "ALTER TABLE d.i SPLIT AT ($1)", error: "no value provided for placeholder: $1", }, { in: "ALTER TABLE d.i SPLIT AT ($1)", args: []interface{}{"blah"}, error: "error in argument for $1: strconv.ParseInt", }, { in: "ALTER TABLE d.i SPLIT AT ($1::string)", args: []interface{}{"1"}, error: "argument of SPLIT AT must be type int, not type string", }, { in: "ALTER TABLE d.i SPLIT AT ((SELECT 1))", }, { in: "ALTER TABLE d.i SPLIT AT ((SELECT 1, 2))", error: "subquery must return only one column, found 2", }, } for _, tt := range tests { var key roachpb.Key var pretty string err := db.QueryRow(tt.in, tt.args...).Scan(&key, &pretty) if err != nil && tt.error == "" { t.Fatalf("%s: unexpected error: %s", tt.in, err) } else if tt.error != "" && err == nil { t.Fatalf("%s: expected error: %s", tt.in, tt.error) } else if err != nil && tt.error != "" { if !strings.Contains(err.Error(), tt.error) { t.Fatalf("%s: unexpected error: %s", tt.in, err) } } else { // Successful split, verify it happened. rng, err := s.(*server.TestServer).LookupRange(key) if err != nil { t.Fatal(err) } expect := roachpb.Key(keys.MakeRowSentinelKey(rng.StartKey)) if !expect.Equal(key) { t.Fatalf("%s: expected range start %s, got %s", tt.in, pretty, expect) } } } }
// TestAmbiguousCommitDueToLeadershipChange verifies that an ambiguous // commit error is returned from sql.Exec in situations where an // EndTransaction is part of a batch and the disposition of the batch // request is unknown after a network failure or timeout. The goal // here is to prevent spurious transaction retries after the initial // transaction actually succeeded. In cases where there's an // auto-generated primary key, this can result in silent // duplications. In cases where the primary key is specified in // advance, it can result in violated uniqueness constraints, or // duplicate key violations. See #6053, #7604, and #10023. func TestAmbiguousCommitDueToLeadershipChange(t *testing.T) { defer leaktest.AfterTest(t)() t.Skip("#10341") // Create a command filter which prevents EndTransaction from // returning a response. params := base.TestServerArgs{} committed := make(chan struct{}) wait := make(chan struct{}) var tableStartKey atomic.Value var responseCount int32 // Prevent the first conditional put on table 51 from returning to // waiting client in order to simulate a lost update or slow network // link. params.Knobs.Store = &storage.StoreTestingKnobs{ TestingResponseFilter: func(ba roachpb.BatchRequest, br *roachpb.BatchResponse) *roachpb.Error { req, ok := ba.GetArg(roachpb.ConditionalPut) tsk := tableStartKey.Load() if tsk == nil { return nil } if !ok || !bytes.HasPrefix(req.Header().Key, tsk.([]byte)) { return nil } // If this is the first write to the table, wait to respond to the // client in order to simulate a retry. if atomic.AddInt32(&responseCount, 1) == 1 { close(committed) <-wait } return nil }, } testClusterArgs := base.TestClusterArgs{ ReplicationMode: base.ReplicationAuto, ServerArgs: params, } const numReplicas = 3 tc := testcluster.StartTestCluster(t, numReplicas, testClusterArgs) defer tc.Stopper().Stop() sqlDB := sqlutils.MakeSQLRunner(t, tc.Conns[0]) sqlDB.Exec(`CREATE DATABASE test`) sqlDB.Exec(`CREATE TABLE test.t (k SERIAL PRIMARY KEY, v INT)`) tableID := sqlutils.QueryTableID(t, tc.Conns[0], "test", "t") tableStartKey.Store(keys.MakeTablePrefix(tableID)) // Wait for new table to split. util.SucceedsSoon(t, func() error { startKey := tableStartKey.Load().([]byte) desc, err := tc.LookupRange(keys.MakeRowSentinelKey(startKey)) if err != nil { t.Fatal(err) } if !desc.StartKey.Equal(startKey) { return errors.Errorf("expected range start key %s; got %s", startKey, desc.StartKey) } return nil }) // Lookup the lease. tableRangeDesc, err := tc.LookupRange(keys.MakeRowSentinelKey(tableStartKey.Load().([]byte))) if err != nil { t.Fatal(err) } leaseHolder, err := tc.FindRangeLeaseHolder( &tableRangeDesc, &testcluster.ReplicationTarget{ NodeID: tc.Servers[0].GetNode().Descriptor.NodeID, StoreID: tc.Servers[0].GetFirstStoreID(), }) if err != nil { t.Fatal(err) } // In a goroutine, send an insert which will commit but not return // from the leader (due to the command filter we installed on node 0). sqlErrCh := make(chan error, 1) go func() { // Use a connection other than through the node which is the current // leaseholder to ensure that we use GRPC instead of the local server. // If we use a local server, the hanging response we simulate takes // up the dist sender thread of execution because local requests are // executed synchronously. sqlConn := tc.Conns[leaseHolder.NodeID%numReplicas] _, err := sqlConn.Exec(`INSERT INTO test.t (v) VALUES (1)`) sqlErrCh <- err close(wait) }() // Wait until the insert has committed. <-committed // Find a node other than the current lease holder to transfer the lease to. for i, s := range tc.Servers { if leaseHolder.StoreID != s.GetFirstStoreID() { if err := tc.TransferRangeLease(&tableRangeDesc, tc.Target(i)); err != nil { t.Fatal(err) } break } } // Wait for the error from the pending SQL insert. if err := <-sqlErrCh; !testutils.IsError(err, "result is ambiguous") { t.Errorf("expected ambiguous commit error; got %v", err) } // Verify a single row exists in the table. var rowCount int sqlDB.QueryRow(`SELECT COUNT(*) FROM test.t`).Scan(&rowCount) if rowCount != 1 { t.Errorf("expected 1 row but found %d", rowCount) } }
func TestManualReplication(t *testing.T) { defer leaktest.AfterTest(t)() tc := StartTestCluster(t, 3, base.TestClusterArgs{ ReplicationMode: base.ReplicationManual, ServerArgs: base.TestServerArgs{ UseDatabase: "t", }, }) defer tc.Stopper().Stop() s0 := sqlutils.MakeSQLRunner(t, tc.Conns[0]) s1 := sqlutils.MakeSQLRunner(t, tc.Conns[1]) s2 := sqlutils.MakeSQLRunner(t, tc.Conns[2]) s0.Exec(`CREATE DATABASE t`) s0.Exec(`CREATE TABLE test (k INT PRIMARY KEY, v INT)`) s0.Exec(`INSERT INTO test VALUES (5, 1), (4, 2), (1, 2)`) if r := s1.Query(`SELECT * FROM test WHERE k = 5`); !r.Next() { t.Fatal("no rows") } s2.ExecRowsAffected(3, `DELETE FROM test`) // Split the table to a new range. kvDB := tc.Servers[0].DB() tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") tableStartKey := keys.MakeRowSentinelKey(keys.MakeTablePrefix(uint32(tableDesc.ID))) leftRangeDesc, tableRangeDesc, err := tc.SplitRange(tableStartKey) if err != nil { t.Fatal(err) } log.Infof(context.Background(), "After split got ranges: %+v and %+v.", leftRangeDesc, tableRangeDesc) if len(tableRangeDesc.Replicas) == 0 { t.Fatalf( "expected replica on node 1, got no replicas: %+v", tableRangeDesc.Replicas) } if tableRangeDesc.Replicas[0].NodeID != 1 { t.Fatalf( "expected replica on node 1, got replicas: %+v", tableRangeDesc.Replicas) } // Replicate the table's range to all the nodes. tableRangeDesc, err = tc.AddReplicas( tableRangeDesc.StartKey.AsRawKey(), tc.Target(1), tc.Target(2), ) if err != nil { t.Fatal(err) } if len(tableRangeDesc.Replicas) != 3 { t.Fatalf("expected 3 replicas, got %+v", tableRangeDesc.Replicas) } for i := 0; i < 3; i++ { if _, ok := tableRangeDesc.GetReplicaDescriptor( tc.Servers[i].GetFirstStoreID()); !ok { t.Fatalf("expected replica on store %d, got %+v", tc.Servers[i].GetFirstStoreID(), tableRangeDesc.Replicas) } } // Transfer the lease to node 1. leaseHolder, err := tc.FindRangeLeaseHolder( tableRangeDesc, &ReplicationTarget{ NodeID: tc.Servers[0].GetNode().Descriptor.NodeID, StoreID: tc.Servers[0].GetFirstStoreID(), }) if err != nil { t.Fatal(err) } if leaseHolder.StoreID != tc.Servers[0].GetFirstStoreID() { t.Fatalf("expected initial lease on server idx 0, but is on node: %+v", leaseHolder) } err = tc.TransferRangeLease(tableRangeDesc, tc.Target(1)) if err != nil { t.Fatal(err) } // Check that the lease holder has changed. We'll use the old lease holder as // the hint, since it's guaranteed that the old lease holder has applied the // new lease. leaseHolder, err = tc.FindRangeLeaseHolder( tableRangeDesc, &ReplicationTarget{ NodeID: tc.Servers[0].GetNode().Descriptor.NodeID, StoreID: tc.Servers[0].GetFirstStoreID(), }) if err != nil { t.Fatal(err) } if leaseHolder.StoreID != tc.Servers[1].GetFirstStoreID() { t.Fatalf("expected lease on server idx 1 (node: %d store: %d), but is on node: %+v", tc.Servers[1].GetNode().Descriptor.NodeID, tc.Servers[1].GetFirstStoreID(), leaseHolder) } }
func TestServer(t *testing.T) { defer leaktest.AfterTest(t)() s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() conn, err := s.RPCContext().GRPCDial(s.ServingAddr()) if err != nil { t.Fatal(err) } r := sqlutils.MakeSQLRunner(t, sqlDB) r.Exec(`CREATE DATABASE test`) r.Exec(`CREATE TABLE test.t (a INT PRIMARY KEY, b INT)`) r.Exec(`INSERT INTO test.t VALUES (1, 10), (2, 20), (3, 30)`) td := sqlbase.GetTableDescriptor(kvDB, "test", "t") ts := TableReaderSpec{ Table: *td, IndexIdx: 0, Reverse: false, Spans: nil, Filter: Expression{Expr: "$0 != 2"}, // a != 2 OutputColumns: []uint32{0, 1}, // a } txn := client.NewTxn(context.Background(), *kvDB) req := &SetupFlowRequest{Txn: txn.Proto} req.Flow = FlowSpec{ Processors: []ProcessorSpec{{ Core: ProcessorCoreUnion{TableReader: &ts}, Output: []OutputRouterSpec{{ Type: OutputRouterSpec_MIRROR, Streams: []StreamEndpointSpec{{Mailbox: &MailboxSpec{SimpleResponse: true}}}, }}, }}, } distSQLClient := NewDistSQLClient(conn) stream, err := distSQLClient.RunSimpleFlow(context.Background(), req) if err != nil { t.Fatal(err) } var decoder StreamDecoder var rows sqlbase.EncDatumRows for { msg, err := stream.Recv() if err != nil { if err == io.EOF { break } t.Fatal(err) } err = decoder.AddMessage(msg) if err != nil { t.Fatal(err) } rows = testGetDecodedRows(t, &decoder, rows) } if done, trailerErr := decoder.IsDone(); !done { t.Fatal("stream not done") } else if trailerErr != nil { t.Fatal("error in the stream trailer:", trailerErr) } str := rows.String() expected := "[[1 10] [3 30]]" if str != expected { t.Errorf("invalid results: %s, expected %s'", str, expected) } }
func TestDistSQLJoinAndAgg(t *testing.T) { defer leaktest.AfterTest(t)() // This test sets up a distributed join between two tables: // - a NumToSquare table of size N that maps integers from 1 to n to their // squares // - a NumToStr table of size N^2 that maps integers to their string // representations. This table is split and distributed to all the nodes. const n = 100 const numNodes = 5 tc := serverutils.StartTestCluster(t, numNodes, base.TestClusterArgs{ ReplicationMode: base.ReplicationManual, ServerArgs: base.TestServerArgs{ UseDatabase: "test", }, }) defer tc.Stopper().Stop() cdb := tc.Server(0).KVClient().(*client.DB) sqlutils.CreateTable( t, tc.ServerConn(0), "NumToSquare", "x INT PRIMARY KEY, xsquared INT", n, sqlutils.ToRowFn(sqlutils.RowIdxFn, func(row int) parser.Datum { return parser.NewDInt(parser.DInt(row * row)) }), ) sqlutils.CreateTable( t, tc.ServerConn(0), "NumToStr", "y INT PRIMARY KEY, str STRING", n*n, sqlutils.ToRowFn(sqlutils.RowIdxFn, sqlutils.RowEnglishFn), ) // Split the table into multiple ranges, with each range having a single // replica on a certain node. This forces the query to be distributed. // // TODO(radu): this approach should be generalized into test infrastructure // (perhaps by adding functionality to logic tests). // TODO(radu): we should verify that the plan is indeed distributed as // intended. descNumToStr := sqlbase.GetTableDescriptor(cdb, "test", "NumToStr") // split introduces a split and moves the right range to a given node. split := func(val int, targetNode int) { pik, err := sqlbase.MakePrimaryIndexKey(descNumToStr, val) if err != nil { t.Fatal(err) } splitKey := keys.MakeRowSentinelKey(pik) _, rightRange, err := tc.Server(0).SplitRange(splitKey) if err != nil { t.Fatal(err) } splitKey = rightRange.StartKey.AsRawKey() rightRange, err = tc.AddReplicas(splitKey, tc.Target(targetNode)) if err != nil { t.Fatal(err) } // This transfer is necessary to avoid waiting for the lease to expire when // removing the first replica. if err := tc.TransferRangeLease(rightRange, tc.Target(targetNode)); err != nil { t.Fatal(err) } if _, err := tc.RemoveReplicas(splitKey, tc.Target(0)); err != nil { t.Fatal(err) } } // split moves the right range, so we split things back to front. for i := numNodes - 1; i > 0; i-- { split(n*n/numNodes*i, i) } r := sqlutils.MakeSQLRunner(t, tc.ServerConn(0)) r.DB.SetMaxOpenConns(1) r.Exec("SET DIST_SQL = ALWAYS") res := r.QueryStr("SELECT x, str FROM NumToSquare JOIN NumToStr ON y = xsquared") // Verify that res contains one entry for each integer, with the string // representation of its square, e.g.: // [1, one] // [2, two] // [3, nine] // [4, one-six] // (but not necessarily in order). if len(res) != n { t.Fatalf("expected %d rows, got %d", n, len(res)) } resMap := make(map[int]string) for _, row := range res { if len(row) != 2 { t.Fatalf("invalid row %v", row) } n, err := strconv.Atoi(row[0]) if err != nil { t.Fatalf("error parsing row %v: %s", row, err) } resMap[n] = row[1] } for i := 1; i <= n; i++ { if resMap[i] != sqlutils.IntToEnglish(i*i) { t.Errorf("invalid string for %d: %s", i, resMap[i]) } } checkRes := func(exp int) bool { return len(res) == 1 && len(res[0]) == 1 && res[0][0] == strconv.Itoa(exp) } // Sum the numbers in the NumToStr table. res = r.QueryStr("SELECT SUM(y) FROM NumToStr") if exp := n * n * (n*n + 1) / 2; !checkRes(exp) { t.Errorf("expected [[%d]], got %s", exp, res) } // Count the rows in the NumToStr table. res = r.QueryStr("SELECT COUNT(*) FROM NumToStr") if !checkRes(n * n) { t.Errorf("expected [[%d]], got %s", n*n, res) } // Count how many numbers contain the digit 5. res = r.QueryStr("SELECT COUNT(*) FROM NumToStr WHERE str LIKE '%five%'") exp := 0 for i := 1; i <= n*n; i++ { for x := i; x > 0; x /= 10 { if x%10 == 5 { exp++ break } } } if !checkRes(exp) { t.Errorf("expected [[%d]], got %s", exp, res) } }