github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/split_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  	"context"
    15  	gosql "database/sql"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/server"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/tests"
    22  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    23  	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
    24  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    25  )
    26  
    27  func TestSplitAt(t *testing.T) {
    28  	defer leaktest.AfterTest(t)()
    29  
    30  	params, _ := tests.CreateTestServerParams()
    31  	s, db, _ := serverutils.StartServer(t, params)
    32  	defer s.Stopper().Stop(context.Background())
    33  
    34  	r := sqlutils.MakeSQLRunner(db)
    35  
    36  	r.Exec(t, "CREATE DATABASE d")
    37  	r.Exec(t, `CREATE TABLE d.t (
    38  		i INT,
    39  		s STRING,
    40  		PRIMARY KEY (i, s),
    41  		INDEX s_idx (s)
    42  	)`)
    43  	r.Exec(t, `CREATE TABLE d.i (k INT PRIMARY KEY)`)
    44  
    45  	tests := []struct {
    46  		in    string
    47  		error string
    48  		args  []interface{}
    49  	}{
    50  		{
    51  			in: "ALTER TABLE d.t SPLIT AT VALUES (2, 'b')",
    52  		},
    53  		{
    54  			// Splitting at an existing split is a silent no-op.
    55  			in: "ALTER TABLE d.t SPLIT AT VALUES (2, 'b')",
    56  		},
    57  		{
    58  			in: "ALTER TABLE d.t SPLIT AT VALUES (3, 'c'), (4, 'd')",
    59  		},
    60  		{
    61  			in: "ALTER TABLE d.t SPLIT AT SELECT 5, 'd'",
    62  		},
    63  		{
    64  			in: "ALTER TABLE d.t SPLIT AT SELECT * FROM (VALUES (6, 'e'), (7, 'f')) AS a",
    65  		},
    66  		{
    67  			in: "ALTER TABLE d.t SPLIT AT VALUES (10)",
    68  		},
    69  		{
    70  			in:    "ALTER TABLE d.t SPLIT AT VALUES ('c', 3)",
    71  			error: "could not parse \"c\" as type int",
    72  		},
    73  		{
    74  			in:    "ALTER TABLE d.t SPLIT AT VALUES (i, s)",
    75  			error: `column "i" does not exist`,
    76  		},
    77  		{
    78  			in: "ALTER INDEX d.t@s_idx SPLIT AT VALUES ('f')",
    79  		},
    80  		{
    81  			in:    "ALTER INDEX d.t@not_present SPLIT AT VALUES ('g')",
    82  			error: `index "not_present" does not exist`,
    83  		},
    84  		{
    85  			in:    "ALTER TABLE d.i SPLIT AT VALUES (avg(1::float))",
    86  			error: "aggregate functions are not allowed in VALUES",
    87  		},
    88  		{
    89  			in:   "ALTER TABLE d.i SPLIT AT VALUES ($1)",
    90  			args: []interface{}{8},
    91  		},
    92  		{
    93  			in:    "ALTER TABLE d.i SPLIT AT VALUES ($1)",
    94  			error: "no value provided for placeholder: $1",
    95  		},
    96  		{
    97  			in:    "ALTER TABLE d.i SPLIT AT VALUES ($1)",
    98  			args:  []interface{}{"blah"},
    99  			error: "error in argument for $1: strconv.ParseInt",
   100  		},
   101  		{
   102  			in:    "ALTER TABLE d.i SPLIT AT VALUES ($1::string)",
   103  			args:  []interface{}{"1"},
   104  			error: "SPLIT AT data column 1 (k) must be of type int, not type string",
   105  		},
   106  		{
   107  			in: "ALTER TABLE d.i SPLIT AT VALUES ((SELECT 1))",
   108  		},
   109  		{
   110  			in: "ALTER TABLE d.i SPLIT AT VALUES (10) WITH EXPIRATION '1 day'",
   111  		},
   112  		{
   113  			in: "ALTER TABLE d.i SPLIT AT VALUES (11) WITH EXPIRATION '1 day'::interval",
   114  		},
   115  		{
   116  			in: "ALTER TABLE d.i SPLIT AT VALUES (12) WITH EXPIRATION '7258118400000000000.0'",
   117  		},
   118  		{
   119  			in: "ALTER TABLE d.i SPLIT AT VALUES (13) WITH EXPIRATION '2200-01-01 00:00:00.0'",
   120  		},
   121  		{
   122  			in: "ALTER TABLE d.i SPLIT AT VALUES (14) WITH EXPIRATION TIMESTAMP '2200-01-01 00:00:00.0'",
   123  		},
   124  		{
   125  			in: "ALTER TABLE d.i SPLIT AT VALUES (15) WITH EXPIRATION '2200-01-01 00:00:00.0':::timestamp",
   126  		},
   127  		{
   128  			in: "ALTER TABLE d.i SPLIT AT VALUES (16) WITH EXPIRATION TIMESTAMPTZ '2200-01-01 00:00:00.0'",
   129  		},
   130  		{
   131  			in:    "ALTER TABLE d.i SPLIT AT VALUES (17) WITH EXPIRATION 'a'",
   132  			error: "SPLIT AT: value is neither timestamp, decimal, nor interval",
   133  		},
   134  		{
   135  			in:    "ALTER TABLE d.i SPLIT AT VALUES (17) WITH EXPIRATION true",
   136  			error: "SPLIT AT: expected timestamp, decimal, or interval, got bool (*tree.DBool)",
   137  		},
   138  		{
   139  			in:    "ALTER TABLE d.i SPLIT AT VALUES (17) WITH EXPIRATION '1969-01-01 00:00:00.0'",
   140  			error: "SPLIT AT: timestamp before 1970-01-01T00:00:00Z is invalid",
   141  		},
   142  		{
   143  			in:    "ALTER TABLE d.i SPLIT AT VALUES (17) WITH EXPIRATION '1970-01-01 00:00:00.0'",
   144  			error: "SPLIT AT: zero timestamp is invalid",
   145  		},
   146  		{
   147  			in:    "ALTER TABLE d.i SPLIT AT VALUES (17) WITH EXPIRATION '-1 day'::interval",
   148  			error: "SPLIT AT: expiration time should be greater than or equal to current time",
   149  		},
   150  		{
   151  			in:    "ALTER TABLE d.i SPLIT AT VALUES (17) WITH EXPIRATION '0.1us'",
   152  			error: "SPLIT AT: interval value '0.1us' too small, absolute value must be >= 1µs",
   153  		},
   154  	}
   155  
   156  	for _, tt := range tests {
   157  		var key roachpb.Key
   158  		var pretty string
   159  		var expirationTimestamp gosql.NullString
   160  		err := db.QueryRow(tt.in, tt.args...).Scan(&key, &pretty, &expirationTimestamp)
   161  		if err != nil && tt.error == "" {
   162  			t.Fatalf("%s: unexpected error: %s", tt.in, err)
   163  		} else if tt.error != "" && err == nil {
   164  			t.Fatalf("%s: expected error: %s", tt.in, tt.error)
   165  		} else if err != nil && tt.error != "" {
   166  			if !strings.Contains(err.Error(), tt.error) {
   167  				t.Fatalf("%s: unexpected error: %s", tt.in, err)
   168  			}
   169  		} else {
   170  			// Successful split, verify it happened.
   171  			rng, err := s.(*server.TestServer).LookupRange(key)
   172  			if err != nil {
   173  				t.Fatal(err)
   174  			}
   175  			expect := roachpb.Key(rng.StartKey)
   176  			if !expect.Equal(key) {
   177  				t.Fatalf("%s: expected range start %s, got %s", tt.in, expect, pretty)
   178  			}
   179  		}
   180  	}
   181  }