github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/importccl/client_import_test.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Licensed as a CockroachDB Enterprise file under the Cockroach Community 4 // License (the "License"); you may not use this file except in compliance with 5 // the License. You may obtain a copy of the License at 6 // 7 // https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt 8 9 package importccl_test 10 11 import ( 12 "context" 13 "net/http" 14 "net/http/httptest" 15 "sync" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/base" 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 20 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 21 "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 "github.com/cockroachdb/errors" 24 "github.com/lib/pq" 25 "github.com/stretchr/testify/require" 26 ) 27 28 // TestDropDatabaseCascadeDuringImportsFails ensures that dropping a database 29 // while an IMPORT is ongoing fails with an error. This is critical because 30 // otherwise we may end up with orphaned table descriptors. See #48589 for 31 // more details. 32 func TestDropDatabaseCascadeDuringImportsFails(t *testing.T) { 33 defer leaktest.AfterTest(t)() 34 35 ctx, cancel := context.WithCancel(context.Background()) 36 defer cancel() 37 args := base.TestClusterArgs{} 38 tc := testcluster.StartTestCluster(t, 1, args) 39 defer tc.Stopper().Stop(ctx) 40 41 tc.WaitForNodeLiveness(t) 42 require.NoError(t, tc.WaitForFullReplication()) 43 44 db := tc.ServerConn(0) 45 runner := sqlutils.MakeSQLRunner(db) 46 47 // Use some names that need quoting to ensure that the error quoting is correct. 48 const dbName, tableName = `"fooBarBaz"`, `"foo bar"` 49 runner.Exec(t, `CREATE DATABASE `+dbName) 50 51 mkServer := func(method string, handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server { 52 return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 53 if r.Method == method { 54 handler(w, r) 55 } 56 })) 57 } 58 59 // Let's start an import into this table of ours. 60 allowResponse := make(chan struct{}) 61 var gotRequestOnce sync.Once 62 gotRequest := make(chan struct{}) 63 srv := mkServer("GET", func(w http.ResponseWriter, r *http.Request) { 64 gotRequestOnce.Do(func() { close(gotRequest) }) 65 select { 66 case <-allowResponse: 67 case <-ctx.Done(): // Deal with test failures. 68 } 69 _, _ = w.Write([]byte("1,asdfasdfasdfasdf")) 70 }) 71 defer srv.Close() 72 73 importErrCh := make(chan error, 1) 74 go func() { 75 _, err := db.Exec(`IMPORT TABLE `+dbName+"."+tableName+ 76 ` (k INT, v STRING) CSV DATA ($1)`, srv.URL) 77 importErrCh <- err 78 }() 79 select { 80 case <-gotRequest: 81 case err := <-importErrCh: 82 t.Fatalf("err %v", err) 83 } 84 85 _, err := db.Exec(`DROP DATABASE "fooBarBaz" CASCADE`) 86 require.Regexp(t, `cannot drop a database with OFFLINE tables, ensure `+ 87 dbName+`\.public\.`+tableName+` is dropped or made public before dropping`+ 88 ` database `+dbName, err) 89 pgErr := new(pq.Error) 90 require.True(t, errors.As(err, &pgErr)) 91 require.Equal(t, pgcode.ObjectNotInPrerequisiteState, string(pgErr.Code)) 92 93 close(allowResponse) 94 require.NoError(t, <-importErrCh) 95 runner.Exec(t, `DROP DATABASE `+dbName+` CASCADE`) 96 }