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  }