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 }