github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/importccl/exportcsv_test.go (about)

     1  // Copyright 2018 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  	"compress/gzip"
    13  	"context"
    14  	"fmt"
    15  	"io"
    16  	"io/ioutil"
    17  	"os"
    18  	"path/filepath"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/cockroachdb/cockroach/pkg/base"
    23  	"github.com/cockroachdb/cockroach/pkg/config"
    24  	"github.com/cockroachdb/cockroach/pkg/config/zonepb"
    25  	"github.com/cockroachdb/cockroach/pkg/keys"
    26  	"github.com/cockroachdb/cockroach/pkg/testutils"
    27  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    28  	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
    29  	"github.com/cockroachdb/cockroach/pkg/testutils/testcluster"
    30  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    31  	"github.com/cockroachdb/cockroach/pkg/workload/bank"
    32  	"github.com/cockroachdb/cockroach/pkg/workload/workloadsql"
    33  	"github.com/gogo/protobuf/proto"
    34  )
    35  
    36  func setupExportableBank(t *testing.T, nodes, rows int) (*sqlutils.SQLRunner, string, func()) {
    37  	ctx := context.Background()
    38  	dir, cleanupDir := testutils.TempDir(t)
    39  
    40  	tc := testcluster.StartTestCluster(t, nodes,
    41  		base.TestClusterArgs{ServerArgs: base.TestServerArgs{ExternalIODir: dir, UseDatabase: "test"}},
    42  	)
    43  	conn := tc.Conns[0]
    44  	db := sqlutils.MakeSQLRunner(conn)
    45  	db.Exec(t, "CREATE DATABASE test")
    46  
    47  	wk := bank.FromRows(rows)
    48  	l := workloadsql.InsertsDataLoader{BatchSize: 100, Concurrency: 3}
    49  	if _, err := workloadsql.Setup(ctx, conn, wk, l); err != nil {
    50  		t.Fatal(err)
    51  	}
    52  
    53  	config.TestingSetupZoneConfigHook(tc.Stopper())
    54  	v, err := tc.Servers[0].DB().Get(context.Background(), keys.SystemSQLCodec.DescIDSequenceKey())
    55  	if err != nil {
    56  		t.Fatal(err)
    57  	}
    58  	last := uint32(v.ValueInt())
    59  	zoneConfig := zonepb.DefaultZoneConfig()
    60  	zoneConfig.RangeMaxBytes = proto.Int64(5000)
    61  	config.TestingSetZoneConfig(last+1, zoneConfig)
    62  	db.Exec(t, "ALTER TABLE bank SCATTER")
    63  	db.Exec(t, "SELECT 'force a scan to repopulate range cache' FROM [SELECT count(*) FROM bank]")
    64  
    65  	return db, dir, func() {
    66  		tc.Stopper().Stop(ctx)
    67  		cleanupDir()
    68  	}
    69  }
    70  
    71  func TestExportImportBank(t *testing.T) {
    72  	defer leaktest.AfterTest(t)()
    73  
    74  	db, dir, cleanup := setupExportableBank(t, 3, 100)
    75  	defer cleanup()
    76  
    77  	// Add some unicode to prove FmtExport works as advertised.
    78  	db.Exec(t, "UPDATE bank SET payload = payload || '✅' WHERE id = 5")
    79  	db.Exec(t, "UPDATE bank SET payload = NULL WHERE id % 2 = 0")
    80  
    81  	chunkSize := 13
    82  	for _, null := range []string{"", "NULL"} {
    83  		nullAs, nullIf := "", ", nullif = ''"
    84  		if null != "" {
    85  			nullAs = fmt.Sprintf(", nullas = '%s'", null)
    86  			nullIf = fmt.Sprintf(", nullif = '%s'", null)
    87  		}
    88  		t.Run("null="+null, func(t *testing.T) {
    89  			var files []string
    90  
    91  			var asOf string
    92  			db.QueryRow(t, "SELECT cluster_logical_timestamp()").Scan(&asOf)
    93  
    94  			for _, row := range db.QueryStr(t,
    95  				fmt.Sprintf(`EXPORT INTO CSV 'nodelocal://0/t'
    96  					WITH chunk_rows = $1, delimiter = '|' %s
    97  					FROM SELECT * FROM bank AS OF SYSTEM TIME %s`, nullAs, asOf), chunkSize,
    98  			) {
    99  				files = append(files, row[0])
   100  				f, err := ioutil.ReadFile(filepath.Join(dir, "t", row[0]))
   101  				if err != nil {
   102  					t.Fatal(err)
   103  				}
   104  				t.Log(string(f))
   105  			}
   106  
   107  			schema := bank.FromRows(1).Tables()[0].Schema
   108  			fileList := "'nodelocal://0/t/" + strings.Join(files, "', 'nodelocal://0/t/") + "'"
   109  			db.Exec(t, fmt.Sprintf(`IMPORT TABLE bank2 %s CSV DATA (%s) WITH delimiter = '|'%s`, schema, fileList, nullIf))
   110  
   111  			db.CheckQueryResults(t,
   112  				fmt.Sprintf(`SELECT * FROM bank AS OF SYSTEM TIME %s ORDER BY id`, asOf), db.QueryStr(t, `SELECT * FROM bank2 ORDER BY id`),
   113  			)
   114  			db.CheckQueryResults(t,
   115  				`SHOW EXPERIMENTAL_FINGERPRINTS FROM TABLE bank2`, db.QueryStr(t, `SHOW EXPERIMENTAL_FINGERPRINTS FROM TABLE bank`),
   116  			)
   117  			db.Exec(t, "DROP TABLE bank2")
   118  		})
   119  	}
   120  }
   121  
   122  func TestMultiNodeExportStmt(t *testing.T) {
   123  	defer leaktest.AfterTest(t)()
   124  
   125  	nodes := 5
   126  	exportRows := 100
   127  	db, _, cleanup := setupExportableBank(t, nodes, exportRows*2)
   128  	defer cleanup()
   129  
   130  	maxTries := 10
   131  	// we might need to retry if our table didn't actually scatter enough.
   132  	for tries := 0; tries < maxTries; tries++ {
   133  		chunkSize := 13
   134  		rows := db.Query(t,
   135  			`EXPORT INTO CSV 'nodelocal://0/t' WITH chunk_rows = $3 FROM SELECT * FROM bank WHERE id >= $1 and id < $2`,
   136  			10, 10+exportRows, chunkSize,
   137  		)
   138  
   139  		files, totalRows, totalBytes := 0, 0, 0
   140  		nodesSeen := make(map[string]bool)
   141  		for rows.Next() {
   142  			filename, count, bytes := "", 0, 0
   143  			if err := rows.Scan(&filename, &count, &bytes); err != nil {
   144  				t.Fatal(err)
   145  			}
   146  			files++
   147  			if count > chunkSize {
   148  				t.Fatalf("expected no chunk larger than %d, got %d", chunkSize, count)
   149  			}
   150  			totalRows += count
   151  			totalBytes += bytes
   152  			nodesSeen[strings.SplitN(filename, ".", 2)[0]] = true
   153  		}
   154  		if totalRows != exportRows {
   155  			t.Fatalf("Expected %d rows, got %d", exportRows, totalRows)
   156  		}
   157  		if expected := exportRows / chunkSize; files < expected {
   158  			t.Fatalf("expected at least %d files, got %d", expected, files)
   159  		}
   160  		if len(nodesSeen) < 2 {
   161  			// table isn't as scattered as we expected, but we can try again.
   162  			if tries < maxTries {
   163  				continue
   164  			}
   165  			t.Fatalf("expected files from %d nodes, got %d: %v", 2, len(nodesSeen), nodesSeen)
   166  		}
   167  		break
   168  	}
   169  }
   170  
   171  func TestExportJoin(t *testing.T) {
   172  	defer leaktest.AfterTest(t)()
   173  	dir, cleanupDir := testutils.TempDir(t)
   174  	defer cleanupDir()
   175  
   176  	srv, db, _ := serverutils.StartServer(t, base.TestServerArgs{ExternalIODir: dir})
   177  	defer srv.Stopper().Stop(context.Background())
   178  	sqlDB := sqlutils.MakeSQLRunner(db)
   179  
   180  	sqlDB.Exec(t, `CREATE TABLE t AS VALUES (1, 2)`)
   181  	sqlDB.Exec(t, `EXPORT INTO CSV 'nodelocal://0/join' FROM SELECT * FROM t, t as u`)
   182  }
   183  
   184  func TestExportOrder(t *testing.T) {
   185  	defer leaktest.AfterTest(t)()
   186  	dir, cleanupDir := testutils.TempDir(t)
   187  	defer cleanupDir()
   188  
   189  	srv, db, _ := serverutils.StartServer(t, base.TestServerArgs{ExternalIODir: dir})
   190  	defer srv.Stopper().Stop(context.Background())
   191  	sqlDB := sqlutils.MakeSQLRunner(db)
   192  
   193  	sqlDB.Exec(t, `create table foo (i int primary key, x int, y int, z int, index (y))`)
   194  	sqlDB.Exec(t, `insert into foo values (1, 12, 3, 14), (2, 22, 2, 24), (3, 32, 1, 34)`)
   195  
   196  	sqlDB.Exec(t, `EXPORT INTO CSV 'nodelocal://0/order' from select * from foo order by y asc limit 2`)
   197  	content, err := ioutil.ReadFile(filepath.Join(dir, "order", "n1.0.csv"))
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  	if expected, got := "3,32,1,34\n2,22,2,24\n", string(content); expected != got {
   202  		t.Fatalf("expected %q, got %q", expected, got)
   203  	}
   204  }
   205  
   206  func TestExportOrderCompressed(t *testing.T) {
   207  	defer leaktest.AfterTest(t)()
   208  	dir, cleanupDir := testutils.TempDir(t)
   209  	defer cleanupDir()
   210  
   211  	var close = func(c io.Closer) {
   212  		if err := c.Close(); err != nil {
   213  			t.Fatalf("failed to close stream, got error %s", err)
   214  		}
   215  	}
   216  
   217  	srv, db, _ := serverutils.StartServer(t, base.TestServerArgs{ExternalIODir: dir})
   218  	defer srv.Stopper().Stop(context.Background())
   219  	sqlDB := sqlutils.MakeSQLRunner(db)
   220  
   221  	sqlDB.Exec(t, `create table foo (i int primary key, x int, y int, z int, index (y))`)
   222  	sqlDB.Exec(t, `insert into foo values (1, 12, 3, 14), (2, 22, 2, 24), (3, 32, 1, 34)`)
   223  
   224  	sqlDB.Exec(t, `EXPORT INTO CSV 'nodelocal://0/order' with compression = gzip from select * from foo order by y asc limit 2`)
   225  	fi, err := os.Open(filepath.Join(dir, "order", "n1.0.csv.gz"))
   226  	defer close(fi)
   227  
   228  	if err != nil {
   229  		t.Fatal(err)
   230  	}
   231  
   232  	gzipReader, err := gzip.NewReader(fi)
   233  	defer close(gzipReader)
   234  
   235  	if err != nil {
   236  		t.Fatal(err)
   237  	}
   238  
   239  	content, err := ioutil.ReadAll(gzipReader)
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  
   244  	if expected, got := "3,32,1,34\n2,22,2,24\n", string(content); expected != got {
   245  		t.Fatalf("expected %q, got %q", expected, got)
   246  	}
   247  }
   248  
   249  func TestExportShow(t *testing.T) {
   250  	defer leaktest.AfterTest(t)()
   251  	dir, cleanupDir := testutils.TempDir(t)
   252  	defer cleanupDir()
   253  
   254  	srv, db, _ := serverutils.StartServer(t, base.TestServerArgs{ExternalIODir: dir})
   255  	defer srv.Stopper().Stop(context.Background())
   256  	sqlDB := sqlutils.MakeSQLRunner(db)
   257  
   258  	sqlDB.Exec(t, `EXPORT INTO CSV 'nodelocal://0/show' FROM SELECT * FROM [SHOW DATABASES] ORDER BY database_name`)
   259  	content, err := ioutil.ReadFile(filepath.Join(dir, "show", "n1.0.csv"))
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  	if expected, got := "defaultdb\npostgres\nsystem\n", string(content); expected != got {
   264  		t.Fatalf("expected %q, got %q", expected, got)
   265  	}
   266  }
   267  
   268  // TestExportVectorized makes sure that SupportsVectorized check doesn't panic
   269  // on CSVWriter processor.
   270  func TestExportVectorized(t *testing.T) {
   271  	defer leaktest.AfterTest(t)()
   272  	dir, cleanupDir := testutils.TempDir(t)
   273  	defer cleanupDir()
   274  
   275  	srv, db, _ := serverutils.StartServer(t, base.TestServerArgs{ExternalIODir: dir})
   276  	defer srv.Stopper().Stop(context.Background())
   277  	sqlDB := sqlutils.MakeSQLRunner(db)
   278  
   279  	sqlDB.Exec(t, `CREATE TABLE t(a INT PRIMARY KEY)`)
   280  	sqlDB.Exec(t, `SET vectorize_row_count_threshold=0`)
   281  	sqlDB.Exec(t, `EXPORT INTO CSV 'http://0.1:37957/exp_1' FROM TABLE t`)
   282  }