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

     1  // Copyright 2017 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 sampledataccl
    10  
    11  import (
    12  	"bytes"
    13  	"context"
    14  	"fmt"
    15  	"io"
    16  	"io/ioutil"
    17  	"path/filepath"
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/cockroachdb/cockroach/pkg/base"
    22  	"github.com/cockroachdb/cockroach/pkg/ccl/backupccl"
    23  	"github.com/cockroachdb/cockroach/pkg/ccl/importccl"
    24  	"github.com/cockroachdb/cockroach/pkg/ccl/storageccl"
    25  	"github.com/cockroachdb/cockroach/pkg/keys"
    26  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    28  	"github.com/cockroachdb/cockroach/pkg/storage"
    29  	"github.com/cockroachdb/cockroach/pkg/testutils"
    30  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    31  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    32  	"github.com/cockroachdb/cockroach/pkg/workload"
    33  	"github.com/cockroachdb/cockroach/pkg/workload/workloadsql"
    34  	"github.com/cockroachdb/errors"
    35  )
    36  
    37  // ToBackup creates an enterprise backup in `dir`.
    38  func ToBackup(t testing.TB, data workload.Table, dir string) (*Backup, error) {
    39  	return toBackup(t, data, dir, 0)
    40  }
    41  
    42  func toBackup(t testing.TB, data workload.Table, dir string, chunkBytes int64) (*Backup, error) {
    43  	tempDir, dirCleanupFn := testutils.TempDir(t)
    44  	defer dirCleanupFn()
    45  
    46  	// TODO(dan): Get rid of the `t testing.TB` parameter and this `TestServer`.
    47  	ctx := context.Background()
    48  	s, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
    49  	defer s.Stopper().Stop(ctx)
    50  	if _, err := db.Exec(`CREATE DATABASE data`); err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	var stmts bytes.Buffer
    55  	fmt.Fprintf(&stmts, "CREATE TABLE %s %s;\n", data.Name, data.Schema)
    56  	for rowIdx := 0; rowIdx < data.InitialRows.NumBatches; rowIdx++ {
    57  		for _, row := range data.InitialRows.BatchRows(rowIdx) {
    58  			rowBatch := strings.Join(workloadsql.StringTuple(row), `,`)
    59  			fmt.Fprintf(&stmts, "INSERT INTO %s VALUES (%s);\n", data.Name, rowBatch)
    60  		}
    61  	}
    62  
    63  	// TODO(dan): The csv load will be less overhead, use it when we have it.
    64  	ts := hlc.Timestamp{WallTime: hlc.UnixNano()}
    65  	desc, err := importccl.Load(ctx, db, &stmts, `data`, `nodelocal://0/`, ts, chunkBytes, tempDir, dir)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	return &Backup{BaseDir: dir, Desc: desc}, nil
    70  }
    71  
    72  // Backup is a representation of an enterprise BACKUP.
    73  type Backup struct {
    74  	// BaseDir can be used for a RESTORE. All paths in the descriptor are
    75  	// relative to this.
    76  	BaseDir string
    77  	Desc    backupccl.BackupManifest
    78  
    79  	fileIdx int
    80  	iterIdx int
    81  }
    82  
    83  // ResetKeyValueIteration resets the NextKeyValues iteration to the first kv.
    84  func (b *Backup) ResetKeyValueIteration() {
    85  	b.fileIdx = 0
    86  	b.iterIdx = 0
    87  }
    88  
    89  // NextKeyValues iterates and returns every *user table data* key-value in the
    90  // backup. At least `count` kvs will be returned, but rows are not broken up, so
    91  // slightly more than `count` may come back. If fewer than `count` are
    92  // available, err will be `io.EOF` and kvs may be partially filled with the
    93  // remainer.
    94  func (b *Backup) NextKeyValues(
    95  	count int, newTableID sqlbase.ID,
    96  ) ([]storage.MVCCKeyValue, roachpb.Span, error) {
    97  	var userTables []*sqlbase.TableDescriptor
    98  	for _, d := range b.Desc.Descriptors {
    99  		if t := d.Table(hlc.Timestamp{}); t != nil && t.ParentID != keys.SystemDatabaseID {
   100  			userTables = append(userTables, t)
   101  		}
   102  	}
   103  	if len(userTables) != 1 {
   104  		return nil, roachpb.Span{}, errors.Errorf(
   105  			"only backups of one table are currently supported, got %d", len(userTables))
   106  	}
   107  	tableDesc := userTables[0]
   108  
   109  	newDesc := *tableDesc
   110  	newDesc.ID = newTableID
   111  	kr, err := storageccl.MakeKeyRewriter(sqlbase.TablesByID{tableDesc.ID: &newDesc})
   112  	if err != nil {
   113  		return nil, roachpb.Span{}, err
   114  	}
   115  
   116  	var kvs []storage.MVCCKeyValue
   117  	span := roachpb.Span{Key: keys.MaxKey}
   118  	for ; b.fileIdx < len(b.Desc.Files); b.fileIdx++ {
   119  		file := b.Desc.Files[b.fileIdx]
   120  
   121  		sst := storage.MakeRocksDBSstFileReader()
   122  		defer sst.Close()
   123  		fileContents, err := ioutil.ReadFile(filepath.Join(b.BaseDir, file.Path))
   124  		if err != nil {
   125  			return nil, roachpb.Span{}, err
   126  		}
   127  		if err := sst.IngestExternalFile(fileContents); err != nil {
   128  			return nil, roachpb.Span{}, err
   129  		}
   130  
   131  		it := sst.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax})
   132  		defer it.Close()
   133  		it.SeekGE(storage.MVCCKey{Key: file.Span.Key})
   134  
   135  		iterIdx := 0
   136  		for ; ; it.Next() {
   137  			if len(kvs) >= count {
   138  				break
   139  			}
   140  			if iterIdx < b.iterIdx {
   141  				iterIdx++
   142  				continue
   143  			}
   144  
   145  			ok, err := it.Valid()
   146  			if err != nil {
   147  				return nil, roachpb.Span{}, err
   148  			}
   149  			if !ok || it.UnsafeKey().Key.Compare(file.Span.EndKey) >= 0 {
   150  				break
   151  			}
   152  
   153  			if iterIdx < b.iterIdx {
   154  				break
   155  			}
   156  			b.iterIdx = iterIdx
   157  
   158  			key := it.Key()
   159  			key.Key, ok, err = kr.RewriteKey(key.Key, false /* isFromSpan */)
   160  			if err != nil {
   161  				return nil, roachpb.Span{}, err
   162  			}
   163  			if !ok {
   164  				return nil, roachpb.Span{}, errors.Errorf("rewriter did not match key: %s", key.Key)
   165  			}
   166  			v := roachpb.Value{RawBytes: it.Value()}
   167  			v.ClearChecksum()
   168  			v.InitChecksum(key.Key)
   169  			kvs = append(kvs, storage.MVCCKeyValue{Key: key, Value: v.RawBytes})
   170  
   171  			if key.Key.Compare(span.Key) < 0 {
   172  				span.Key = append(span.Key[:0], key.Key...)
   173  			}
   174  			if key.Key.Compare(span.EndKey) > 0 {
   175  				span.EndKey = append(span.EndKey[:0], key.Key...)
   176  			}
   177  			iterIdx++
   178  		}
   179  		b.iterIdx = iterIdx
   180  
   181  		if len(kvs) >= count {
   182  			break
   183  		}
   184  		if ok, _ := it.Valid(); !ok {
   185  			b.iterIdx = 0
   186  		}
   187  	}
   188  
   189  	span.EndKey = span.EndKey.Next()
   190  	if len(kvs) < count {
   191  		return kvs, span, io.EOF
   192  	}
   193  	return kvs, span, nil
   194  }