github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/as_of_test.go (about) 1 // Copyright 2016 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 "bytes" 15 "context" 16 "fmt" 17 "testing" 18 19 "github.com/cockroachdb/apd" 20 "github.com/cockroachdb/cockroach/pkg/base" 21 "github.com/cockroachdb/cockroach/pkg/kv" 22 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase" 23 "github.com/cockroachdb/cockroach/pkg/roachpb" 24 "github.com/cockroachdb/cockroach/pkg/sql" 25 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 26 "github.com/cockroachdb/cockroach/pkg/sql/tests" 27 "github.com/cockroachdb/cockroach/pkg/testutils" 28 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 29 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 30 ) 31 32 func TestAsOfTime(t *testing.T) { 33 defer leaktest.AfterTest(t)() 34 35 params, _ := tests.CreateTestServerParams() 36 params.Knobs.GCJob = &sql.GCJobTestingKnobs{RunBeforeResume: func(_ int64) error { select {} }} 37 s, db, _ := serverutils.StartServer(t, params) 38 defer s.Stopper().Stop(context.Background()) 39 40 const val1 = 1 41 const val2 = 2 42 const query = "SELECT a FROM d.t AS OF SYSTEM TIME %s WHERE a > $1" 43 44 var i, j int 45 46 // Expect an error if table doesn't exist at specified time. This ensures 47 // that the code that fetches schemas at the time returns an error instead 48 // of panics. 49 var tsEmpty string 50 if err := db.QueryRow("SELECT cluster_logical_timestamp()").Scan(&tsEmpty); err != nil { 51 t.Fatal(err) 52 } 53 if _, err := db.Query(fmt.Sprintf(query, tsEmpty), 0); !testutils.IsError(err, `pq: relation "d.t" does not exist`) { 54 t.Fatal(err) 55 } 56 57 var tsDBExists string 58 if err := db.QueryRow("CREATE DATABASE d; SELECT cluster_logical_timestamp()").Scan(&tsDBExists); err != nil { 59 t.Fatal(err) 60 } 61 if _, err := db.Query(fmt.Sprintf(query, tsDBExists), 0); !testutils.IsError(err, `pq: relation "d.t" does not exist`) { 62 t.Fatal(err) 63 } 64 65 if _, err := db.Exec(` 66 CREATE TABLE d.t (a INT, b INT); 67 CREATE TABLE d.j (c INT); 68 `); err != nil { 69 t.Fatal(err) 70 } 71 var tsTableExists string 72 if err := db.QueryRow("SELECT cluster_logical_timestamp()").Scan(&tsTableExists); err != nil { 73 t.Fatal(err) 74 } 75 if err := db.QueryRow(fmt.Sprintf(query, tsTableExists), 0).Scan(&i); !testutils.IsError(err, "sql: no rows in result set") { 76 t.Fatal(err) 77 } 78 79 if _, err := db.Exec("INSERT INTO d.t (a) VALUES ($1)", val1); err != nil { 80 t.Fatal(err) 81 } 82 if _, err := db.Exec("INSERT INTO d.j (c) VALUES ($1)", val2); err != nil { 83 t.Fatal(err) 84 } 85 var tsVal1 string 86 if err := db.QueryRow("SELECT a, cluster_logical_timestamp() FROM d.t").Scan(&i, &tsVal1); err != nil { 87 t.Fatal(err) 88 } else if i != val1 { 89 t.Fatalf("expected %v, got %v", val1, i) 90 } 91 if _, err := db.Exec("UPDATE d.t SET a = $1", val2); err != nil { 92 t.Fatal(err) 93 } 94 if _, err := db.Exec("UPDATE d.j SET c = $1", val1); err != nil { 95 t.Fatal(err) 96 } 97 if err := db.QueryRow("SELECT a FROM d.t").Scan(&i); err != nil { 98 t.Fatal(err) 99 } else if i != val2 { 100 t.Fatalf("expected %v, got %v", val2, i) 101 } 102 103 // Test a simple query, and do it with and without wrapping parens to check 104 // that parens don't matter. 105 testutils.RunTrueAndFalse(t, "parens", func(t *testing.T, useParens bool) { 106 openParens := "" 107 closeParens := "" 108 if useParens { 109 openParens = "((" 110 closeParens = "))" 111 } 112 query := fmt.Sprintf("%sSELECT a, c FROM d.t, d.j AS OF SYSTEM TIME %s%s", openParens, tsVal1, closeParens) 113 if err := db.QueryRow(query).Scan(&i, &j); err != nil { 114 t.Fatal(err) 115 } else if i != val1 { 116 t.Fatalf("expected %v, got %v", val1, i) 117 } else if j != val2 { 118 t.Fatalf("expected %v, got %v", val2, j) 119 } 120 }) 121 122 // Future queries shouldn't work. 123 if err := db.QueryRow("SELECT a FROM d.t AS OF SYSTEM TIME '2200-01-01'").Scan(&i); !testutils.IsError(err, "pq: AS OF SYSTEM TIME: cannot specify timestamp in the future") { 124 t.Fatal(err) 125 } 126 127 // Verify queries with positive scale work properly. 128 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME 1e1"); !testutils.IsError(err, `pq: relation "d.t" does not exist`) { 129 t.Fatal(err) 130 } 131 132 // Verify queries with large exponents error properly. 133 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME 1e40"); !testutils.IsError(err, "value out of range") { 134 t.Fatal(err) 135 } 136 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME 1.4"); !testutils.IsError(err, 137 `parsing argument: strconv.ParseInt: parsing "4000000000": value out of range`) { 138 t.Fatal(err) 139 } 140 141 // Verify logical parts parse with < 10 digits. 142 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME 1.123456789"); !testutils.IsError(err, `pq: relation "d.t" does not exist`) { 143 t.Fatal(err) 144 } 145 146 // Verify logical parts parse with == 10 digits. 147 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME 1.1234567890"); !testutils.IsError(err, `pq: relation "d.t" does not exist`) { 148 t.Fatal(err) 149 } 150 151 // Too much logical precision is an error. 152 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME 1.12345678901"); !testutils.IsError(err, "logical part has too many digits") { 153 t.Fatal(err) 154 } 155 156 // Ditto, as string. 157 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME '1.12345678901'"); !testutils.IsError(err, "logical part has too many digits") { 158 t.Fatal(err) 159 } 160 161 // String values that are neither timestamps nor decimals are an error. 162 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME 'xxx'"); !testutils.IsError(err, "value is neither timestamp, decimal, nor interval") { 163 t.Fatal(err) 164 } 165 166 // Zero is not a valid value. 167 for _, zero := range []string{"0", "'0'", "0.0000000000", "'0.0000000000'"} { 168 if _, err := db.Query("SELECT a FROM d.t AS OF SYSTEM TIME " + zero); !testutils.IsError(err, "zero timestamp is invalid") { 169 t.Fatal(err) 170 } 171 } 172 173 // Queries before the Unix epoch definitely shouldn't work. 174 if err := db.QueryRow("SELECT a FROM d.t AS OF SYSTEM TIME '1969-12-30'").Scan(&i); err == nil { 175 t.Fatal("expected error") 176 } else if !testutils.IsError(err, "AS OF SYSTEM TIME: timestamp before 1970-01-01T00:00:00Z is invalid") { 177 t.Fatal(err) 178 } 179 180 // Subqueries shouldn't work. 181 _, err := db.Query( 182 fmt.Sprintf("SELECT (SELECT a FROM d.t AS OF SYSTEM TIME %s)", tsVal1)) 183 if !testutils.IsError(err, "pq: AS OF SYSTEM TIME must be provided on a top-level statement") { 184 t.Fatalf("expected not supported, got: %v", err) 185 } 186 187 // Subqueries do work of the timestamps are consistent. 188 _, err = db.Query( 189 fmt.Sprintf("SELECT (SELECT a FROM d.t AS OF SYSTEM TIME %s) FROM (SELECT 1) AS OF SYSTEM TIME '1980-01-01'", tsVal1)) 190 if !testutils.IsError(err, "cannot specify AS OF SYSTEM TIME with different timestamps") { 191 t.Fatalf("expected inconsistent statements, got: %v", err) 192 } 193 if err := db.QueryRow( 194 fmt.Sprintf("SELECT (SELECT 1 FROM d.t AS OF SYSTEM TIME %s) FROM (SELECT 1) AS OF SYSTEM TIME %s", tsVal1, tsVal1)).Scan(&i); err != nil { 195 t.Fatal(err) 196 } 197 198 // Lightly test AS OF SYSTEM TIME with SET TRANSACTION, more complete testing 199 // for this functionality lives in the logic_tests. 200 tx, err := db.Begin() 201 if err != nil { 202 t.Fatal(err) 203 } 204 if _, err := tx.Exec(fmt.Sprintf("SET TRANSACTION AS OF SYSTEM TIME %s", tsVal1)); err != nil { 205 t.Fatal(err) 206 } 207 if err := tx.QueryRow("SELECT a FROM d.t WHERE a > $1", 0).Scan(&i); err != nil { 208 t.Fatal(err) 209 } else if i != val1 { 210 t.Fatalf("expected %v, got %v", val1, i) 211 } 212 if err := tx.Commit(); err != nil { 213 t.Fatal(err) 214 } 215 216 // Verify that we can read columns in the past that are dropped in the future. 217 if _, err := db.Exec("ALTER TABLE d.t DROP COLUMN a"); err != nil { 218 t.Fatal(err) 219 } 220 if err := db.QueryRow(fmt.Sprintf(query, tsVal1), 0).Scan(&i); err != nil { 221 t.Fatal(err) 222 } else if i != val1 { 223 t.Fatalf("expected %v, got %v", val1, i) 224 } 225 226 // Can use in a transaction by using the SET TRANSACTION syntax 227 if err := db.QueryRow(fmt.Sprintf(` 228 BEGIN; 229 SET TRANSACTION AS OF SYSTEM TIME %s; 230 SELECT a FROM d.t; 231 COMMIT; 232 `, tsVal1)).Scan(&i); err != nil { 233 t.Fatal(err) 234 } else if i != val1 { 235 t.Fatalf("expected %v, got %v", val1, i) 236 } 237 238 // Can't use in a transaction without SET TRANSACTION AS OF SYSTEM TIME syntax 239 _, err = db.Query( 240 fmt.Sprintf("BEGIN; SELECT a FROM d.t AS OF SYSTEM TIME %s; COMMIT;", tsVal1)) 241 if !testutils.IsError(err, "try SET TRANSACTION AS OF SYSTEM TIME") { 242 t.Fatalf("expected try SET TRANSACTION AS OF SYSTEM TIME, got: %v", err) 243 } 244 } 245 246 // Test that a TransactionRetryError will retry the read until it succeeds. The 247 // test is designed so that if the proto timestamps are bumped during retry 248 // a failure will occur. 249 func TestAsOfRetry(t *testing.T) { 250 defer leaktest.AfterTest(t)() 251 252 params, cmdFilters := tests.CreateTestServerParams() 253 s, sqlDB, _ := serverutils.StartServer(t, params) 254 defer s.Stopper().Stop(context.Background()) 255 256 const val1 = 1 257 const val2 = 2 258 const name = "boulanger" 259 260 if _, err := sqlDB.Exec(` 261 CREATE DATABASE d; 262 CREATE TABLE d.t (s STRING PRIMARY KEY, a INT); 263 `); err != nil { 264 t.Fatal(err) 265 } 266 var tsStart string 267 if err := sqlDB.QueryRow(` 268 INSERT INTO d.t (s, a) VALUES ($1, $2) 269 RETURNING cluster_logical_timestamp(); 270 `, name, val1).Scan(&tsStart); err != nil { 271 t.Fatal(err) 272 } 273 274 var tsVal2 string 275 if err := sqlDB.QueryRow("UPDATE d.t SET a = $1 RETURNING cluster_logical_timestamp()", val2).Scan(&tsVal2); err != nil { 276 t.Fatal(err) 277 } 278 walltime := new(apd.Decimal) 279 if _, _, err := walltime.SetString(tsVal2); err != nil { 280 t.Fatalf("couldn't set decimal: %s", tsVal2) 281 } 282 oneTick := apd.New(1, 0) 283 // Set tsVal1 to 1ns before tsVal2. 284 if _, err := tree.ExactCtx.Sub(walltime, walltime, oneTick); err != nil { 285 t.Fatal(err) 286 } 287 tsVal1 := walltime.Text('f') 288 289 // Set up error injection that causes retries. 290 magicVals := createFilterVals(nil, nil) 291 magicVals.restartCounts = map[string]int{ 292 name: 5, 293 } 294 cleanupFilter := cmdFilters.AppendFilter( 295 func(args kvserverbase.FilterArgs) *roachpb.Error { 296 magicVals.Lock() 297 defer magicVals.Unlock() 298 299 switch req := args.Req.(type) { 300 case *roachpb.ScanRequest: 301 if kv.TestingIsRangeLookupRequest(req) { 302 return nil 303 } 304 for key, count := range magicVals.restartCounts { 305 if err := checkCorrectTxn(string(req.Key), magicVals, args.Hdr.Txn); err != nil { 306 return roachpb.NewError(err) 307 } 308 if count > 0 && bytes.Contains(req.Key, []byte(key)) { 309 magicVals.restartCounts[key]-- 310 err := roachpb.NewTransactionRetryError( 311 roachpb.RETRY_REASON_UNKNOWN, "filter err") 312 magicVals.failedValues[string(req.Key)] = 313 failureRecord{err, args.Hdr.Txn} 314 txn := args.Hdr.Txn.Clone() 315 txn.WriteTimestamp = txn.WriteTimestamp.Add(0, 1) 316 return roachpb.NewErrorWithTxn(err, txn) 317 } 318 } 319 } 320 return nil 321 }, false) 322 323 var i int 324 // Query with tsVal1 which should return the first value. Since tsVal1 is just 325 // one nanosecond before tsVal2, any proto timestamp bumping will return val2 326 // and error. 327 // Must specify the WHERE here to trigger the injection errors. 328 if err := sqlDB.QueryRow(fmt.Sprintf("SELECT a FROM d.t AS OF SYSTEM TIME %s WHERE s = '%s'", tsVal1, name)).Scan(&i); err != nil { 329 t.Fatal(err) 330 } else if i != val1 { 331 t.Fatalf("unexpected val: %v", i) 332 } 333 334 cleanupFilter() 335 // Verify that the retry errors were injected. 336 checkRestarts(t, magicVals) 337 338 // Query with tsVal2 to ensure val2 is indeed present. 339 if err := sqlDB.QueryRow(fmt.Sprintf("SELECT a FROM d.t AS OF SYSTEM TIME %s", tsVal2)).Scan(&i); err != nil { 340 t.Fatal(err) 341 } else if i != val2 { 342 t.Fatalf("unexpected val: %v", i) 343 } 344 } 345 346 // Test that tracing works with SELECT ... AS OF SYSTEM TIME. 347 func TestShowTraceAsOfTime(t *testing.T) { 348 defer leaktest.AfterTest(t)() 349 350 s, db, _ := serverutils.StartServer(t, base.TestServerArgs{}) 351 defer s.Stopper().Stop(context.Background()) 352 353 const val1 = 456 354 const val2 = 789 355 356 if _, err := db.Exec(` 357 CREATE DATABASE test; 358 CREATE TABLE test.t (x INT); 359 `); err != nil { 360 t.Fatal(err) 361 } 362 363 if _, err := db.Exec("INSERT INTO test.t (x) VALUES ($1)", val1); err != nil { 364 t.Fatal(err) 365 } 366 var tsVal1 string 367 var i int 368 err := db.QueryRow("SELECT x, cluster_logical_timestamp() FROM test.t").Scan( 369 &i, &tsVal1) 370 if err != nil { 371 t.Fatal(err) 372 } else if i != val1 { 373 t.Fatalf("expected %d, got %v", val1, i) 374 } 375 if _, err := db.Exec("UPDATE test.t SET x = $1", val2); err != nil { 376 t.Fatal(err) 377 } 378 379 // We now run a traced historical query and expect to see val1 instead of the 380 // more recent val2. We play some tricks for testing this; we run SET tracing = results 381 // so that rows like "output row: [<foo>]" are part of the results. And 382 // then we look for a particular such row. 383 query := fmt.Sprintf("SET tracing = on,results; SELECT x FROM test.t AS OF SYSTEM TIME %s; SET tracing = off", tsVal1) 384 if _, err := db.Exec(query); err != nil { 385 t.Fatal(err) 386 } 387 388 query = fmt.Sprintf("SELECT count(1) FROM [SHOW KV TRACE FOR SESSION] "+ 389 "WHERE message = 'output row: [%d]'", val1) 390 if err := db.QueryRow(query).Scan(&i); err != nil { 391 t.Fatal(err) 392 } else if i != 1 { 393 t.Fatalf("expected to find one matching row, got %v", i) 394 } 395 }