// Set sets session variables. // Privileges: None. // Notes: postgres/mysql do not require privileges for session variables (some exceptions). func (p *planner) Set(n *parser.Set) (planNode, error) { // By using QualifiedName.String() here any variables that are keywords will // be double quoted. name := strings.ToLower(n.Name.String()) switch name { case `"database"`: // Quoted: database is a reserved word if len(n.Values) != 1 { return nil, fmt.Errorf("database: requires a single string value") } val, err := parser.EvalExpr(n.Values[0]) if err != nil { return nil, err } s, ok := val.(parser.DString) if !ok { return nil, fmt.Errorf("database: requires a single string value: %s is a %s", n.Values[0], val.Type()) } dbName := string(s) if len(dbName) != 0 { // Verify database descriptor exists. if _, err := p.getDatabaseDesc(dbName); err != nil { return nil, err } } p.session.Database = dbName default: return nil, util.Errorf("unknown variable: %s", name) } return &valuesNode{}, nil }
// Values constructs a valuesNode from a VALUES expression. func (p *planner) Values(n parser.Values) (planNode, error) { v := &valuesNode{ rows: make([]parser.DTuple, 0, len(n)), } nCols := 0 for _, tuple := range n { data, err := parser.EvalExpr(tuple) if err != nil { return nil, err } vals, ok := data.(parser.DTuple) if !ok { return nil, fmt.Errorf("expected a tuple, but found %T", data) } if len(v.rows) == 0 { nCols = len(vals) } else if nCols != len(vals) { return nil, errors.New("VALUES lists must all be the same length") } v.rows = append(v.rows, vals) } v.columns = make([]string, nCols) for i := 0; i < nCols; i++ { v.columns[i] = fmt.Sprintf("column%d", i+1) } return v, nil }
// filterRow checks to see if the current row matches the filter (i.e. the // where-clause). May set n.err if an error occurs during expression // evaluation. func (n *scanNode) filterRow() bool { if n.desc != nil { for _, col := range n.visibleCols { if !col.Nullable { break } if qval, ok := n.qvals[col.ID]; ok && qval.datum == nil { qval.datum = parser.DNull continue } } } if n.filter == nil { return true } var d parser.Datum d, n.err = parser.EvalExpr(n.filter) if n.err != nil { return false } v, ok := d.(parser.DBool) if !ok { n.err = fmt.Errorf("WHERE clause did not evaluate to a boolean") return false } return bool(v) }
// filterRow checks to see if the current row matches the filter (i.e. the // where-clause). May set n.err if an error occurs during expression // evaluation. func (n *scanNode) filterRow() bool { if n.desc != nil { for _, col := range n.visibleCols { if !col.Nullable { break } if qval, ok := n.qvals[col.ID]; ok && qval.datum == nil { qval.datum = parser.DNull continue } } } if n.filter == nil { return true } var d parser.Datum d, n.err = parser.EvalExpr(n.filter) if n.err != nil { return false } return d != parser.DNull && bool(d.(parser.DBool)) }
func fuzzSingle(stmt parser.Statement) (interestingness int) { var lastExpr parser.Expr rcvr := func() { if r := recover(); r != nil { if !expected(fmt.Sprintf("%v", r)) { fmt.Printf("Stmt: %s\n%s", stmt, spew.Sdump(stmt)) if lastExpr != nil { fmt.Printf("Expr: %s", spew.Sdump(lastExpr)) } panic(r) } // Anything that has expected errors in it is fine, but not as // interesting as things that go through. interestingness = 1 } } defer rcvr() data0 := stmt.String() // TODO(tschottdorf): again, this is since we're ignoring stuff in the // grammar instead of erroring out on unsupported language. See: // https://github.com/cockroachdb/cockroach/issues/1949 if strings.Contains(data0, "%!s(<nil>)") { return 0 } stmt1, err := parser.Parse(data0) if err != nil { fmt.Printf("AST: %s", spew.Sdump(stmt)) fmt.Printf("data0: %q\n", data0) panic(err) } interestingness = 2 data1 := stmt1.String() // TODO(tschottdorf): due to the ignoring issue again. // if !reflect.DeepEqual(stmt, stmt1) { if data1 != data0 { fmt.Printf("data0: %q\n", data0) fmt.Printf("AST: %s", spew.Sdump(stmt)) fmt.Printf("data1: %q\n", data1) fmt.Printf("AST: %s", spew.Sdump(stmt1)) panic("not equal") } var v visitorFunc = func(e parser.Expr) parser.Expr { lastExpr = e if _, err := parser.EvalExpr(e); err != nil { panic(err) } return e } parser.WalkStmt(v, stmt) return }
func (n *scanNode) initWhere(where *parser.Where) error { if where == nil { return nil } n.filter, n.err = n.resolveQNames(where.Expr) if n.err == nil { // Evaluate the expression once to memoize operators and functions. _, n.err = parser.EvalExpr(n.filter) } return n.err }
func checkEquivExpr(a, b parser.Expr, qvals qvalMap) error { // The expressions above only use the values 1 and 2. Verify that the // simplified expressions evaluate to the same value as the original // expression for interesting values. for _, v := range []parser.DInt{0, 1, 2, 3} { for _, q := range qvals { q.datum = v } da, err := parser.EvalExpr(a) if err != nil { return fmt.Errorf("%s: %v", a, err) } db, err := parser.EvalExpr(b) if err != nil { return fmt.Errorf("%s: %v", b, err) } if da != db { return fmt.Errorf("%s: %d: expected %s, but found %s", a, v, da, db) } } return nil }
func (n *scanNode) renderRow() error { if n.row == nil { n.row = make([]parser.Datum, len(n.render)) } for i, e := range n.render { var err error n.row[i], err = parser.EvalExpr(e, n.vals) if err != nil { return err } } return nil }
func shouldOutputRow(where *parser.Where, vals valMap) (bool, error) { if where == nil { return true, nil } d, err := parser.EvalExpr(where.Expr, vals) if err != nil { return false, err } v, ok := d.(parser.DBool) if !ok { return false, fmt.Errorf("WHERE clause did not evaluate to a boolean") } return bool(v), nil }
func (n *scanNode) filterRow() (bool, error) { if n.filter == nil { return true, nil } d, err := parser.EvalExpr(n.filter, n.vals) if err != nil { return false, err } v, ok := d.(parser.DBool) if !ok { return false, fmt.Errorf("WHERE clause did not evaluate to a boolean") } return bool(v), nil }
// limit constructs a limitNode based on the LIMIT and OFFSET clauses. func (*planner) limit(n *parser.Select, p planNode) (planNode, error) { if n.Limit == nil { return p, nil } var count, offset int64 data := []struct { name string src parser.Expr dst *int64 defaultVal int64 }{ {"LIMIT", n.Limit.Count, &count, math.MaxInt64}, {"OFFSET", n.Limit.Offset, &offset, 0}, } for _, datum := range data { if datum.src == nil { *datum.dst = datum.defaultVal } else { if parser.ContainsVars(datum.src) { return nil, fmt.Errorf("argument of %s must not contain variables", datum.name) } normalized, err := parser.NormalizeExpr(datum.src) if err != nil { return nil, err } dstDatum, err := parser.EvalExpr(normalized) if err != nil { return nil, err } if dstDatum == parser.DNull { *datum.dst = datum.defaultVal continue } if dstDInt, ok := dstDatum.(parser.DInt); ok { *datum.dst = int64(dstDInt) continue } return nil, fmt.Errorf("argument of %s must be type %s, not type %s", datum.name, parser.DummyInt.Type(), dstDatum.Type()) } } return &limitNode{planNode: p, count: count, offset: offset}, nil }
func getStringVal(name string, values parser.Exprs) (string, error) { if len(values) != 1 { return "", fmt.Errorf("%s: requires a single string value", name) } val, err := parser.EvalExpr(values[0]) if err != nil { return "", err } s, ok := val.(parser.DString) if !ok { return "", fmt.Errorf("%s: requires a single string value: %s is a %s", name, values[0], val.Type()) } return string(s), nil }
// renderRow renders the row by evaluating the render expressions. May set // n.err if an error occurs during expression evaluation. func (n *scanNode) renderRow() { if n.explain == explainDebug { n.explainDebug(true, true) return } if n.row == nil { n.row = make([]parser.Datum, len(n.render)) } for i, e := range n.render { n.row[i], n.err = parser.EvalExpr(e) if n.err != nil { return } } n.rowIndex++ }
// Values constructs a valuesNode from a VALUES expression. func (p *planner) Values(n parser.Values) (planNode, error) { v := &valuesNode{ rows: make([]parser.DTuple, 0, len(n)), } for _, tuple := range n { data, err := parser.EvalExpr(tuple, nil) if err != nil { return nil, err } vals, ok := data.(parser.DTuple) if !ok { return nil, fmt.Errorf("expected a tuple, but found %T", data) } v.rows = append(v.rows, vals) } return v, nil }
// Set sets session variables. func (s *Server) Set(session *Session, p *parser.Set, args []sqlwire.Datum, resp *sqlwire.Response) error { // By using QualifiedName.String() here any variables that are keywords will // be double quoted. name := strings.ToLower(p.Name.String()) switch name { case `"database"`: // Quoted: database is a reserved word if len(p.Values) != 1 { return fmt.Errorf("database: requires a single string value") } val, err := parser.EvalExpr(p.Values[0], nil) if err != nil { return err } session.Database = val.String() default: return util.Errorf("unknown variable: %s", name) } return nil }
func (s *Server) processSelect(node parser.SelectStatement) (rows []sqlwire.Result_Row, _ error) { switch nt := node.(type) { // case *parser.Select: // case *parser.Union: // TODO(vivek): return s.query(nt.stmt, nil) case parser.Values: for _, tuple := range nt { data, err := parser.EvalExpr(tuple, nil) if err != nil { return rows, err } dTuple, ok := data.(parser.DTuple) if !ok { // A one-element DTuple is currently turned into whatever its // underlying element is, so we have to massage here. // See #1741. dTuple = parser.DTuple([]parser.Datum{data}) } var vals []sqlwire.Datum for _, val := range dTuple { switch vt := val.(type) { case parser.DBool: vals = append(vals, sqlwire.Datum{BoolVal: (*bool)(&vt)}) case parser.DInt: vals = append(vals, sqlwire.Datum{IntVal: (*int64)(&vt)}) case parser.DFloat: vals = append(vals, sqlwire.Datum{FloatVal: (*float64)(&vt)}) case parser.DString: vals = append(vals, sqlwire.Datum{StringVal: (*string)(&vt)}) case parser.DNull: vals = append(vals, sqlwire.Datum{}) default: return rows, util.Errorf("unsupported node: %T", val) } } rows = append(rows, sqlwire.Result_Row{Values: vals}) } return rows, nil } return nil, util.Errorf("TODO(pmattis): unsupported node: %T", node) }
func (n *groupNode) Next() bool { if !n.needGroup { return false } n.needGroup = false // Loop over the rows passing the values into the corresponding aggregation // functions. for n.plan.Next() { values := n.plan.Values() for i, f := range n.funcs { if n.err = f.impl.Add(values[i]); n.err != nil { return false } } } n.err = n.plan.Err() if n.err != nil { return false } // Fill in the aggregate function result value. for _, f := range n.funcs { if f.val.datum, n.err = f.impl.Result(); n.err != nil { return false } } // Render the results. n.row = make([]parser.Datum, len(n.render)) for i, r := range n.render { n.row[i], n.err = parser.EvalExpr(r) if n.err != nil { return false } } return n.err == nil }
func (s *Server) processSelect(node parser.SelectStatement) (rows []sqlwire.Result_Row, _ error) { switch nt := node.(type) { // case *parser.Select: // case *parser.Union: // TODO(vivek): return s.query(nt.stmt, nil) case parser.Values: for _, tuple := range nt { data, err := parser.EvalExpr(tuple, nil) if err != nil { return rows, err } dTuple, ok := data.(parser.DTuple) if !ok { return nil, fmt.Errorf("expected a tuple, but found %T", data) } var vals []sqlwire.Datum for _, val := range dTuple { switch vt := val.(type) { case parser.DBool: vals = append(vals, sqlwire.Datum{BoolVal: (*bool)(&vt)}) case parser.DInt: vals = append(vals, sqlwire.Datum{IntVal: (*int64)(&vt)}) case parser.DFloat: vals = append(vals, sqlwire.Datum{FloatVal: (*float64)(&vt)}) case parser.DString: vals = append(vals, sqlwire.Datum{StringVal: (*string)(&vt)}) case parser.DNull: vals = append(vals, sqlwire.Datum{}) default: return rows, util.Errorf("unsupported node: %T", val) } } rows = append(rows, sqlwire.Result_Row{Values: vals}) } return rows, nil } return nil, util.Errorf("TODO(pmattis): unsupported node: %T", node) }
func (n *scanNode) renderRow() error { if n.row == nil { n.row = make([]parser.Datum, len(n.render)) } if n.desc != nil { for _, col := range n.desc.Columns { if _, ok := n.vals[col.Name]; ok { continue } if col.Nullable { n.vals[col.Name] = parser.DNull } } } for i, e := range n.render { var err error n.row[i], err = parser.EvalExpr(e, n.vals) if err != nil { return err } } return nil }
// Insert inserts rows into the database. // Privileges: INSERT on table // Notes: postgres requires INSERT. No "on duplicate key update" option. // mysql requires INSERT. Also requires UPDATE on "ON DUPLICATE KEY UPDATE". func (p *planner) Insert(n *parser.Insert) (planNode, error) { tableDesc, err := p.getTableDesc(n.Table) if err != nil { return nil, err } if err := p.checkPrivilege(tableDesc, privilege.INSERT); err != nil { return nil, err } // Determine which columns we're inserting into. cols, err := p.processColumns(tableDesc, n.Columns) if err != nil { return nil, err } // Construct a map from column ID to the index the value appears at within a // row. colIDtoRowIndex := map[ColumnID]int{} for i, c := range cols { colIDtoRowIndex[c.ID] = i } // Add any column not already present that has a DEFAULT expression. for _, col := range tableDesc.Columns { if _, ok := colIDtoRowIndex[col.ID]; ok { continue } if col.DefaultExpr != nil { colIDtoRowIndex[col.ID] = len(cols) cols = append(cols, col) } } // Verify we have at least the columns that are part of the primary key. primaryKeyCols := map[ColumnID]struct{}{} for i, id := range tableDesc.PrimaryIndex.ColumnIDs { if _, ok := colIDtoRowIndex[id]; !ok { return nil, fmt.Errorf("missing %q primary key column", tableDesc.PrimaryIndex.ColumnNames[i]) } primaryKeyCols[id] = struct{}{} } // Construct the default expressions. The returned slice will be nil if no // column in the table has a default expression. defaultExprs, err := makeDefaultExprs(cols) if err != nil { return nil, err } // Replace any DEFAULT markers with the corresponding default expressions. if n.Rows, err = p.fillDefaults(defaultExprs, cols, n.Rows); err != nil { return nil, err } // Transform the values into a rows object. This expands SELECT statements or // generates rows from the values contained within the query. rows, err := p.makePlan(n.Rows) if err != nil { return nil, err } primaryIndex := tableDesc.PrimaryIndex primaryIndexKeyPrefix := MakeIndexKeyPrefix(tableDesc.ID, primaryIndex.ID) var b client.Batch for rows.Next() { rowVals := rows.Values() // The values for the row may be shorter than the number of columns being // inserted into. Generate default values for those columns using the // default expressions. for i := len(rowVals); i < len(cols); i++ { if defaultExprs == nil { rowVals = append(rowVals, parser.DNull) continue } d, err := parser.EvalExpr(defaultExprs[i]) if err != nil { return nil, err } rowVals = append(rowVals, d) } // Check to see if NULL is being inserted into any non-nullable column. for _, col := range tableDesc.Columns { if !col.Nullable { if i, ok := colIDtoRowIndex[col.ID]; !ok || rowVals[i] == parser.DNull { return nil, fmt.Errorf("null value in column %q violates not-null constraint", col.Name) } } } primaryIndexKey, _, err := encodeIndexKey( primaryIndex.ColumnIDs, colIDtoRowIndex, rowVals, primaryIndexKeyPrefix) if err != nil { return nil, err } // Write the secondary indexes. secondaryIndexEntries, err := encodeSecondaryIndexes( tableDesc.ID, tableDesc.Indexes, colIDtoRowIndex, rowVals) if err != nil { return nil, err } for _, secondaryIndexEntry := range secondaryIndexEntries { if log.V(2) { log.Infof("CPut %q -> %v", secondaryIndexEntry.key, secondaryIndexEntry.value) } b.CPut(secondaryIndexEntry.key, secondaryIndexEntry.value, nil) } // Write the row sentinel. if log.V(2) { log.Infof("CPut %q -> NULL", primaryIndexKey) } b.CPut(primaryIndexKey, nil, nil) // Write the row columns. for i, val := range rowVals { col := cols[i] // Make sure the value can be written to the column before proceeding. primitive, err := convertDatum(col, val) if err != nil { return nil, err } if _, ok := primaryKeyCols[col.ID]; ok { // Skip primary key columns as their values are encoded in the row // sentinel key which is guaranteed to exist for as long as the row // exists. continue } if primitive != nil { // We only output non-NULL values. Non-existent column keys are // considered NULL during scanning and the row sentinel ensures we know // the row exists. key := MakeColumnKey(col.ID, primaryIndexKey) if log.V(2) { log.Infof("CPut %q -> %v", key, primitive) } b.CPut(key, primitive, nil) } } } if err := rows.Err(); err != nil { return nil, err } if IsSystemID(tableDesc.GetID()) { // Mark transaction as operating on the system DB. p.txn.SetSystemDBTrigger() } if err := p.txn.Run(&b); err != nil { return nil, convertBatchError(tableDesc, b, err) } // TODO(tamird/pmattis): return the number of affected rows return &valuesNode{}, nil }
func (n *scanNode) addRender(target parser.SelectExpr) error { // If a QualifiedName has a StarIndirection suffix we need to match the // prefix of the qualified name to one of the tables in the query and // then expand the "*" into a list of columns. if qname, ok := target.Expr.(*parser.QualifiedName); ok { if n.err = qname.NormalizeColumnName(); n.err != nil { return n.err } if qname.IsStar() { if n.desc == nil { return fmt.Errorf("\"%s\" with no tables specified is not valid", qname) } if target.As != "" { return fmt.Errorf("\"%s\" cannot be aliased", qname) } tableName := qname.Table() if tableName != "" && !equalName(n.desc.Alias, tableName) { return fmt.Errorf("table \"%s\" not found", tableName) } if n.isSecondaryIndex { for i, col := range n.index.ColumnNames { n.columns = append(n.columns, col) var col *ColumnDescriptor if col, n.err = n.desc.FindColumnByID(n.index.ColumnIDs[i]); n.err != nil { return n.err } n.render = append(n.render, n.getQVal(*col)) } } else { for _, col := range n.desc.Columns { n.columns = append(n.columns, col.Name) n.render = append(n.render, n.getQVal(col)) } } return nil } } // Resolve qualified names. This has the side-effect of normalizing any // qualified name found. var resolved parser.Expr if resolved, n.err = n.resolveQNames(target.Expr); n.err != nil { return n.err } // Evaluate the expression once to memoize operators and functions. if _, n.err = parser.EvalExpr(resolved); n.err != nil { return n.err } n.render = append(n.render, resolved) if target.As != "" { n.columns = append(n.columns, string(target.As)) return nil } switch t := target.Expr.(type) { case *parser.QualifiedName: // If the expression is a qualified name, use the column name, not the // full qualification as the column name to return. n.columns = append(n.columns, t.Column()) default: n.columns = append(n.columns, target.Expr.String()) } return nil }
// analyzeComparisonExpr analyzes the comparison expression, restricting the // start and end info for any qvalues found within it. func (m qvalueRangeMap) analyzeComparisonExpr(node *parser.ComparisonExpr) { op := node.Operator switch op { case parser.EQ, parser.LT, parser.LE, parser.GT, parser.GE: break default: // TODO(pmattis): For parser.LIKE we could extract the constant prefix and // treat as a range restriction. return } qval, ok := node.Left.(*qvalue) var constExpr parser.Expr if ok { if !isConst(node.Right) { // qvalue <op> non-constant. return } constExpr = node.Right // qval <op> constant. } else { qval, ok = node.Right.(*qvalue) if !ok { // non-qvalue <op> non-qvalue. return } if !isConst(node.Left) { // non-constant <op> qvalue. return } constExpr = node.Left // constant <op> qval: invert the operation. switch op { case parser.LT: op = parser.GT case parser.LE: op = parser.GE case parser.GT: op = parser.LT case parser.GE: op = parser.LE } } // Evaluate the constant expression. datum, err := parser.EvalExpr(constExpr) if err != nil { // TODO(pmattis): Should we pass the error up the stack? return } // Ensure the resulting datum matches the column type. if _, err := convertDatum(qval.col, datum); err != nil { // TODO(pmattis): Should we pass the error up the stack? return } if log.V(2) { log.Infof("analyzeComparisonExpr: %s %s %s", qval.col.Name, op, datum) } r := m.getRange(qval.col.ID) // Restrict the start element. switch startOp := op; startOp { case parser.EQ: startOp = parser.GE fallthrough case parser.GE, parser.GT: r.start.intersect(qvalueInfo{datum, startOp}, true) } // Restrict the end element. switch endOp := op; endOp { case parser.EQ: endOp = parser.LE fallthrough case parser.LE, parser.LT: r.end.intersect(qvalueInfo{datum, endOp}, false) } }
// Update updates columns for a selection of rows from a table. // Privileges: WRITE and READ on table. We currently always use a select statement. // Notes: postgres requires UPDATE. Requires SELECT with WHERE clause with table. // mysql requires UPDATE. Also requires SELECT with WHERE clause with table. func (p *planner) Update(n *parser.Update) (planNode, error) { tableDesc, err := p.getAliasedTableDesc(n.Table) if err != nil { return nil, err } if !tableDesc.HasPrivilege(p.user, parser.PrivilegeWrite) { return nil, fmt.Errorf("user %s does not have %s privilege on table %s", p.user, parser.PrivilegeWrite, tableDesc.Name) } // Determine which columns we're inserting into. var names parser.QualifiedNames for _, expr := range n.Exprs { names = append(names, expr.Name) } cols, err := p.processColumns(tableDesc, names) if err != nil { return nil, err } // Set of columns being updated colIDSet := map[structured.ColumnID]struct{}{} for _, c := range cols { colIDSet[c.ID] = struct{}{} } // Don't allow updating any column that is part of the primary key. for i, id := range tableDesc.PrimaryIndex.ColumnIDs { if _, ok := colIDSet[id]; ok { return nil, fmt.Errorf("primary key column %q cannot be updated", tableDesc.PrimaryIndex.ColumnNames[i]) } } // Query the rows that need updating. // TODO(vivek): Avoid going through Select. row, err := p.Select(&parser.Select{ Exprs: parser.SelectExprs{parser.StarSelectExpr}, From: parser.TableExprs{n.Table}, Where: n.Where, }) if err != nil { return nil, err } // Construct a map from column ID to the index the value appears at within a // row. colIDtoRowIndex := map[structured.ColumnID]int{} for i, name := range row.Columns() { c, err := tableDesc.FindColumnByName(name) if err != nil { return nil, err } colIDtoRowIndex[c.ID] = i } primaryIndex := tableDesc.PrimaryIndex primaryIndexKeyPrefix := structured.MakeIndexKeyPrefix(tableDesc.ID, primaryIndex.ID) // Evaluate all the column value expressions. vals := make([]parser.Datum, 0, 10) for _, expr := range n.Exprs { val, err := parser.EvalExpr(expr.Expr) if err != nil { return nil, err } vals = append(vals, val) } // Secondary indexes needing updating. var indexes []structured.IndexDescriptor for _, index := range tableDesc.Indexes { for _, id := range index.ColumnIDs { if _, ok := colIDSet[id]; ok { indexes = append(indexes, index) break } } } // Update all the rows. b := client.Batch{} for row.Next() { rowVals := row.Values() primaryIndexKeySuffix, _, err := encodeIndexKey(primaryIndex.ColumnIDs, colIDtoRowIndex, rowVals, nil) if err != nil { return nil, err } primaryIndexKey := bytes.Join([][]byte{primaryIndexKeyPrefix, primaryIndexKeySuffix}, nil) // Compute the current secondary index key:value pairs for this row. secondaryIndexEntries, err := encodeSecondaryIndexes(tableDesc.ID, indexes, colIDtoRowIndex, rowVals, primaryIndexKeySuffix) if err != nil { return nil, err } // Compute the new secondary index key:value pairs for this row. // // Update the row values. for i, col := range cols { val := vals[i] if !col.Nullable && val == parser.DNull { return nil, fmt.Errorf("null value in column %q violates not-null constraint", col.Name) } rowVals[colIDtoRowIndex[col.ID]] = val } newSecondaryIndexEntries, err := encodeSecondaryIndexes(tableDesc.ID, indexes, colIDtoRowIndex, rowVals, primaryIndexKeySuffix) if err != nil { return nil, err } // Update secondary indexes. for i, newSecondaryIndexEntry := range newSecondaryIndexEntries { secondaryIndexEntry := secondaryIndexEntries[i] if !bytes.Equal(newSecondaryIndexEntry.key, secondaryIndexEntry.key) { if log.V(2) { log.Infof("CPut %q -> %v", newSecondaryIndexEntry.key, newSecondaryIndexEntry.value) } b.CPut(newSecondaryIndexEntry.key, newSecondaryIndexEntry.value, nil) if log.V(2) { log.Infof("Del %q", secondaryIndexEntry.key) } b.Del(secondaryIndexEntry.key) } } // Add the new values. for i, val := range vals { col := cols[i] primitive, err := convertDatum(col, val) if err != nil { return nil, err } key := structured.MakeColumnKey(col.ID, primaryIndexKey) if primitive != nil { // We only output non-NULL values. Non-existent column keys are // considered NULL during scanning and the row sentinel ensures we know // the row exists. if log.V(2) { log.Infof("Put %q -> %v", key, val) } b.Put(key, primitive) } else { // The column might have already existed but is being set to NULL, so // delete it. if log.V(2) { log.Infof("Del %q", key) } b.Del(key) } } } if err := row.Err(); err != nil { return nil, err } if err := p.txn.Run(&b); err != nil { if tErr, ok := err.(*proto.ConditionFailedError); ok { return nil, fmt.Errorf("duplicate key value %q violates unique constraint %s", tErr.ActualValue.Bytes, "TODO(tamird)") } return nil, err } // TODO(tamird/pmattis): return the number of affected rows. return &valuesNode{}, nil }