github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/scrub_constraint.go (about) 1 // Copyright 2017 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 "go/constant" 16 "time" 17 18 "github.com/cockroachdb/cockroach/pkg/sql/parser" 19 "github.com/cockroachdb/cockroach/pkg/sql/scrub" 20 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 21 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 22 "github.com/cockroachdb/cockroach/pkg/util/hlc" 23 ) 24 25 // sqlCheckConstraintCheckOperation is a check which validates a SQL 26 // CHECK constraint on a table. 27 type sqlCheckConstraintCheckOperation struct { 28 tableName *tree.TableName 29 tableDesc *sqlbase.ImmutableTableDescriptor 30 checkDesc *sqlbase.TableDescriptor_CheckConstraint 31 asOf hlc.Timestamp 32 33 // columns is a list of the columns returned in the query result 34 // tree.Datums. 35 columns []*sqlbase.ColumnDescriptor 36 // primaryColIdxs maps PrimaryIndex.Columns to the row 37 // indexes in the query result tree.Datums. 38 primaryColIdxs []int 39 40 run sqlCheckConstraintCheckRun 41 } 42 43 // sqlCheckConstraintCheckRun contains the run-time state for 44 // sqlCheckConstraintCheckOperation during local execution. 45 type sqlCheckConstraintCheckRun struct { 46 started bool 47 rows []tree.Datums 48 rowIndex int 49 } 50 51 func newSQLCheckConstraintCheckOperation( 52 tableName *tree.TableName, 53 tableDesc *sqlbase.ImmutableTableDescriptor, 54 checkDesc *sqlbase.TableDescriptor_CheckConstraint, 55 asOf hlc.Timestamp, 56 ) *sqlCheckConstraintCheckOperation { 57 return &sqlCheckConstraintCheckOperation{ 58 tableName: tableName, 59 tableDesc: tableDesc, 60 checkDesc: checkDesc, 61 asOf: asOf, 62 } 63 } 64 65 // Start implements the checkOperation interface. 66 // It creates a SELECT expression and generates a plan from it, which 67 // then runs in the distSQL execution engine. 68 func (o *sqlCheckConstraintCheckOperation) Start(params runParams) error { 69 ctx := params.ctx 70 expr, err := parser.ParseExpr(o.checkDesc.Expr) 71 if err != nil { 72 return err 73 } 74 // Generate a query of the form: 75 // SELECT a,b,c FROM db.t WHERE NOT (condition) 76 // We always fully qualify the table in the query. 77 tn := *o.tableName 78 tn.ExplicitCatalog = true 79 tn.ExplicitSchema = true 80 sel := &tree.SelectClause{ 81 Exprs: sqlbase.ColumnsSelectors(o.tableDesc.Columns), 82 From: tree.From{ 83 Tables: tree.TableExprs{&tn}, 84 }, 85 Where: &tree.Where{ 86 Type: tree.AstWhere, 87 Expr: &tree.NotExpr{Expr: expr}, 88 }, 89 } 90 if o.asOf != hlc.MaxTimestamp { 91 sel.From.AsOf = tree.AsOfClause{ 92 Expr: tree.NewNumVal( 93 constant.MakeInt64(o.asOf.WallTime), 94 "", /* origString */ 95 false /* negative */), 96 } 97 } 98 99 rows, err := params.extendedEvalCtx.ExecCfg.InternalExecutor.Query( 100 ctx, "check-constraint", params.p.txn, tree.AsStringWithFlags(sel, tree.FmtParsable), 101 ) 102 if err != nil { 103 return err 104 } 105 106 o.run.started = true 107 o.run.rows = rows 108 109 // Collect all the columns. 110 for i := range o.tableDesc.Columns { 111 o.columns = append(o.columns, &o.tableDesc.Columns[i]) 112 } 113 // Find the row indexes for all of the primary index columns. 114 o.primaryColIdxs, err = getPrimaryColIdxs(o.tableDesc, o.columns) 115 return err 116 } 117 118 // Next implements the checkOperation interface. 119 func (o *sqlCheckConstraintCheckOperation) Next(params runParams) (tree.Datums, error) { 120 row := o.run.rows[o.run.rowIndex] 121 o.run.rowIndex++ 122 timestamp, err := tree.MakeDTimestamp( 123 params.extendedEvalCtx.GetStmtTimestamp(), 124 time.Nanosecond, 125 ) 126 if err != nil { 127 return nil, err 128 } 129 130 var primaryKeyDatums tree.Datums 131 for _, rowIdx := range o.primaryColIdxs { 132 primaryKeyDatums = append(primaryKeyDatums, row[rowIdx]) 133 } 134 135 details := make(map[string]interface{}) 136 rowDetails := make(map[string]interface{}) 137 details["row_data"] = rowDetails 138 details["constraint_name"] = o.checkDesc.Name 139 for rowIdx, col := range o.columns { 140 // TODO(joey): We should maybe try to get the underlying type. 141 rowDetails[col.Name] = row[rowIdx].String() 142 } 143 detailsJSON, err := tree.MakeDJSON(details) 144 if err != nil { 145 return nil, err 146 } 147 148 return tree.Datums{ 149 // TODO(joey): Add the job UUID once the SCRUB command uses jobs. 150 tree.DNull, /* job_uuid */ 151 tree.NewDString(scrub.CheckConstraintViolation), 152 tree.NewDString(o.tableName.Catalog()), 153 tree.NewDString(o.tableName.Table()), 154 tree.NewDString(primaryKeyDatums.String()), 155 timestamp, 156 tree.DBoolFalse, 157 detailsJSON, 158 }, nil 159 } 160 161 // Started implements the checkOperation interface. 162 func (o *sqlCheckConstraintCheckOperation) Started() bool { 163 return o.run.started 164 } 165 166 // Done implements the checkOperation interface. 167 func (o *sqlCheckConstraintCheckOperation) Done(ctx context.Context) bool { 168 return o.run.rows == nil || o.run.rowIndex >= len(o.run.rows) 169 } 170 171 // Close implements the checkOperation interface. 172 func (o *sqlCheckConstraintCheckOperation) Close(ctx context.Context) { 173 o.run.rows = nil 174 }