github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/table_test.go (about) 1 // Copyright 2015 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 12 13 import ( 14 "context" 15 "reflect" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/keys" 19 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 20 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 21 "github.com/cockroachdb/cockroach/pkg/sql/tests" 22 "github.com/cockroachdb/cockroach/pkg/sql/types" 23 "github.com/cockroachdb/cockroach/pkg/testutils" 24 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 25 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 26 "github.com/cockroachdb/cockroach/pkg/util/protoutil" 27 ) 28 29 func TestMakeTableDescColumns(t *testing.T) { 30 defer leaktest.AfterTest(t)() 31 32 testData := []struct { 33 sqlType string 34 colType *types.T 35 nullable bool 36 }{ 37 { 38 "BIT", 39 types.MakeBit(1), 40 true, 41 }, 42 { 43 "BIT(3)", 44 types.MakeBit(3), 45 true, 46 }, 47 { 48 "VARBIT", 49 types.VarBit, 50 true, 51 }, 52 { 53 "VARBIT(3)", 54 types.MakeVarBit(3), 55 true, 56 }, 57 { 58 "BOOLEAN", 59 types.Bool, 60 true, 61 }, 62 { 63 "INT", 64 types.Int, 65 true, 66 }, 67 { 68 "INT2", 69 types.Int2, 70 true, 71 }, 72 { 73 "INT4", 74 types.Int4, 75 true, 76 }, 77 { 78 "INT8", 79 types.Int, 80 true, 81 }, 82 { 83 "INT64", 84 types.Int, 85 true, 86 }, 87 { 88 "BIGINT", 89 types.Int, 90 true, 91 }, 92 { 93 "FLOAT(3)", 94 types.Float4, 95 true, 96 }, 97 { 98 "DOUBLE PRECISION", 99 types.Float, 100 true, 101 }, 102 { 103 "DECIMAL(6,5)", 104 types.MakeDecimal(6, 5), 105 true, 106 }, 107 { 108 "DATE", 109 types.Date, 110 true, 111 }, 112 { 113 "TIME", 114 types.Time, 115 true, 116 }, 117 { 118 "TIMESTAMP", 119 types.Timestamp, 120 true, 121 }, 122 { 123 "INTERVAL", 124 types.Interval, 125 true, 126 }, 127 { 128 "CHAR", 129 types.MakeChar(1), 130 true, 131 }, 132 { 133 "CHAR(3)", 134 types.MakeChar(3), 135 true, 136 }, 137 { 138 "VARCHAR", 139 types.VarChar, 140 true, 141 }, 142 { 143 "VARCHAR(3)", 144 types.MakeVarChar(3), 145 true, 146 }, 147 { 148 "TEXT", 149 types.String, 150 true, 151 }, 152 { 153 `"char"`, 154 types.MakeQChar(0), 155 true, 156 }, 157 { 158 "BLOB", 159 types.Bytes, 160 true, 161 }, 162 { 163 "INT NOT NULL", 164 types.Int, 165 false, 166 }, 167 { 168 "INT NULL", 169 types.Int, 170 true, 171 }, 172 } 173 for i, d := range testData { 174 s := "CREATE TABLE foo.test (a " + d.sqlType + " PRIMARY KEY, b " + d.sqlType + ")" 175 schema, err := CreateTestTableDescriptor(context.Background(), 1, 100, s, sqlbase.NewDefaultPrivilegeDescriptor()) 176 if err != nil { 177 t.Fatalf("%d: %v", i, err) 178 } 179 if schema.Columns[0].Nullable { 180 t.Fatalf("%d: expected non-nullable primary key, but got %+v", i, schema.Columns[0].Nullable) 181 } 182 if !d.colType.Identical(schema.Columns[0].Type) { 183 t.Fatalf("%d: expected %+v, but got %+v", i, d.colType.DebugString(), schema.Columns[0].Type.DebugString()) 184 } 185 if d.nullable != schema.Columns[1].Nullable { 186 t.Fatalf("%d: expected %+v, but got %+v", i, d.nullable, schema.Columns[1].Nullable) 187 } 188 } 189 } 190 191 func TestMakeTableDescIndexes(t *testing.T) { 192 defer leaktest.AfterTest(t)() 193 194 testData := []struct { 195 sql string 196 primary sqlbase.IndexDescriptor 197 indexes []sqlbase.IndexDescriptor 198 }{ 199 { 200 "a INT PRIMARY KEY", 201 sqlbase.IndexDescriptor{ 202 Name: sqlbase.PrimaryKeyIndexName, 203 ID: 1, 204 Unique: true, 205 ColumnNames: []string{"a"}, 206 ColumnIDs: []sqlbase.ColumnID{1}, 207 ColumnDirections: []sqlbase.IndexDescriptor_Direction{sqlbase.IndexDescriptor_ASC}, 208 Version: sqlbase.SecondaryIndexFamilyFormatVersion, 209 }, 210 []sqlbase.IndexDescriptor{}, 211 }, 212 { 213 "a INT UNIQUE, b INT PRIMARY KEY", 214 sqlbase.IndexDescriptor{ 215 Name: "primary", 216 ID: 1, 217 Unique: true, 218 ColumnNames: []string{"b"}, 219 ColumnIDs: []sqlbase.ColumnID{2}, 220 ColumnDirections: []sqlbase.IndexDescriptor_Direction{sqlbase.IndexDescriptor_ASC}, 221 Version: sqlbase.SecondaryIndexFamilyFormatVersion, 222 }, 223 []sqlbase.IndexDescriptor{ 224 { 225 Name: "test_a_key", 226 ID: 2, 227 Unique: true, 228 ColumnNames: []string{"a"}, 229 ColumnIDs: []sqlbase.ColumnID{1}, 230 ExtraColumnIDs: []sqlbase.ColumnID{2}, 231 ColumnDirections: []sqlbase.IndexDescriptor_Direction{sqlbase.IndexDescriptor_ASC}, 232 Version: sqlbase.SecondaryIndexFamilyFormatVersion, 233 }, 234 }, 235 }, 236 { 237 "a INT, b INT, CONSTRAINT c PRIMARY KEY (a, b)", 238 sqlbase.IndexDescriptor{ 239 Name: "c", 240 ID: 1, 241 Unique: true, 242 ColumnNames: []string{"a", "b"}, 243 ColumnIDs: []sqlbase.ColumnID{1, 2}, 244 ColumnDirections: []sqlbase.IndexDescriptor_Direction{sqlbase.IndexDescriptor_ASC, sqlbase.IndexDescriptor_ASC}, 245 Version: sqlbase.SecondaryIndexFamilyFormatVersion, 246 }, 247 []sqlbase.IndexDescriptor{}, 248 }, 249 { 250 "a INT, b INT, CONSTRAINT c UNIQUE (b), PRIMARY KEY (a, b)", 251 sqlbase.IndexDescriptor{ 252 Name: "primary", 253 ID: 1, 254 Unique: true, 255 ColumnNames: []string{"a", "b"}, 256 ColumnIDs: []sqlbase.ColumnID{1, 2}, 257 ColumnDirections: []sqlbase.IndexDescriptor_Direction{sqlbase.IndexDescriptor_ASC, sqlbase.IndexDescriptor_ASC}, 258 Version: sqlbase.SecondaryIndexFamilyFormatVersion, 259 }, 260 []sqlbase.IndexDescriptor{ 261 { 262 Name: "c", 263 ID: 2, 264 Unique: true, 265 ColumnNames: []string{"b"}, 266 ColumnIDs: []sqlbase.ColumnID{2}, 267 ExtraColumnIDs: []sqlbase.ColumnID{1}, 268 ColumnDirections: []sqlbase.IndexDescriptor_Direction{sqlbase.IndexDescriptor_ASC}, 269 Version: sqlbase.SecondaryIndexFamilyFormatVersion, 270 }, 271 }, 272 }, 273 { 274 "a INT, b INT, PRIMARY KEY (a, b)", 275 sqlbase.IndexDescriptor{ 276 Name: sqlbase.PrimaryKeyIndexName, 277 ID: 1, 278 Unique: true, 279 ColumnNames: []string{"a", "b"}, 280 ColumnIDs: []sqlbase.ColumnID{1, 2}, 281 ColumnDirections: []sqlbase.IndexDescriptor_Direction{sqlbase.IndexDescriptor_ASC, sqlbase.IndexDescriptor_ASC}, 282 Version: sqlbase.SecondaryIndexFamilyFormatVersion, 283 }, 284 []sqlbase.IndexDescriptor{}, 285 }, 286 } 287 for i, d := range testData { 288 s := "CREATE TABLE foo.test (" + d.sql + ")" 289 schema, err := CreateTestTableDescriptor(context.Background(), 1, 100, s, sqlbase.NewDefaultPrivilegeDescriptor()) 290 if err != nil { 291 t.Fatalf("%d (%s): %v", i, d.sql, err) 292 } 293 if !reflect.DeepEqual(d.primary, schema.PrimaryIndex) { 294 t.Fatalf("%d (%s): primary mismatch: expected %+v, but got %+v", i, d.sql, d.primary, schema.PrimaryIndex) 295 } 296 if !reflect.DeepEqual(d.indexes, append([]sqlbase.IndexDescriptor{}, schema.Indexes...)) { 297 t.Fatalf("%d (%s): index mismatch: expected %+v, but got %+v", i, d.sql, d.indexes, schema.Indexes) 298 } 299 300 } 301 } 302 303 func TestPrimaryKeyUnspecified(t *testing.T) { 304 defer leaktest.AfterTest(t)() 305 s := "CREATE TABLE foo.test (a INT, b INT, CONSTRAINT c UNIQUE (b))" 306 desc, err := CreateTestTableDescriptor(context.Background(), 1, 100, s, sqlbase.NewDefaultPrivilegeDescriptor()) 307 if err != nil { 308 t.Fatal(err) 309 } 310 desc.PrimaryIndex = sqlbase.IndexDescriptor{} 311 312 err = desc.ValidateTable() 313 if !testutils.IsError(err, sqlbase.ErrMissingPrimaryKey.Error()) { 314 t.Fatalf("unexpected error: %v", err) 315 } 316 } 317 318 func TestCanCloneTableWithUDT(t *testing.T) { 319 defer leaktest.AfterTest(t)() 320 321 ctx := context.Background() 322 params, _ := tests.CreateTestServerParams() 323 s, sqlDB, kvDB := serverutils.StartServer(t, params) 324 defer s.Stopper().Stop(ctx) 325 if _, err := sqlDB.Exec(` 326 SET experimental_enable_enums=true; 327 CREATE DATABASE test; 328 CREATE TYPE test.t AS ENUM ('hello'); 329 CREATE TABLE test.tt (x test.t); 330 `); err != nil { 331 t.Fatal(err) 332 } 333 desc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "tt") 334 typLookup := func(id sqlbase.ID) (*tree.TypeName, *sqlbase.TypeDescriptor, error) { 335 typDesc, err := sqlbase.GetTypeDescFromID(ctx, kvDB, keys.SystemSQLCodec, id) 336 if err != nil { 337 return nil, nil, err 338 } 339 return &tree.TypeName{}, typDesc, nil 340 } 341 if err := sqlbase.HydrateTypesInTableDescriptor(desc, typLookup); err != nil { 342 t.Fatal(err) 343 } 344 // Ensure that we can clone this table. 345 _ = protoutil.Clone(desc).(*TableDescriptor) 346 } 347 348 // TestSerializedUDTsInTableDescriptor tests that expressions containing 349 // explicit type references and members of user defined types are serialized 350 // in a way that is stable across changes to the type itself. For example, 351 // we want to ensure that enum members are serialized in a way that is stable 352 // across renames to the member itself. 353 func TestSerializedUDTsInTableDescriptor(t *testing.T) { 354 defer leaktest.AfterTest(t)() 355 356 ctx := context.Background() 357 getDefault := func(desc *TableDescriptor) string { 358 return *desc.Columns[0].DefaultExpr 359 } 360 getComputed := func(desc *TableDescriptor) string { 361 return *desc.Columns[0].ComputeExpr 362 } 363 getCheck := func(desc *TableDescriptor) string { 364 return desc.Checks[0].Expr 365 } 366 testdata := []struct { 367 colSQL string 368 expectedExpr string 369 getExpr func(desc *TableDescriptor) string 370 }{ 371 // Test a simple UDT as the default value. 372 { 373 "x greeting DEFAULT ('hello')", 374 `b'\x80':::@53`, 375 getDefault, 376 }, 377 { 378 "x greeting DEFAULT ('hello':::greeting)", 379 `b'\x80':::@53`, 380 getDefault, 381 }, 382 // Test when a UDT is used in a default value, but isn't the 383 // final type of the column. 384 { 385 "x INT DEFAULT (CASE WHEN 'hello'::greeting = 'hello'::greeting THEN 0 ELSE 1 END)", 386 `CASE WHEN b'\x80':::@53::@53 = b'\x80':::@53::@53 THEN 0:::INT8 ELSE 1:::INT8 END`, 387 getDefault, 388 }, 389 { 390 "x BOOL DEFAULT ('hello'::greeting IS OF (greeting, greeting))", 391 `b'\x80':::@53::@53 IS OF (@53, @53)`, 392 getDefault, 393 }, 394 // Test check constraints. 395 { 396 "x greeting, CHECK (x = 'hello')", 397 `x = b'\x80':::@53`, 398 getCheck, 399 }, 400 { 401 "x greeting, y STRING, CHECK (y::greeting = x)", 402 `y::@53 = x`, 403 getCheck, 404 }, 405 // Test a computed column in the same cases as above. 406 { 407 "x greeting AS ('hello') STORED", 408 `b'\x80':::@53`, 409 getComputed, 410 }, 411 { 412 "x INT AS (CASE WHEN 'hello'::greeting = 'hello'::greeting THEN 0 ELSE 1 END) STORED", 413 `CASE WHEN b'\x80':::@53::@53 = b'\x80':::@53::@53 THEN 0:::INT8 ELSE 1:::INT8 END`, 414 getComputed, 415 }, 416 } 417 418 params, _ := tests.CreateTestServerParams() 419 s, sqlDB, kvDB := serverutils.StartServer(t, params) 420 defer s.Stopper().Stop(ctx) 421 if _, err := sqlDB.Exec(` 422 CREATE DATABASE test; 423 USE test; 424 SET experimental_enable_enums=true; 425 CREATE TYPE greeting AS ENUM ('hello'); 426 `); err != nil { 427 t.Fatal(err) 428 } 429 for _, tc := range testdata { 430 create := "CREATE TABLE t (" + tc.colSQL + ")" 431 if _, err := sqlDB.Exec(create); err != nil { 432 t.Fatal(err) 433 } 434 desc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t") 435 found := tc.getExpr(desc) 436 if tc.expectedExpr != found { 437 t.Errorf("for column %s, found %s, expected %s", tc.colSQL, found, tc.expectedExpr) 438 } 439 if _, err := sqlDB.Exec("DROP TABLE t"); err != nil { 440 t.Fatal(err) 441 } 442 } 443 }