github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/unsplit_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package sql_test 12 13 import ( 14 "context" 15 gosql "database/sql" 16 "strings" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/base" 20 "github.com/cockroachdb/cockroach/pkg/kv/kvserver" 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/server" 23 "github.com/cockroachdb/cockroach/pkg/sql/tests" 24 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 25 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 26 "github.com/cockroachdb/cockroach/pkg/util/hlc" 27 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 28 ) 29 30 func TestUnsplitAt(t *testing.T) { 31 defer leaktest.AfterTest(t)() 32 33 params, _ := tests.CreateTestServerParams() 34 // TODO(jeffreyxiao): Disable the merge queue due to a race condition. The 35 // merge queue might issue an AdminMerge and before the actual merge happens, 36 // the LHS of the merge is manually split and is later merged even though a 37 // sticky bit has been set on the new RHS. This race condition happens 38 // because there is two independent fetches of the RHS during a merge 39 // operation (one in the merge queue and another in the actual merge). The 40 // merge queue should pass the expected descriptor of the RHS into the 41 // AdminMerge request. 42 params.Knobs = base.TestingKnobs{ 43 Store: &kvserver.StoreTestingKnobs{ 44 DisableMergeQueue: true, 45 }, 46 } 47 s, db, _ := serverutils.StartServer(t, params) 48 defer s.Stopper().Stop(context.Background()) 49 50 r := sqlutils.MakeSQLRunner(db) 51 52 r.Exec(t, "CREATE DATABASE d") 53 r.Exec(t, `CREATE TABLE d.t ( 54 i INT, 55 s STRING, 56 PRIMARY KEY (i, s), 57 INDEX s_idx (s) 58 )`) 59 r.Exec(t, `CREATE TABLE d.i (k INT PRIMARY KEY)`) 60 r.Exec(t, `CREATE TABLE i (k INT PRIMARY KEY)`) 61 62 tests := []struct { 63 splitStmt string 64 unsplitStmt string 65 // Number of unsplits expected. 66 count int 67 error string 68 args []interface{} 69 }{ 70 { 71 splitStmt: "ALTER TABLE d.t SPLIT AT VALUES (2, 'b')", 72 unsplitStmt: "ALTER TABLE d.t UNSPLIT AT VALUES (2, 'b')", 73 count: 1, 74 }, 75 { 76 splitStmt: "ALTER TABLE d.t SPLIT AT VALUES (3, 'c'), (4, 'd')", 77 unsplitStmt: "ALTER TABLE d.t UNSPLIT AT VALUES (3, 'c'), (4, 'd')", 78 count: 2, 79 }, 80 { 81 splitStmt: "ALTER TABLE d.t SPLIT AT VALUES (5, 'd')", 82 unsplitStmt: "ALTER TABLE d.t UNSPLIT AT SELECT 5, 'd'", 83 count: 1, 84 }, 85 { 86 splitStmt: "ALTER TABLE d.t SPLIT AT VALUES (6, 'e'), (7, 'f')", 87 unsplitStmt: "ALTER TABLE d.t UNSPLIT AT SELECT * FROM (VALUES (6, 'e'), (7, 'f')) AS a", 88 count: 2, 89 }, 90 { 91 splitStmt: "ALTER TABLE d.t SPLIT AT VALUES (10)", 92 unsplitStmt: "ALTER TABLE d.t UNSPLIT AT VALUES (10)", 93 count: 1, 94 }, 95 { 96 splitStmt: "ALTER TABLE d.i SPLIT AT VALUES (1)", 97 unsplitStmt: "ALTER TABLE d.i UNSPLIT AT VALUES ((SELECT 1))", 98 count: 1, 99 }, 100 { 101 splitStmt: "ALTER TABLE d.i SPLIT AT VALUES (8)", 102 unsplitStmt: "ALTER TABLE d.i UNSPLIT AT VALUES ($1)", 103 args: []interface{}{8}, 104 count: 1, 105 }, 106 { 107 splitStmt: "ALTER INDEX d.t@s_idx SPLIT AT VALUES ('f')", 108 unsplitStmt: "ALTER INDEX d.t@s_idx UNSPLIT AT VALUES ('f')", 109 count: 1, 110 }, 111 { 112 splitStmt: "ALTER TABLE d.t SPLIT AT VALUES (8, 'g'), (9, 'h'), (10, 'i')", 113 unsplitStmt: "ALTER TABLE d.t UNSPLIT ALL", 114 count: 3, 115 }, 116 { 117 splitStmt: "ALTER INDEX d.t@s_idx SPLIT AT VALUES ('g'), ('h'), ('i')", 118 unsplitStmt: "ALTER INDEX d.t@s_idx UNSPLIT ALL", 119 count: 3, 120 }, 121 { 122 splitStmt: "ALTER TABLE d.i SPLIT AT VALUES (10), (11), (12)", 123 unsplitStmt: "ALTER TABLE d.i UNSPLIT ALL", 124 count: 3, 125 }, 126 { 127 splitStmt: "ALTER TABLE i SPLIT AT VALUES (10), (11), (12)", 128 unsplitStmt: "ALTER TABLE i UNSPLIT ALL", 129 count: 3, 130 }, 131 { 132 unsplitStmt: "ALTER TABLE d.t UNSPLIT AT VALUES (1, 'non-existent')", 133 error: "could not UNSPLIT AT (1, 'non-existent')", 134 }, 135 { 136 unsplitStmt: "ALTER TABLE d.t UNSPLIT AT VALUES ('c', 3)", 137 error: "could not parse \"c\" as type int", 138 }, 139 { 140 unsplitStmt: "ALTER TABLE d.t UNSPLIT AT VALUES (i, s)", 141 error: `column "i" does not exist`, 142 }, 143 { 144 unsplitStmt: "ALTER INDEX d.t@not_present UNSPLIT AT VALUES ('g')", 145 error: `index "not_present" does not exist`, 146 }, 147 { 148 unsplitStmt: "ALTER TABLE d.i UNSPLIT AT VALUES (avg(1::float))", 149 error: "aggregate functions are not allowed in VALUES", 150 }, 151 { 152 unsplitStmt: "ALTER TABLE d.i UNSPLIT AT VALUES ($1)", 153 error: "no value provided for placeholder: $1", 154 }, 155 { 156 unsplitStmt: "ALTER TABLE d.i UNSPLIT AT VALUES ($1)", 157 args: []interface{}{"blah"}, 158 error: "error in argument for $1: strconv.ParseInt", 159 }, 160 { 161 unsplitStmt: "ALTER TABLE d.i UNSPLIT AT VALUES ($1::string)", 162 args: []interface{}{"1"}, 163 error: "UNSPLIT AT data column 1 (k) must be of type int, not type string", 164 }, 165 } 166 167 for _, tt := range tests { 168 var key roachpb.Key 169 var pretty string 170 var expirationTimestamp gosql.NullString 171 172 if tt.splitStmt != "" { 173 rows, err := db.Query(tt.splitStmt) 174 if err != nil { 175 t.Fatalf("%s: unexpected error setting up test: %s", tt.splitStmt, err) 176 } 177 for rows.Next() { 178 if err := rows.Scan(&key, &pretty, &expirationTimestamp); err != nil { 179 t.Fatalf("%s: unexpected error setting up test: %s", tt.splitStmt, err) 180 } 181 } 182 if err := rows.Err(); err != nil { 183 t.Fatalf("%s: unexpected error setting up test: %s", tt.splitStmt, err) 184 } 185 } 186 187 rows, err := db.Query(tt.unsplitStmt, tt.args...) 188 if err != nil && tt.error == "" { 189 t.Fatalf("%s: unexpected error: %s", tt.unsplitStmt, err) 190 } else if tt.error != "" && err == nil { 191 t.Fatalf("%s: expected error: %s", tt.unsplitStmt, tt.error) 192 } else if err != nil && tt.error != "" { 193 if !strings.Contains(err.Error(), tt.error) { 194 t.Fatalf("%s: unexpected error: %s", tt.unsplitStmt, err) 195 } 196 } else { 197 actualCount := 0 198 for rows.Next() { 199 actualCount++ 200 err := rows.Scan(&key, &pretty) 201 if err != nil { 202 t.Fatalf("%s: unexpected error: %s", tt.unsplitStmt, err) 203 } 204 // Successful unsplit, verify it happened. 205 rng, err := s.(*server.TestServer).LookupRange(key) 206 if err != nil { 207 t.Fatal(err) 208 } 209 if (rng.GetStickyBit() != hlc.Timestamp{}) { 210 t.Fatalf("%s: expected range sticky bit to be hlc.MinTimestamp, got %s", tt.unsplitStmt, rng.GetStickyBit()) 211 } 212 } 213 if err := rows.Err(); err != nil { 214 t.Fatalf("%s: unexpected error: %s", tt.unsplitStmt, err) 215 } 216 217 if tt.count != actualCount { 218 t.Fatalf("%s: expected %d unsplits, got %d", tt.unsplitStmt, tt.count, actualCount) 219 } 220 } 221 } 222 }