github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/row/row.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package row 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 22 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 23 "github.com/dolthub/dolt/go/libraries/utils/valutil" 24 "github.com/dolthub/dolt/go/store/types" 25 ) 26 27 var ErrRowNotValid = errors.New("invalid row for current schema") 28 29 type Row interface { 30 // Iterates over all the columns in the row. Columns that have no value set will not be visited. 31 IterCols(cb func(tag uint64, val types.Value) (stop bool, err error)) (bool, error) 32 33 // Iterates over all columns in the schema, using the value for the row. Columns that have no value set in this row 34 // will still be visited, and receive a nil value. 35 IterSchema(sch schema.Schema, cb func(tag uint64, val types.Value) (stop bool, err error)) (bool, error) 36 37 // Returns the value for the column with the tag given, and a success bool. The value will be null if the row 38 // doesn't contain a value for that tag. 39 GetColVal(tag uint64) (types.Value, bool) 40 41 // Format returns the types.NomsBinFormat for this row. 42 Format() *types.NomsBinFormat 43 44 // TODO(andy): NomsMapKey, NomsMapValue, & SetColVal 45 // don't make sense in the context of keyless tables. 46 // Make these methods package private. 47 48 // Sets a value for the column with the tag given, returning a new row with the update. 49 SetColVal(tag uint64, val types.Value, sch schema.Schema) (Row, error) 50 51 // Returns the noms map key for this row, using the schema provided. 52 NomsMapKey(sch schema.Schema) types.LesserValuable 53 54 // Returns the noms map value for this row, using the schema provided. 55 NomsMapValue(sch schema.Schema) types.Valuable 56 57 // TaggedValues returns the row as TaggedValues. 58 TaggedValues() (TaggedValues, error) 59 } 60 61 func New(nbf *types.NomsBinFormat, sch schema.Schema, colVals TaggedValues) (Row, error) { 62 if schema.IsKeyless(sch) { 63 return keylessRowFromTaggedValued(nbf, sch, colVals) 64 } 65 return pkRowFromTaggedValues(nbf, sch, colVals) 66 } 67 68 func FromNoms(sch schema.Schema, nomsKey, nomsVal types.Tuple) (Row, error) { 69 if schema.IsKeyless(sch) { 70 row, _, err := KeylessRowsFromTuples(nomsKey, nomsVal) 71 return row, err 72 } 73 return pkRowFromNoms(sch, nomsKey, nomsVal) 74 } 75 76 // ToNoms returns the storage-layer tuples corresponding to |r|. 77 func ToNoms(ctx context.Context, sch schema.Schema, r Row) (key, val types.Tuple, err error) { 78 k, err := r.NomsMapKey(sch).Value(ctx) 79 if err != nil { 80 return key, val, err 81 } 82 83 v, err := r.NomsMapValue(sch).Value(ctx) 84 if err != nil { 85 return key, val, err 86 } 87 88 return k.(types.Tuple), v.(types.Tuple), nil 89 } 90 91 func GetFieldByName(colName string, r Row, sch schema.Schema) (types.Value, bool) { 92 col, ok := sch.GetAllCols().GetByName(colName) 93 94 if !ok { 95 panic("Requesting column that isn't in the schema. This is a bug. columns should be verified in the schema before attempted retrieval.") 96 } else { 97 return r.GetColVal(col.Tag) 98 } 99 } 100 101 func GetFieldByNameWithDefault(colName string, defVal types.Value, r Row, sch schema.Schema) types.Value { 102 col, ok := sch.GetAllCols().GetByName(colName) 103 104 if !ok { 105 panic("Requesting column that isn't in the schema. This is a bug. columns should be verified in the schema before attempted retrieval.") 106 } else { 107 val, ok := r.GetColVal(col.Tag) 108 109 if !ok { 110 return defVal 111 } 112 113 return val 114 } 115 } 116 117 // ReduceToIndexKeys creates a full key and a partial key from the given row (first tuple being the full key). Please 118 // refer to the note in the index editor for more information regarding partial keys. 119 func ReduceToIndexKeys(idx schema.Index, r Row) (types.Tuple, types.Tuple, error) { 120 vals := make([]types.Value, 0, len(idx.AllTags())*2) 121 for _, tag := range idx.AllTags() { 122 val, ok := r.GetColVal(tag) 123 if !ok { 124 val = types.NullValue 125 } 126 vals = append(vals, types.Uint(tag), val) 127 } 128 fullKey, err := types.NewTuple(r.Format(), vals...) 129 if err != nil { 130 return types.Tuple{}, types.Tuple{}, err 131 } 132 partialKey, err := types.NewTuple(r.Format(), vals[:idx.Count()*2]...) 133 if err != nil { 134 return types.Tuple{}, types.Tuple{}, err 135 } 136 return fullKey, partialKey, nil 137 } 138 139 // ReduceToIndexKeysFromTagMap creates a full key and a partial key from the given map of tags (first tuple being the 140 // full key). Please refer to the note in the index editor for more information regarding partial keys. 141 func ReduceToIndexKeysFromTagMap(nbf *types.NomsBinFormat, idx schema.Index, tagToVal map[uint64]types.Value) (types.Tuple, types.Tuple, error) { 142 vals := make([]types.Value, 0, len(idx.AllTags())*2) 143 for _, tag := range idx.AllTags() { 144 val, ok := tagToVal[tag] 145 if !ok { 146 val = types.NullValue 147 } 148 vals = append(vals, types.Uint(tag), val) 149 } 150 fullKey, err := types.NewTuple(nbf, vals...) 151 if err != nil { 152 return types.Tuple{}, types.Tuple{}, err 153 } 154 partialKey, err := types.NewTuple(nbf, vals[:idx.Count()*2]...) 155 if err != nil { 156 return types.Tuple{}, types.Tuple{}, err 157 } 158 return fullKey, partialKey, nil 159 } 160 161 // ReduceToIndexPartialKey creates an index record from a primary storage record. 162 func ReduceToIndexPartialKey(idx schema.Index, r Row) (types.Tuple, error) { 163 var vals []types.Value 164 for _, tag := range idx.IndexedColumnTags() { 165 val, ok := r.GetColVal(tag) 166 if !ok { 167 val = types.NullValue 168 } 169 vals = append(vals, types.Uint(tag), val) 170 } 171 return types.NewTuple(r.Format(), vals...) 172 } 173 174 func IsEmpty(r Row) (b bool) { 175 b = true 176 _, _ = r.IterCols(func(_ uint64, _ types.Value) (stop bool, err error) { 177 b = false 178 return true, nil 179 }) 180 return b 181 } 182 183 // IsValid returns whether the row given matches the types and satisfies all the constraints of the schema given. 184 func IsValid(r Row, sch schema.Schema) (bool, error) { 185 column, constraint, err := findInvalidCol(r, sch) 186 187 if err != nil { 188 return false, err 189 } 190 191 return column == nil && constraint == nil, nil 192 } 193 194 // GetInvalidCol returns the first column in the schema that fails a constraint, or nil if none do. 195 func GetInvalidCol(r Row, sch schema.Schema) (*schema.Column, error) { 196 badCol, _, err := findInvalidCol(r, sch) 197 return badCol, err 198 } 199 200 // GetInvalidConstraint returns the failed constraint for the row given (previously identified by IsValid) along with 201 // the column with that constraint. Note that if there is a problem with the row besides the constraint, the constraint 202 // return value will be nil. 203 func GetInvalidConstraint(r Row, sch schema.Schema) (*schema.Column, schema.ColConstraint, error) { 204 return findInvalidCol(r, sch) 205 } 206 207 // Returns the first encountered invalid column and its constraint, or nil if the row is valid. Column will always be 208 // set if the row is invalid. Constraint will be set if the first encountered problem is a constraint failure. 209 func findInvalidCol(r Row, sch schema.Schema) (*schema.Column, schema.ColConstraint, error) { 210 allCols := sch.GetAllCols() 211 212 var badCol *schema.Column 213 var badCnst schema.ColConstraint 214 err := allCols.Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 215 val, colSet := r.GetColVal(tag) 216 if colSet && !types.IsNull(val) && val.Kind() != col.Kind { 217 badCol = &col 218 return true, nil 219 } 220 221 if !col.TypeInfo.IsValid(val) { 222 badCol = &col 223 return true, fmt.Errorf(`"%v" is not valid for "%v"`, val, col.TypeInfo.String()) 224 } 225 226 if len(col.Constraints) > 0 { 227 for _, cnst := range col.Constraints { 228 if !cnst.SatisfiesConstraint(val) { 229 badCol = &col 230 badCnst = cnst 231 return true, nil 232 } 233 } 234 } 235 236 return false, nil 237 }) 238 239 return badCol, badCnst, err 240 } 241 242 func AreEqual(row1, row2 Row, sch schema.Schema) bool { 243 if row1 == nil && row2 == nil { 244 return true 245 } else if row1 == nil || row2 == nil { 246 return false 247 } 248 249 for _, tag := range sch.GetAllCols().Tags { 250 val1, _ := row1.GetColVal(tag) 251 val2, _ := row2.GetColVal(tag) 252 253 if !valutil.NilSafeEqCheck(val1, val2) { 254 return false 255 } 256 } 257 258 return true 259 }