github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/save_table.go (about) 1 // Copyright 2019 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 sql 12 13 import ( 14 "context" 15 "fmt" 16 17 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 18 "github.com/cockroachdb/errors" 19 ) 20 21 // saveTableNode is used for internal testing. It is a node that passes through 22 // input data but saves it in a table. The table can be used subsequently, e.g. 23 // to look at statistics. 24 // 25 // The node creates the table on startup. If the table exists, it errors out. 26 type saveTableNode struct { 27 source planNode 28 29 target tree.TableName 30 31 // Column names from the saved table. These could be different than the names 32 // of the columns in the source plan. Note that saveTableNode passes through 33 // the source plan's column names. 34 colNames []string 35 36 run struct { 37 // vals accumulates a ValuesClause with the rows. 38 vals tree.ValuesClause 39 } 40 } 41 42 // saveTableInsertBatch is the number of rows per issued INSERT statement. 43 const saveTableInsertBatch = 100 44 45 func (p *planner) makeSaveTable( 46 source planNode, target *tree.TableName, colNames []string, 47 ) planNode { 48 return &saveTableNode{source: source, target: *target, colNames: colNames} 49 } 50 51 func (n *saveTableNode) startExec(params runParams) error { 52 create := &tree.CreateTable{ 53 Table: n.target, 54 } 55 56 cols := planColumns(n.source) 57 if len(n.colNames) != len(cols) { 58 return errors.AssertionFailedf( 59 "number of column names (%d) does not match number of columns (%d)", 60 len(n.colNames), len(cols), 61 ) 62 } 63 for i := 0; i < len(cols); i++ { 64 def := &tree.ColumnTableDef{ 65 Name: tree.Name(n.colNames[i]), 66 Type: cols[i].Typ, 67 } 68 def.Nullable.Nullability = tree.SilentNull 69 create.Defs = append(create.Defs, def) 70 } 71 72 _, err := params.p.ExtendedEvalContext().ExecCfg.InternalExecutor.Exec( 73 params.ctx, 74 "create save table", 75 nil, /* txn */ 76 create.String(), 77 ) 78 return err 79 } 80 81 // issue inserts rows into the target table of the saveTableNode. 82 func (n *saveTableNode) issue(params runParams) error { 83 if v := &n.run.vals; len(v.Rows) > 0 { 84 stmt := fmt.Sprintf("INSERT INTO %s %s", n.target.String(), v.String()) 85 if _, err := params.p.ExtendedEvalContext().ExecCfg.InternalExecutor.Exec( 86 params.ctx, 87 "insert into save table", 88 nil, /* txn */ 89 stmt, 90 ); err != nil { 91 return errors.Wrapf(err, "while running %s", stmt) 92 } 93 v.Rows = nil 94 } 95 return nil 96 } 97 98 // Next is part of the planNode interface. 99 func (n *saveTableNode) Next(params runParams) (bool, error) { 100 res, err := n.source.Next(params) 101 if err != nil { 102 return res, err 103 } 104 if !res { 105 // We are done. Insert any accumulated rows. 106 err := n.issue(params) 107 return false, err 108 } 109 row := n.source.Values() 110 exprs := make(tree.Exprs, len(row)) 111 for i := range row { 112 exprs[i] = row[i] 113 } 114 n.run.vals.Rows = append(n.run.vals.Rows, exprs) 115 if len(n.run.vals.Rows) >= saveTableInsertBatch { 116 if err := n.issue(params); err != nil { 117 return false, err 118 } 119 } 120 return true, nil 121 } 122 123 // Values is part of the planNode interface. 124 func (n *saveTableNode) Values() tree.Datums { 125 return n.source.Values() 126 } 127 128 // Close is part of the planNode interface. 129 func (n *saveTableNode) Close(ctx context.Context) { 130 n.source.Close(ctx) 131 }