github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/workload/tpcc/checks.go (about) 1 // Copyright 2017 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 tpcc 12 13 import ( 14 gosql "database/sql" 15 16 "github.com/cockroachdb/errors" 17 ) 18 19 // Check is a tpcc consistency check. 20 type Check struct { 21 Name string 22 // If asOfSystemTime is non-empty it will be used to perform the check as 23 // a historical query using the provided value as the argument to the 24 // AS OF SYSTEM TIME clause. 25 Fn func(db *gosql.DB, asOfSystemTime string) error 26 Expensive bool 27 } 28 29 // AllChecks returns a slice of all of the checks. 30 func AllChecks() []Check { 31 return []Check{ 32 {"3.3.2.1", check3321, false}, 33 {"3.3.2.2", check3322, false}, 34 {"3.3.2.3", check3323, false}, 35 {"3.3.2.4", check3324, false}, 36 {"3.3.2.5", check3325, false}, 37 {"3.3.2.6", check3326, true}, 38 {"3.3.2.7", check3327, false}, 39 {"3.3.2.8", check3328, false}, 40 {"3.3.2.9", check3329, false}, 41 } 42 } 43 44 func check3321(db *gosql.DB, asOfSystemTime string) error { 45 // 3.3.2.1 Entries in the WAREHOUSE and DISTRICT tables must satisfy the relationship: 46 // W_YTD = sum (D_YTD) 47 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 48 if err != nil { 49 return err 50 } 51 defer func() { _ = txn.Rollback() }() 52 row := txn.QueryRow(` 53 SELECT 54 count(*) 55 FROM 56 warehouse 57 FULL JOIN ( 58 SELECT 59 d_w_id, sum(d_ytd) AS sum_d_ytd 60 FROM 61 district 62 GROUP BY 63 d_w_id 64 ) ON w_id = d_w_id 65 WHERE 66 w_ytd != sum_d_ytd 67 `) 68 var i int 69 if err := row.Scan(&i); err != nil { 70 return err 71 } 72 73 if i != 0 { 74 return errors.Errorf("%d rows returned, expected zero", i) 75 } 76 77 return nil 78 } 79 80 func check3322(db *gosql.DB, asOfSystemTime string) (err error) { 81 // Entries in the DISTRICT, ORDER, and NEW-ORDER tables must satisfy the relationship: 82 // D_NEXT_O_ID - 1 = max(O_ID) = max(NO_O_ID) 83 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 84 if err != nil { 85 return err 86 } 87 ts, err := selectTimestamp(txn) 88 _ = txn.Rollback() // close the txn now that we're done with it 89 if err != nil { 90 return err 91 } 92 districtRowsQuery := ` 93 SELECT 94 d_next_o_id 95 FROM 96 district AS OF SYSTEM TIME '` + ts + `' 97 ORDER BY 98 d_w_id, d_id` 99 districtRows, err := db.Query(districtRowsQuery) 100 if err != nil { 101 return err 102 } 103 newOrderQuery := ` 104 SELECT 105 max(no_o_id) 106 FROM 107 new_order AS OF SYSTEM TIME '` + ts + `' 108 GROUP BY 109 no_d_id, no_w_id 110 ORDER BY 111 no_w_id, no_d_id;` 112 newOrderRows, err := db.Query(newOrderQuery) 113 if err != nil { 114 return err 115 } 116 orderRowsQuery := ` 117 SELECT 118 max(o_id) 119 FROM 120 "order" AS OF SYSTEM TIME '` + ts + `' 121 GROUP BY 122 o_d_id, o_w_id 123 ORDER BY 124 o_w_id, o_d_id` 125 orderRows, err := db.Query(orderRowsQuery) 126 if err != nil { 127 return err 128 } 129 130 var district, newOrder, order float64 131 var i int 132 for ; districtRows.Next() && newOrderRows.Next() && orderRows.Next(); i++ { 133 if err := districtRows.Scan(&district); err != nil { 134 return err 135 } 136 if err := newOrderRows.Scan(&newOrder); err != nil { 137 return err 138 } 139 if err := orderRows.Scan(&order); err != nil { 140 return err 141 } 142 143 if (order != newOrder) || (order != (district - 1)) { 144 return errors.Errorf("inequality at idx %d: order: %f, newOrder: %f, district-1: %f", 145 i, order, newOrder, district-1) 146 } 147 } 148 if districtRows.Next() || newOrderRows.Next() || orderRows.Next() { 149 return errors.New("length mismatch between rows") 150 } 151 if err := districtRows.Close(); err != nil { 152 return err 153 } 154 if err := newOrderRows.Close(); err != nil { 155 return err 156 } 157 if err := orderRows.Close(); err != nil { 158 return err 159 } 160 161 if i == 0 { 162 return errors.Errorf("zero rows") 163 } 164 165 return nil 166 } 167 168 func check3323(db *gosql.DB, asOfSystemTime string) error { 169 // max(NO_O_ID) - min(NO_O_ID) + 1 = # of rows in new_order for each warehouse/district 170 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 171 if err != nil { 172 return err 173 } 174 defer func() { _ = txn.Rollback() }() 175 row := txn.QueryRow(` 176 SELECT 177 count(*) 178 FROM 179 ( 180 SELECT 181 max(no_o_id) - min(no_o_id) - count(*) AS nod 182 FROM 183 new_order 184 GROUP BY 185 no_w_id, no_d_id 186 ) 187 WHERE 188 nod != -1`) 189 190 var i int 191 if err := row.Scan(&i); err != nil { 192 return err 193 } 194 195 if i != 0 { 196 return errors.Errorf("%d rows returned, expected zero", i) 197 } 198 199 return nil 200 } 201 202 func check3324(db *gosql.DB, asOfSystemTime string) (err error) { 203 // sum(O_OL_CNT) = [number of rows in the ORDER-LINE table for this district] 204 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 205 if err != nil { 206 return err 207 } 208 // Select a timestamp which will be used for the concurrent queries below. 209 ts, err := selectTimestamp(txn) 210 _ = txn.Rollback() // close txn now that we're done with it. 211 if err != nil { 212 return err 213 } 214 leftRows, err := db.Query(` 215 SELECT 216 sum(o_ol_cnt) 217 FROM 218 "order" AS OF SYSTEM TIME '` + ts + `' 219 GROUP BY 220 o_w_id, o_d_id 221 ORDER BY 222 o_w_id, o_d_id`) 223 if err != nil { 224 return err 225 } 226 rightRows, err := db.Query(` 227 SELECT 228 count(*) 229 FROM 230 order_line AS OF SYSTEM TIME '` + ts + `' 231 GROUP BY 232 ol_w_id, ol_d_id 233 ORDER BY 234 ol_w_id, ol_d_id`) 235 if err != nil { 236 return err 237 } 238 var i int 239 var left, right int64 240 for ; leftRows.Next() && rightRows.Next(); i++ { 241 if err := leftRows.Scan(&left); err != nil { 242 return err 243 } 244 if err := rightRows.Scan(&right); err != nil { 245 return err 246 } 247 if left != right { 248 return errors.Errorf("order.sum(o_ol_cnt): %d != order_line.count(*): %d", left, right) 249 } 250 } 251 if i == 0 { 252 return errors.Errorf("0 rows returned") 253 } 254 if leftRows.Next() || rightRows.Next() { 255 return errors.Errorf("length of order.sum(o_ol_cnt) != order_line.count(*)") 256 } 257 258 if err := leftRows.Close(); err != nil { 259 return err 260 } 261 return rightRows.Close() 262 } 263 264 func check3325(db *gosql.DB, asOfSystemTime string) error { 265 // We want the symmetric difference between the sets: 266 // (SELECT no_w_id, no_d_id, no_o_id FROM new_order) 267 // (SELECT o_w_id, o_d_id, o_id FROM order@primary WHERE o_carrier_id IS NULL) 268 // We achieve this by two EXCEPT ALL queries. 269 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 270 if err != nil { 271 return err 272 } 273 defer func() { _ = txn.Rollback() }() 274 firstQuery, err := txn.Query(` 275 (SELECT no_w_id, no_d_id, no_o_id FROM new_order) 276 EXCEPT ALL 277 (SELECT o_w_id, o_d_id, o_id FROM "order"@primary WHERE o_carrier_id IS NULL)`) 278 if err != nil { 279 return err 280 } 281 if firstQuery.Next() { 282 return errors.Errorf("left EXCEPT right returned nonzero results.") 283 } 284 if err := firstQuery.Close(); err != nil { 285 return err 286 } 287 secondQuery, err := txn.Query(` 288 (SELECT o_w_id, o_d_id, o_id FROM "order"@primary WHERE o_carrier_id IS NULL) 289 EXCEPT ALL 290 (SELECT no_w_id, no_d_id, no_o_id FROM new_order)`) 291 if err != nil { 292 return err 293 } 294 if secondQuery.Next() { 295 return errors.Errorf("right EXCEPT left returned nonzero results.") 296 } 297 return secondQuery.Close() 298 } 299 300 func check3326(db *gosql.DB, asOfSystemTime string) (err error) { 301 // For any row in the ORDER table, O_OL_CNT must equal the number of rows 302 // in the ORDER-LINE table for the corresponding order defined by 303 // (O_W_ID, O_D_ID, O_ID) = (OL_W_ID, OL_D_ID, OL_O_ID). 304 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 305 if err != nil { 306 return err 307 } 308 defer func() { _ = txn.Rollback() }() 309 310 firstQuery, err := txn.Query(` 311 (SELECT o_w_id, o_d_id, o_id, o_ol_cnt FROM "order" 312 ORDER BY o_w_id, o_d_id, o_id DESC) 313 EXCEPT ALL 314 (SELECT ol_w_id, ol_d_id, ol_o_id, count(*) FROM order_line 315 GROUP BY (ol_w_id, ol_d_id, ol_o_id) 316 ORDER BY ol_w_id, ol_d_id, ol_o_id DESC)`) 317 if err != nil { 318 return err 319 } 320 if firstQuery.Next() { 321 return errors.Errorf("left EXCEPT right returned nonzero results") 322 } 323 if err := firstQuery.Close(); err != nil { 324 return err 325 } 326 secondQuery, err := txn.Query(` 327 (SELECT ol_w_id, ol_d_id, ol_o_id, count(*) FROM order_line 328 GROUP BY (ol_w_id, ol_d_id, ol_o_id) ORDER BY ol_w_id, ol_d_id, ol_o_id DESC) 329 EXCEPT ALL 330 (SELECT o_w_id, o_d_id, o_id, o_ol_cnt FROM "order" 331 ORDER BY o_w_id, o_d_id, o_id DESC)`) 332 if err != nil { 333 return err 334 } 335 336 if secondQuery.Next() { 337 return errors.Errorf("right EXCEPT left returned nonzero results") 338 } 339 return secondQuery.Close() 340 } 341 342 func check3327(db *gosql.DB, asOfSystemTime string) error { 343 // For any row in the ORDER-LINE table, OL_DELIVERY_D is set to a null 344 // date/time if and only if the corresponding row in the ORDER table defined 345 // by (O_W_ID, O_D_ID, O_ID) = (OL_W_ID, OL_D_ID, OL_O_ID) has 346 // O_CARRIER_ID set to a null value. 347 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 348 if err != nil { 349 return err 350 } 351 defer func() { _ = txn.Rollback() }() 352 row := txn.QueryRow(` 353 SELECT count(*) FROM 354 (SELECT o_w_id, o_d_id, o_id FROM "order" WHERE o_carrier_id IS NULL) 355 FULL OUTER JOIN 356 (SELECT ol_w_id, ol_d_id, ol_o_id FROM order_line WHERE ol_delivery_d IS NULL) 357 ON (ol_w_id = o_w_id AND ol_d_id = o_d_id AND ol_o_id = o_id) 358 WHERE ol_o_id IS NULL OR o_id IS NULL 359 `) 360 361 var i int 362 if err := row.Scan(&i); err != nil { 363 return err 364 } 365 366 if i != 0 { 367 return errors.Errorf("%d rows returned, expected zero", i) 368 } 369 370 return nil 371 } 372 373 func check3328(db *gosql.DB, asOfSystemTime string) error { 374 // Entries in the WAREHOUSE and HISTORY tables must satisfy the relationship: 375 // W_YTD = SUM(H_AMOUNT) for each warehouse defined by (W_ID = H _W_ID). 376 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 377 if err != nil { 378 return err 379 } 380 defer func() { _ = txn.Rollback() }() 381 row := txn.QueryRow(` 382 SELECT count(*) FROM 383 (SELECT w_id, w_ytd, sum FROM warehouse 384 JOIN 385 (SELECT h_w_id, sum(h_amount) FROM history GROUP BY h_w_id) 386 ON w_id = h_w_id 387 WHERE w_ytd != sum 388 ) 389 `) 390 391 var i int 392 if err := row.Scan(&i); err != nil { 393 return err 394 } 395 396 if i != 0 { 397 return errors.Errorf("%d rows returned, expected zero", i) 398 } 399 400 return nil 401 } 402 403 func check3329(db *gosql.DB, asOfSystemTime string) error { 404 // Entries in the DISTRICT and HISTORY tables must satisfy the relationship: 405 // D_YTD=SUM(H_AMOUNT) for each district defined by (D_W_ID,D_ID)=(H_W_ID,H_D_ID) 406 txn, err := beginAsOfSystemTime(db, asOfSystemTime) 407 if err != nil { 408 return err 409 } 410 defer func() { _ = txn.Rollback() }() 411 row := txn.QueryRow(` 412 SELECT count(*) FROM 413 (SELECT d_id, d_ytd, sum FROM district 414 JOIN 415 (SELECT h_w_id, h_d_id, sum(h_amount) FROM history GROUP BY (h_w_id, h_d_id)) 416 ON d_id = h_d_id AND d_w_id = h_w_id 417 WHERE d_ytd != sum 418 ) 419 `) 420 421 var i int 422 if err := row.Scan(&i); err != nil { 423 return err 424 } 425 426 if i != 0 { 427 return errors.Errorf("%d rows returned, expected zero", i) 428 } 429 430 return nil 431 } 432 433 // beginAsOfSystemTime starts a transaction and optionally sets it to occur at 434 // the provided asOfSystemTime. If asOfSystemTime is empty, the transaction will 435 // not be historical. The asOfSystemTime value will be used as literal SQL in a 436 // SET TRANSACTION AS OF SYSTEM TIME clause. 437 func beginAsOfSystemTime(db *gosql.DB, asOfSystemTime string) (txn *gosql.Tx, err error) { 438 txn, err = db.Begin() 439 if err != nil { 440 return nil, err 441 } 442 if asOfSystemTime != "" { 443 _, err = txn.Exec("SET TRANSACTION AS OF SYSTEM TIME " + asOfSystemTime) 444 if err != nil { 445 _ = txn.Rollback() 446 return nil, err 447 } 448 } 449 return txn, nil 450 } 451 452 // selectTimestamp retreives an unqouted string literal of a decimal value 453 // representing the hlc timestamp of the provided txn. 454 func selectTimestamp(txn *gosql.Tx) (ts string, err error) { 455 err = txn.QueryRow("SELECT cluster_logical_timestamp()::string").Scan(&ts) 456 return ts, err 457 }