github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cli/dump.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 cli
    12  
    13  import (
    14  	"context"
    15  	"database/sql/driver"
    16  	"fmt"
    17  	"io"
    18  	"os"
    19  	"sort"
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/cockroachdb/cockroach/pkg/cli/cliflags"
    24  	"github.com/cockroachdb/cockroach/pkg/keys"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/lex"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/parser"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    28  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    29  	"github.com/cockroachdb/cockroach/pkg/util/ctxgroup"
    30  	"github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented"
    31  	"github.com/cockroachdb/cockroach/pkg/util/timeofday"
    32  	"github.com/cockroachdb/cockroach/pkg/util/timetz"
    33  	"github.com/cockroachdb/cockroach/pkg/util/version"
    34  	"github.com/cockroachdb/errors"
    35  	"github.com/spf13/cobra"
    36  )
    37  
    38  // dumpCmd dumps SQL tables.
    39  var dumpCmd = &cobra.Command{
    40  	Use:   "dump [options] <database> [<table> [<table>...]]",
    41  	Short: "dump sql tables\n",
    42  	Long: `
    43  Dump SQL tables of a cockroach database. If the table name
    44  is omitted, dump all tables in the database.
    45  `,
    46  	RunE: MaybeDecorateGRPCError(runDump),
    47  }
    48  
    49  // We accept versions that are strictly newer than v2.1.0-alpha.20180416
    50  // (hence the "-0" at the end).
    51  var verDump = version.MustParse("v2.1.0-alpha.20180416-0")
    52  
    53  // databasesNamesExtractor extracts list of available databases to dump
    54  func databasesNamesExtractor(conn *sqlConn) ([]string, error) {
    55  	var dbNames []string
    56  
    57  	maxReservedDescID, err := driver.Int32.ConvertValue(keys.MaxReservedDescID)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	rows, err := conn.Query(`SELECT name FROM system.namespace WHERE id > $1 AND "parentID" = 0`, []driver.Value{maxReservedDescID})
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	vals := make([]driver.Value, 1)
    68  	for {
    69  		if err := rows.Next(vals); err == io.EOF {
    70  			break
    71  		}
    72  
    73  		if name, ok := vals[0].(string); ok {
    74  			dbNames = append(dbNames, name)
    75  		} else {
    76  			return nil, fmt.Errorf("unexpected value: %T", name)
    77  		}
    78  	}
    79  
    80  	// sort to get deterministic output of ordered database names
    81  	sort.Strings(dbNames)
    82  
    83  	return dbNames, nil
    84  }
    85  
    86  // runDumps performs a dump of a table or database.
    87  //
    88  // The approach here and its current flaws are summarized
    89  // in https://github.com/cockroachdb/cockroach/issues/28948.
    90  func runDump(cmd *cobra.Command, args []string) error {
    91  	conn, err := makeSQLClient("cockroach dump", useDefaultDb)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	defer conn.Close()
    96  
    97  	if err := conn.requireServerVersion(verDump); err != nil {
    98  		return err
    99  	}
   100  
   101  	var dbNames []string
   102  	if dumpCtx.dumpAll && len(args) != 0 {
   103  		return fmt.Errorf("cannot specify --%s and a specific database at the same time", cliflags.DumpAll.Name)
   104  	}
   105  
   106  	if len(args) != 0 {
   107  		dbNames = append(dbNames, args[0])
   108  	} else if dumpCtx.dumpAll {
   109  		dbNames, err = databasesNamesExtractor(conn)
   110  		if err != nil {
   111  			return err
   112  		}
   113  	}
   114  
   115  	var tableNames []string
   116  	if len(args) > 1 {
   117  		tableNames = args[1:]
   118  	}
   119  
   120  	var fullMds []basicMetadata
   121  	w := os.Stdout
   122  
   123  	for _, dbName := range dbNames {
   124  		mds, err := getDumpMetadata(conn, dbName, tableNames, dumpCtx.asOf)
   125  		if err != nil {
   126  			return err
   127  		}
   128  
   129  		if len(mds) == 0 {
   130  			continue
   131  		}
   132  
   133  		byID := make(map[int64]basicMetadata)
   134  		for _, md := range mds {
   135  			byID[md.ID] = md
   136  		}
   137  
   138  		// First sort by name to guarantee stable output.
   139  		sort.Slice(mds, func(i, j int) bool {
   140  			return mds[i].name.String() < mds[j].name.String()
   141  		})
   142  
   143  		// Collect transitive dependencies in topological order into collected.
   144  		// The topological order is essential here since it captures dependencies
   145  		// for views and sequences creation, hence simple alphabetical sort won't
   146  		// be enough.
   147  		var collected []int64
   148  		seen := make(map[int64]bool)
   149  		for _, md := range mds {
   150  			collect(md.ID, byID, seen, &collected)
   151  		}
   152  		// collectOrder maps a table ID to its collection index. This is needed
   153  		// instead of just using range over collected because collected may contain
   154  		// table IDs not present in the dump spec. It is simpler to sort mds correctly
   155  		// to skip over these referenced-but-not-dumped tables.
   156  		collectOrder := make(map[int64]int)
   157  		for i, id := range collected {
   158  			collectOrder[id] = i
   159  		}
   160  
   161  		// Second sort dumped tables by dependency order.
   162  		sort.SliceStable(mds, func(i, j int) bool {
   163  			return collectOrder[mds[i].ID] < collectOrder[mds[j].ID]
   164  		})
   165  
   166  		if dumpCtx.dumpAll && dumpCtx.dumpMode != dumpDataOnly {
   167  			if _, err := fmt.Fprintf(w, "\nCREATE DATABASE IF NOT EXISTS %s;\nUSE %s;\n\n", dbName, dbName); err != nil {
   168  				return err
   169  			}
   170  		}
   171  
   172  		if dumpCtx.dumpMode != dumpDataOnly {
   173  			for i, md := range mds {
   174  				if i > 0 {
   175  					fmt.Fprintln(w)
   176  				}
   177  				if err := dumpCreateTable(w, md); err != nil {
   178  					return err
   179  				}
   180  			}
   181  		}
   182  		if dumpCtx.dumpMode != dumpSchemaOnly {
   183  			for _, md := range mds {
   184  				switch md.kind {
   185  				case "table":
   186  					if err := dumpTableData(w, conn, md); err != nil {
   187  						return err
   188  					}
   189  				case "sequence":
   190  					if err := dumpSequenceData(w, conn, md); err != nil {
   191  						return err
   192  					}
   193  				case "view":
   194  					continue
   195  				default:
   196  					panic("unknown descriptor type: " + md.kind)
   197  				}
   198  			}
   199  		}
   200  		fullMds = append(fullMds, mds...)
   201  	}
   202  
   203  	// Put FK ALTERs at the end.
   204  	if dumpCtx.dumpMode != dumpDataOnly {
   205  		hasRefs := false
   206  		for _, md := range fullMds {
   207  			for _, alter := range md.alter {
   208  				if !hasRefs {
   209  					hasRefs = true
   210  					if _, err := w.Write([]byte("\n")); err != nil {
   211  						return err
   212  					}
   213  				}
   214  				fmt.Fprintf(w, "%s;\n", alter)
   215  			}
   216  		}
   217  		if hasRefs {
   218  			const alterValidateMessage = `-- Validate foreign key constraints. These can fail if there was unvalidated data during the dump.`
   219  			if _, err := w.Write([]byte("\n" + alterValidateMessage + "\n")); err != nil {
   220  				return err
   221  			}
   222  			for _, md := range fullMds {
   223  				for _, validate := range md.validate {
   224  					fmt.Fprintf(w, "%s;\n", validate)
   225  				}
   226  			}
   227  		}
   228  	}
   229  	return nil
   230  }
   231  
   232  func collect(tid int64, byID map[int64]basicMetadata, seen map[int64]bool, collected *[]int64) {
   233  	// has this table already been collected previously?
   234  	if seen[tid] {
   235  		return
   236  	}
   237  	// no: mark it as seen.
   238  	seen[tid] = true
   239  	for _, dep := range byID[tid].dependsOn {
   240  		// depth-first collection of dependencies
   241  		collect(dep, byID, seen, collected)
   242  	}
   243  	// Only add it after its dependencies.
   244  	*collected = append(*collected, tid)
   245  }
   246  
   247  type basicMetadata struct {
   248  	ID         int64
   249  	name       *tree.TableName
   250  	createStmt string
   251  	dependsOn  []int64
   252  	kind       string // "string", "table", or "view"
   253  	alter      []string
   254  	validate   []string
   255  	ts         string
   256  }
   257  
   258  // tableMetadata describes one table to dump.
   259  type tableMetadata struct {
   260  	basicMetadata
   261  
   262  	columnNames string
   263  	columnTypes map[string]*types.T
   264  }
   265  
   266  // getDumpMetadata retrieves the table information for the specified table(s).
   267  // It also retrieves the cluster timestamp at which the metadata was
   268  // retrieved.
   269  func getDumpMetadata(
   270  	conn *sqlConn, dbName string, tableNames []string, asOf string,
   271  ) (mds []basicMetadata, err error) {
   272  	var clusterTS string
   273  	if asOf == "" {
   274  		vals, err := conn.QueryRow("SELECT cluster_logical_timestamp()", nil)
   275  		if err != nil {
   276  			return nil, err
   277  		}
   278  		clusterTS = string(vals[0].([]byte))
   279  	} else {
   280  		// Validate the timestamp. This prevents SQL injection.
   281  		if _, err := tree.ParseDTimestamp(nil, asOf, time.Nanosecond); err != nil {
   282  			return nil, err
   283  		}
   284  		clusterTS = asOf
   285  	}
   286  
   287  	if tableNames == nil {
   288  		tableNames, err = getTableNames(conn, dbName, clusterTS)
   289  		if err != nil {
   290  			return nil, err
   291  		}
   292  	}
   293  
   294  	mds = make([]basicMetadata, len(tableNames))
   295  	for i, tableName := range tableNames {
   296  		basicMD, err := getBasicMetadata(conn, dbName, tableName, clusterTS)
   297  		if err != nil {
   298  			return nil, err
   299  		}
   300  		mds[i] = basicMD
   301  	}
   302  
   303  	return mds, nil
   304  }
   305  
   306  // getTableNames retrieves all tables names in the given database.
   307  func getTableNames(conn *sqlConn, dbName string, ts string) (tableNames []string, err error) {
   308  	rows, err := conn.Query(fmt.Sprintf(`
   309  		SELECT descriptor_name
   310  		FROM "".crdb_internal.create_statements
   311  		AS OF SYSTEM TIME %s
   312  		WHERE database_name = $1
   313  		`, lex.EscapeSQLString(ts)), []driver.Value{dbName})
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  
   318  	vals := make([]driver.Value, 1)
   319  	for {
   320  		if err := rows.Next(vals); err == io.EOF {
   321  			break
   322  		} else if err != nil {
   323  			return nil, err
   324  		}
   325  		nameI := vals[0]
   326  		name, ok := nameI.(string)
   327  		if !ok {
   328  			return nil, fmt.Errorf("unexpected value: %T", nameI)
   329  		}
   330  		tableNames = append(tableNames, name)
   331  	}
   332  
   333  	if err := rows.Close(); err != nil {
   334  		return nil, err
   335  	}
   336  
   337  	return tableNames, nil
   338  }
   339  
   340  func getBasicMetadata(conn *sqlConn, dbName, tableName string, ts string) (basicMetadata, error) {
   341  	name := tree.NewTableName(tree.Name(dbName), tree.Name(tableName))
   342  
   343  	// Fetch table ID.
   344  	dbNameEscaped := tree.NameString(dbName)
   345  	vals, err := conn.QueryRow(fmt.Sprintf(`
   346  		SELECT
   347  			descriptor_id,
   348  			create_nofks,
   349  			descriptor_type,
   350  			alter_statements,
   351  			validate_statements
   352  		FROM %s.crdb_internal.create_statements
   353  		AS OF SYSTEM TIME %s
   354  		WHERE database_name = $1
   355  			AND descriptor_name = $2
   356  	`, dbNameEscaped, lex.EscapeSQLString(ts)), []driver.Value{dbName, tableName})
   357  	if err != nil {
   358  		if err == io.EOF {
   359  			return basicMetadata{}, errors.Wrap(
   360  				errors.Errorf("relation %s does not exist", tree.ErrString(name)),
   361  				"getBasicMetadata",
   362  			)
   363  		}
   364  		return basicMetadata{}, errors.Wrap(err, "getBasicMetadata")
   365  	}
   366  	idI := vals[0]
   367  	id, ok := idI.(int64)
   368  	if !ok {
   369  		return basicMetadata{}, fmt.Errorf("unexpected value: %T", idI)
   370  	}
   371  	createStatementI := vals[1]
   372  	createStatement, ok := createStatementI.(string)
   373  	if !ok {
   374  		return basicMetadata{}, fmt.Errorf("unexpected value: %T", createStatementI)
   375  	}
   376  	kindI := vals[2]
   377  	kind, ok := kindI.(string)
   378  	if !ok {
   379  		return basicMetadata{}, fmt.Errorf("unexpected value: %T", kindI)
   380  	}
   381  	alterStatements, err := extractArray(vals[3])
   382  	if err != nil {
   383  		return basicMetadata{}, err
   384  	}
   385  	validateStatements, err := extractArray(vals[4])
   386  	if err != nil {
   387  		return basicMetadata{}, err
   388  	}
   389  
   390  	// Get dependencies.
   391  	rows, err := conn.Query(fmt.Sprintf(`
   392  		SELECT dependson_id
   393  		FROM %s.crdb_internal.backward_dependencies
   394  		AS OF SYSTEM TIME %s
   395  		WHERE descriptor_id = $1
   396  		`, dbNameEscaped, lex.EscapeSQLString(ts)), []driver.Value{id})
   397  	if err != nil {
   398  		return basicMetadata{}, err
   399  	}
   400  	vals = make([]driver.Value, 1)
   401  
   402  	var refs []int64
   403  	for {
   404  		if err := rows.Next(vals); err == io.EOF {
   405  			break
   406  		} else if err != nil {
   407  			return basicMetadata{}, err
   408  		}
   409  		id := vals[0].(int64)
   410  		refs = append(refs, id)
   411  	}
   412  	if err := rows.Close(); err != nil {
   413  		return basicMetadata{}, err
   414  	}
   415  
   416  	md := basicMetadata{
   417  		ID:         id,
   418  		name:       tree.NewTableName(tree.Name(dbName), tree.Name(tableName)),
   419  		createStmt: createStatement,
   420  		dependsOn:  refs,
   421  		kind:       kind,
   422  		alter:      alterStatements,
   423  		validate:   validateStatements,
   424  		ts:         ts,
   425  	}
   426  
   427  	return md, nil
   428  }
   429  
   430  func extractArray(val interface{}) ([]string, error) {
   431  	b, ok := val.([]byte)
   432  	if !ok {
   433  		return nil, fmt.Errorf("unexpected value: %T", b)
   434  	}
   435  	arr, err := tree.ParseDArrayFromString(tree.NewTestingEvalContext(serverCfg.Settings), string(b), types.String)
   436  	if err != nil {
   437  		return nil, err
   438  	}
   439  	res := make([]string, len(arr.Array))
   440  	for i, v := range arr.Array {
   441  		res[i] = string(*v.(*tree.DString))
   442  	}
   443  	return res, nil
   444  }
   445  
   446  func makeMetadataQuery(md basicMetadata, columnName string) string {
   447  	// This query is parameterized by the column name because of
   448  	// 2.0/2.1beta/2.1 trans-version compatibility requirements.  See
   449  	// below for details.
   450  	return fmt.Sprintf(`
   451  		SELECT COLUMN_NAME, %s
   452  		FROM %s.information_schema.columns
   453  		AS OF SYSTEM TIME %s
   454  		WHERE TABLE_CATALOG = $1
   455  			AND TABLE_SCHEMA = $2
   456  			AND TABLE_NAME = $3
   457  			AND GENERATION_EXPRESSION = ''
   458  		`, columnName, &md.name.CatalogName, lex.EscapeSQLString(md.ts))
   459  }
   460  
   461  func fetchColumnsNamesAndTypes(conn *sqlConn, md basicMetadata, noHidden bool) (*sqlRows, error) {
   462  	query := makeMetadataQuery(md, "CRDB_SQL_TYPE")
   463  	if noHidden {
   464  		query = query + ` AND IS_HIDDEN = 'NO'`
   465  	}
   466  	rows, err := conn.Query(query,
   467  		[]driver.Value{md.name.Catalog(), md.name.Schema(), md.name.Table()})
   468  	if err != nil {
   469  		// IS_HIDDEN was introduced in the first 2.1 beta. CRDB_SQL_TYPE
   470  		// some time after that.  To ensure `cockroach dump` works across
   471  		// versions we must try the previous forms if the first form
   472  		// fails.
   473  		//
   474  		// TODO(knz): Remove this fallback logic post-2.2.
   475  		if strings.Contains(err.Error(), "column \"crdb_sql_type\" does not exist") {
   476  			// Pre-2.1 CRDB_SQL_HIDDEN did not exist in
   477  			// information_schema.columns. When it does not exist,
   478  			// information_schema.columns.data_type contains a usable SQL
   479  			// type name instead. Use that.
   480  			query := makeMetadataQuery(md, "DATA_TYPE")
   481  			if noHidden {
   482  				query = query + ` AND IS_HIDDEN = 'NO'`
   483  			}
   484  			rows, err = conn.Query(query,
   485  				[]driver.Value{md.name.Catalog(), md.name.Schema(), md.name.Table()})
   486  		}
   487  		if strings.Contains(err.Error(), "column \"is_hidden\" does not exist") {
   488  			// Pre-2.1 IS_HIDDEN did not exist in information_schema.columns.
   489  			// When it does not exist, information_schema.columns only returns
   490  			// non-hidden columns so we can still use that.
   491  			rows, err = conn.Query(makeMetadataQuery(md, "DATA_TYPE"),
   492  				[]driver.Value{md.name.Catalog(), md.name.Schema(), md.name.Table()})
   493  		}
   494  		if err != nil {
   495  			return nil, err
   496  		}
   497  	}
   498  	return rows, err
   499  }
   500  
   501  func constructTableMetadata(rows *sqlRows, md basicMetadata) (tableMetadata, error) {
   502  	vals := make([]driver.Value, 2)
   503  	coltypes := make(map[string]*types.T)
   504  	colnames := tree.NewFmtCtx(tree.FmtSimple)
   505  	defer colnames.Close()
   506  	for {
   507  		if err := rows.Next(vals); err == io.EOF {
   508  			break
   509  		} else if err != nil {
   510  			return tableMetadata{}, err
   511  		}
   512  		nameI, typI := vals[0], vals[1]
   513  		name, ok := nameI.(string)
   514  		if !ok {
   515  			return tableMetadata{}, fmt.Errorf("unexpected value: %T", nameI)
   516  		}
   517  		typ, ok := typI.(string)
   518  		if !ok {
   519  			return tableMetadata{}, fmt.Errorf("unexpected value: %T", typI)
   520  		}
   521  
   522  		// Transform the type name to an internal coltype.
   523  		sql := fmt.Sprintf("CREATE TABLE woo (x %s)", typ)
   524  		stmt, err := parser.ParseOne(sql)
   525  		if err != nil {
   526  			return tableMetadata{}, fmt.Errorf("type %s is not a valid CockroachDB type", typ)
   527  		}
   528  		ref := stmt.AST.(*tree.CreateTable).Defs[0].(*tree.ColumnTableDef).Type
   529  
   530  		coltyp, ok := tree.GetStaticallyKnownType(ref)
   531  		if !ok {
   532  			return tableMetadata{}, unimplemented.NewWithIssue(47765, "user defined types are unsupported")
   533  		}
   534  		coltypes[name] = coltyp
   535  		if colnames.Len() > 0 {
   536  			colnames.WriteString(", ")
   537  		}
   538  		colnames.FormatName(name)
   539  	}
   540  	if err := rows.Close(); err != nil {
   541  		return tableMetadata{}, err
   542  	}
   543  
   544  	return tableMetadata{
   545  		basicMetadata: md,
   546  
   547  		columnNames: colnames.String(),
   548  		columnTypes: coltypes,
   549  	}, nil
   550  }
   551  
   552  func getMetadataForTable(conn *sqlConn, md basicMetadata) (tableMetadata, error) {
   553  	// Fetch column types.
   554  	//
   555  	// TODO(knz): this approach is flawed, see #28948.
   556  
   557  	rows, err := fetchColumnsNamesAndTypes(conn, md, true)
   558  	if err != nil {
   559  		return tableMetadata{}, err
   560  	}
   561  
   562  	metadata, err := constructTableMetadata(rows, md)
   563  	if err != nil {
   564  		return tableMetadata{}, err
   565  	}
   566  
   567  	if len(metadata.columnNames) == 0 {
   568  		rows, err := fetchColumnsNamesAndTypes(conn, md, false)
   569  		if err != nil {
   570  			return tableMetadata{}, err
   571  		}
   572  
   573  		return constructTableMetadata(rows, md)
   574  	}
   575  	return metadata, err
   576  }
   577  
   578  // dumpCreateTable dumps the CREATE statement of the specified table to w.
   579  func dumpCreateTable(w io.Writer, md basicMetadata) error {
   580  	if _, err := w.Write([]byte(md.createStmt)); err != nil {
   581  		return err
   582  	}
   583  	if _, err := w.Write([]byte(";\n")); err != nil {
   584  		return err
   585  	}
   586  	return nil
   587  }
   588  
   589  const (
   590  	// insertRows is the number of rows per INSERT statement.
   591  	insertRows = 100
   592  )
   593  
   594  func dumpSequenceData(w io.Writer, conn *sqlConn, bmd basicMetadata) error {
   595  	// Get sequence value.
   596  	vals, err := conn.QueryRow(fmt.Sprintf(
   597  		"SELECT last_value FROM %s AS OF SYSTEM TIME %s",
   598  		bmd.name, lex.EscapeSQLString(bmd.ts),
   599  	), nil)
   600  	if err != nil {
   601  		return err
   602  	}
   603  	seqVal := vals[0].(int64)
   604  
   605  	// Get sequence increment.
   606  	// TODO(knz,vilterp): This could use a shortcut via crdb_internal.
   607  	vals2, err := conn.QueryRow(fmt.Sprintf(
   608  		`SELECT inc
   609         FROM (SELECT s.seqincrement AS inc
   610                 FROM %[1]s.pg_catalog.pg_namespace n, %[1]s.pg_catalog.pg_class c, %[1]s.pg_catalog.pg_sequence s
   611                WHERE n.nspname = %[2]s
   612                  AND n.oid = c.relnamespace
   613                  AND c.relname = %[3]s
   614                  AND c.oid = s.seqrelid)
   615       AS OF SYSTEM TIME %[4]s`,
   616  		&bmd.name.CatalogName,
   617  		lex.EscapeSQLString(bmd.name.Schema()),
   618  		lex.EscapeSQLString(bmd.name.Table()),
   619  		lex.EscapeSQLString(bmd.ts),
   620  	), nil)
   621  	if err != nil {
   622  		return err
   623  	}
   624  	seqInc := vals2[0].(int64)
   625  
   626  	fmt.Fprintln(w)
   627  
   628  	// Dump `setval(name, val + inc, false)`. This will cause the value to be
   629  	// set to `(val + inc) - inc = val`, so that the next value given out by the
   630  	// sequence will be `val`. This also avoids the minval check -- a sequence with
   631  	// a minval of 1 will have its value saved in KV as 0, so that the next value
   632  	// given out is 1.
   633  	fmt.Fprintf(
   634  		w, "SELECT setval(%s, %d, false);\n",
   635  		lex.EscapeSQLString(tree.NameString(bmd.name.Table())), seqVal+seqInc,
   636  	)
   637  
   638  	return nil
   639  }
   640  
   641  // dumpTableData dumps the data of the specified table to w.
   642  func dumpTableData(w io.Writer, conn *sqlConn, bmd basicMetadata) error {
   643  	md, err := getMetadataForTable(conn, bmd)
   644  	if err != nil {
   645  		return err
   646  	}
   647  	var collationEnv tree.CollationEnvironment
   648  	bs := fmt.Sprintf("SELECT %s FROM %s AS OF SYSTEM TIME %s ORDER BY PRIMARY KEY %[2]s",
   649  		md.columnNames,
   650  		md.name,
   651  		lex.EscapeSQLString(md.ts),
   652  	)
   653  	inserts := make([]string, 0, insertRows)
   654  	rows, err := conn.Query(bs, nil)
   655  	if err != nil {
   656  		return err
   657  	}
   658  	cols := rows.Columns()
   659  	// Make 2 []driver.Values and alternate sending them on the chan. This is
   660  	// needed so val encoding can proceed at the same time as fetching a new
   661  	// row. There's no benefit to having more than 2 because that's all we can
   662  	// encode at once if we want to preserve the select order.
   663  	var valArray [2][]driver.Value
   664  	for i := range valArray {
   665  		valArray[i] = make([]driver.Value, len(cols))
   666  	}
   667  	g := ctxgroup.WithContext(context.Background())
   668  	valsCh := make(chan []driver.Value)
   669  	// stringsCh receives VALUES lines and batches them before writing to the
   670  	// output. Buffering this chan allows the val encoding to proceed during
   671  	// writes.
   672  	stringsCh := make(chan string, insertRows)
   673  
   674  	g.GoCtx(func(ctx context.Context) error {
   675  		// Fetch SQL rows and put them onto valsCh.
   676  		defer close(valsCh)
   677  		done := ctx.Done()
   678  		for i := 0; ; i++ {
   679  			vals := valArray[i%len(valArray)]
   680  			if err := rows.Next(vals); err == io.EOF {
   681  				return rows.Close()
   682  			} else if err != nil {
   683  				return err
   684  			}
   685  			select {
   686  			case <-done:
   687  				return ctx.Err()
   688  			case valsCh <- vals:
   689  			}
   690  		}
   691  	})
   692  	g.GoCtx(func(ctx context.Context) error {
   693  		// Convert SQL rows into VALUE strings.
   694  		defer close(stringsCh)
   695  		f := tree.NewFmtCtx(tree.FmtParsableNumerics)
   696  		defer f.Close()
   697  		done := ctx.Done()
   698  		for vals := range valsCh {
   699  			f.Reset()
   700  			// Values need to be correctly encoded for INSERT statements in a text file.
   701  			for si, sv := range vals {
   702  				if si > 0 {
   703  					f.WriteString(", ")
   704  				}
   705  				var d tree.Datum
   706  				// TODO(knz): this approach is brittle+flawed, see #28948.
   707  				// TODO(mjibson): can we use tree.ParseDatumStringAs here?
   708  				switch t := sv.(type) {
   709  				case nil:
   710  					d = tree.DNull
   711  				case bool:
   712  					d = tree.MakeDBool(tree.DBool(t))
   713  				case int64:
   714  					d = tree.NewDInt(tree.DInt(t))
   715  				case float64:
   716  					d = tree.NewDFloat(tree.DFloat(t))
   717  				case string:
   718  					switch ct := md.columnTypes[cols[si]]; ct.Family() {
   719  					case types.StringFamily:
   720  						d = tree.NewDString(t)
   721  					case types.CollatedStringFamily:
   722  						d, err = tree.NewDCollatedString(t, ct.Locale(), &collationEnv)
   723  						if err != nil {
   724  							return err
   725  						}
   726  					default:
   727  						return errors.AssertionFailedf("unknown string type %s", ct)
   728  					}
   729  				case []byte:
   730  					// TODO(knz): this approach is brittle+flawed, see #28948.
   731  					switch ct := md.columnTypes[cols[si]]; ct.Family() {
   732  					case types.IntervalFamily:
   733  						d, err = tree.ParseDInterval(string(t))
   734  						if err != nil {
   735  							return err
   736  						}
   737  					case types.BytesFamily:
   738  						d = tree.NewDBytes(tree.DBytes(t))
   739  					case types.UuidFamily:
   740  						d, err = tree.ParseDUuidFromString(string(t))
   741  						if err != nil {
   742  							return err
   743  						}
   744  					case types.INetFamily:
   745  						d, err = tree.ParseDIPAddrFromINetString(string(t))
   746  						if err != nil {
   747  							return err
   748  						}
   749  					case types.GeographyFamily:
   750  						d, err = tree.ParseDGeography(string(t))
   751  						if err != nil {
   752  							return err
   753  						}
   754  					case types.GeometryFamily:
   755  						d, err = tree.ParseDGeometry(string(t))
   756  						if err != nil {
   757  							return err
   758  						}
   759  					case types.JsonFamily:
   760  						d, err = tree.ParseDJSON(string(t))
   761  						if err != nil {
   762  							return err
   763  						}
   764  					case types.ArrayFamily:
   765  						// We can only observe ARRAY types by their [] suffix.
   766  						d, err = tree.ParseDArrayFromString(
   767  							tree.NewTestingEvalContext(serverCfg.Settings), string(t), ct.ArrayContents())
   768  						if err != nil {
   769  							return err
   770  						}
   771  					case types.StringFamily:
   772  						// STRING types can have optional length suffixes, so only
   773  						// examine the prefix of the type.
   774  						d = tree.NewDString(string(t))
   775  					case types.DecimalFamily:
   776  						// DECIMAL types can have optional length suffixes, so only
   777  						// examine the prefix of the type.
   778  						d, err = tree.ParseDDecimal(string(t))
   779  						if err != nil {
   780  							return err
   781  						}
   782  					case types.OidFamily:
   783  						var i *tree.DInt
   784  						i, err = tree.ParseDInt(string(t))
   785  						if err != nil {
   786  							return err
   787  						}
   788  						d = tree.NewDOid(*i)
   789  					default:
   790  						return errors.Errorf("unknown []byte type: %s, %v: %s", t, cols[si], md.columnTypes[cols[si]])
   791  					}
   792  				case time.Time:
   793  					switch ct := md.columnTypes[cols[si]]; ct.Family() {
   794  					case types.DateFamily:
   795  						d, err = tree.NewDDateFromTime(t)
   796  						if err != nil {
   797  							return err
   798  						}
   799  					case types.TimeFamily:
   800  						// pq awkwardly represents TIME as a time.Time with date 0000-01-01.
   801  						d = tree.MakeDTime(timeofday.FromTimeAllow2400(t))
   802  					case types.TimeTZFamily:
   803  						d = tree.NewDTimeTZ(timetz.MakeTimeTZFromTimeAllow2400(t))
   804  					case types.TimestampFamily:
   805  						d, err = tree.MakeDTimestamp(t, time.Nanosecond)
   806  						if err != nil {
   807  							return err
   808  						}
   809  					case types.TimestampTZFamily:
   810  						d, err = tree.MakeDTimestampTZ(t, time.Nanosecond)
   811  						if err != nil {
   812  							return err
   813  						}
   814  					default:
   815  						return errors.Errorf("unknown timestamp type: %s, %v: %s", t, cols[si], md.columnTypes[cols[si]])
   816  					}
   817  				default:
   818  					return errors.Errorf("unknown field type: %T (%s)", t, cols[si])
   819  				}
   820  				d.Format(f)
   821  			}
   822  			select {
   823  			case <-done:
   824  				return ctx.Err()
   825  			case stringsCh <- f.String():
   826  			}
   827  		}
   828  		return nil
   829  	})
   830  	g.Go(func() error {
   831  		// Batch SQL strings into groups and write to output.
   832  		for s := range stringsCh {
   833  			inserts = append(inserts, s)
   834  			if len(inserts) == cap(inserts) {
   835  				writeInserts(w, md, inserts)
   836  				inserts = inserts[:0]
   837  			}
   838  		}
   839  		if len(inserts) != 0 {
   840  			writeInserts(w, md, inserts)
   841  			inserts = inserts[:0]
   842  		}
   843  		return nil
   844  	})
   845  	return g.Wait()
   846  }
   847  
   848  func writeInserts(w io.Writer, tmd tableMetadata, inserts []string) {
   849  	fmt.Fprintf(w, "\nINSERT INTO %s (%s) VALUES", &tmd.name.ObjectName, tmd.columnNames)
   850  	for idx, values := range inserts {
   851  		if idx > 0 {
   852  			fmt.Fprint(w, ",")
   853  		}
   854  		fmt.Fprintf(w, "\n\t(%s)", values)
   855  	}
   856  	fmt.Fprintln(w, ";")
   857  }