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  }