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  }