github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/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 // SetColVal 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 // NomsMapKey returns the noms map key for this row, using the schema provided. 52 NomsMapKey(sch schema.Schema) types.LesserValuable 53 54 // NomsMapValue returns the noms map value for this row, using the schema provided. 55 NomsMapValue(sch schema.Schema) types.Valuable 56 57 // NomsMapKeyTuple returns the noms map key tuple for this row, using the schema provided 58 NomsMapKeyTuple(sch schema.Schema, tf *types.TupleFactory) (types.Tuple, error) 59 60 // NomsMapValueTuple returns the noms map value tuple for this row, using the schema provided. 61 NomsMapValueTuple(sch schema.Schema, tf *types.TupleFactory) (types.Tuple, error) 62 63 // TaggedValues returns the row as TaggedValues. 64 TaggedValues() (TaggedValues, error) 65 66 // ReduceToIndexKeys returns full and partial index keys 67 ReduceToIndexKeys(idx schema.Index, tf *types.TupleFactory) (full types.Tuple, partial types.Tuple, value types.Tuple, err error) 68 } 69 70 func New(nbf *types.NomsBinFormat, sch schema.Schema, colVals TaggedValues) (Row, error) { 71 if schema.IsKeyless(sch) { 72 return keylessRowFromTaggedValued(nbf, sch, colVals) 73 } 74 return pkRowFromTaggedValues(nbf, sch, colVals) 75 } 76 77 func FromNoms(sch schema.Schema, nomsKey, nomsVal types.Tuple) (Row, error) { 78 if schema.IsKeyless(sch) { 79 row, _, err := KeylessRowsFromTuples(nomsKey, nomsVal) 80 return row, err 81 } 82 return pkRowFromNoms(sch, nomsKey, nomsVal) 83 } 84 85 // ToNoms returns the storage-layer tuples corresponding to |r|. 86 func ToNoms(ctx context.Context, sch schema.Schema, r Row) (key, val types.Tuple, err error) { 87 k, err := r.NomsMapKey(sch).Value(ctx) 88 if err != nil { 89 return key, val, err 90 } 91 92 v, err := r.NomsMapValue(sch).Value(ctx) 93 if err != nil { 94 return key, val, err 95 } 96 97 return k.(types.Tuple), v.(types.Tuple), nil 98 } 99 100 func GetFieldByName(colName string, r Row, sch schema.Schema) (types.Value, bool) { 101 col, ok := sch.GetAllCols().GetByName(colName) 102 103 if !ok { 104 panic("Requesting column that isn't in the schema. This is a bug. columns should be verified in the schema before attempted retrieval.") 105 } else { 106 return r.GetColVal(col.Tag) 107 } 108 } 109 110 func GetFieldByNameWithDefault(colName string, defVal types.Value, r Row, sch schema.Schema) types.Value { 111 col, ok := sch.GetAllCols().GetByName(colName) 112 113 if !ok { 114 panic("Requesting column that isn't in the schema. This is a bug. columns should be verified in the schema before attempted retrieval.") 115 } else { 116 val, ok := r.GetColVal(col.Tag) 117 118 if !ok { 119 return defVal 120 } 121 122 return val 123 } 124 } 125 126 // ReduceToIndexKeysFromTagMap creates a full key and a partial key from the given map of tags (first tuple being the 127 // full key). Please refer to the note in the index editor for more information regarding partial keys. 128 func ReduceToIndexKeysFromTagMap(nbf *types.NomsBinFormat, idx schema.Index, tagToVal map[uint64]types.Value, tf *types.TupleFactory) (types.Tuple, types.Tuple, error) { 129 vals := make([]types.Value, 0, len(idx.AllTags())*2) 130 for _, tag := range idx.AllTags() { 131 val, ok := tagToVal[tag] 132 if !ok { 133 val = types.NullValue 134 } 135 vals = append(vals, types.Uint(tag), val) 136 } 137 138 if tf == nil { 139 fullKey, err := types.NewTuple(nbf, vals...) 140 if err != nil { 141 return types.Tuple{}, types.Tuple{}, err 142 } 143 144 partialKey, err := types.NewTuple(nbf, vals[:idx.Count()*2]...) 145 if err != nil { 146 return types.Tuple{}, types.Tuple{}, err 147 } 148 149 return fullKey, partialKey, nil 150 } else { 151 fullKey, err := tf.Create(vals...) 152 if err != nil { 153 return types.Tuple{}, types.Tuple{}, err 154 } 155 156 partialKey, err := tf.Create(vals[:idx.Count()*2]...) 157 if err != nil { 158 return types.Tuple{}, types.Tuple{}, err 159 } 160 161 return fullKey, partialKey, nil 162 } 163 } 164 165 // ReduceToIndexPartialKey creates an index record from a primary storage record. 166 func ReduceToIndexPartialKey(tags []uint64, idx schema.Index, r Row) (types.Tuple, error) { 167 var vals []types.Value 168 if idx.Name() != "" { 169 tags = idx.IndexedColumnTags() 170 } 171 for _, tag := range tags { 172 val, ok := r.GetColVal(tag) 173 if !ok { 174 val = types.NullValue 175 } 176 vals = append(vals, types.Uint(tag), val) 177 } 178 179 return types.NewTuple(r.Format(), vals...) 180 } 181 182 func IsEmpty(r Row) (b bool) { 183 b = true 184 _, _ = r.IterCols(func(_ uint64, _ types.Value) (stop bool, err error) { 185 b = false 186 return true, nil 187 }) 188 return b 189 } 190 191 // IsValid returns whether the row given matches the types and satisfies all the constraints of the schema given. 192 func IsValid(r Row, sch schema.Schema) (bool, error) { 193 column, constraint, err := findInvalidCol(r, sch) 194 195 if err != nil { 196 return false, err 197 } 198 199 return column == nil && constraint == nil, nil 200 } 201 202 // GetInvalidCol returns the first column in the schema that fails a constraint, or nil if none do. 203 func GetInvalidCol(r Row, sch schema.Schema) (*schema.Column, error) { 204 badCol, _, err := findInvalidCol(r, sch) 205 return badCol, err 206 } 207 208 // GetInvalidConstraint returns the failed constraint for the row given (previously identified by IsValid) along with 209 // the column with that constraint. Note that if there is a problem with the row besides the constraint, the constraint 210 // return value will be nil. 211 func GetInvalidConstraint(r Row, sch schema.Schema) (*schema.Column, schema.ColConstraint, error) { 212 return findInvalidCol(r, sch) 213 } 214 215 // Returns the first encountered invalid column and its constraint, or nil if the row is valid. Column will always be 216 // set if the row is invalid. Constraint will be set if the first encountered problem is a constraint failure. 217 func findInvalidCol(r Row, sch schema.Schema) (*schema.Column, schema.ColConstraint, error) { 218 allCols := sch.GetAllCols() 219 220 var badCol *schema.Column 221 var badCnst schema.ColConstraint 222 err := allCols.Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 223 val, colSet := r.GetColVal(tag) 224 if colSet && !types.IsNull(val) && val.Kind() != col.Kind { 225 badCol = &col 226 return true, nil 227 } 228 229 if !col.TypeInfo.IsValid(val) { 230 badCol = &col 231 return true, fmt.Errorf(`"%v" is not valid for column "%s" (type "%s")`, val, col.Name, col.TypeInfo.ToSqlType().String()) 232 } 233 234 if len(col.Constraints) > 0 { 235 for _, cnst := range col.Constraints { 236 if !cnst.SatisfiesConstraint(val) { 237 badCol = &col 238 badCnst = cnst 239 return true, nil 240 } 241 } 242 } 243 244 return false, nil 245 }) 246 247 return badCol, badCnst, err 248 } 249 250 func AreEqual(row1, row2 Row, sch schema.Schema) bool { 251 if row1 == nil && row2 == nil { 252 return true 253 } else if row1 == nil || row2 == nil { 254 return false 255 } 256 257 for _, tag := range sch.GetAllCols().Tags { 258 val1, _ := row1.GetColVal(tag) 259 val2, _ := row2.GetColVal(tag) 260 261 if !valutil.NilSafeEqCheck(val1, val2) { 262 return false 263 } 264 } 265 266 return true 267 } 268 269 func TaggedValsEqualForSch(tv, other TaggedValues, sch schema.Schema) bool { 270 if tv == nil && other == nil { 271 return true 272 } else if tv == nil || other == nil { 273 return false 274 } 275 276 for _, tag := range sch.GetAllCols().Tags { 277 val1, _ := tv[tag] 278 val2, _ := other[tag] 279 280 if !valutil.NilSafeEqCheck(val1, val2) { 281 return false 282 } 283 } 284 285 return true 286 } 287 288 func KeyAndTaggedValuesForRow(r Row, sch schema.Schema) (types.Tuple, TaggedValues, error) { 289 switch typed := r.(type) { 290 case nomsRow: 291 pkCols := sch.GetPKCols() 292 keyVals := make([]types.Value, 0, pkCols.Size()*2) 293 tv := make(TaggedValues) 294 err := pkCols.Iter(func(tag uint64, col schema.Column) (stop bool, err error) { 295 val, ok := typed.key[tag] 296 if !ok || types.IsNull(val) { 297 return false, errors.New("invalid key contains null values") 298 } 299 300 tv[tag] = val 301 keyVals = append(keyVals, types.Uint(tag)) 302 keyVals = append(keyVals, val) 303 return false, nil 304 }) 305 306 if err != nil { 307 return types.Tuple{}, nil, err 308 } 309 310 nonPkCols := sch.GetNonPKCols() 311 _, err = typed.value.Iter(func(tag uint64, val types.Value) (stop bool, err error) { 312 if _, ok := nonPkCols.TagToIdx[tag]; ok { 313 tv[tag] = val 314 } 315 316 return false, nil 317 }) 318 319 if err != nil { 320 return types.Tuple{}, nil, err 321 } 322 323 t, err := types.NewTuple(r.Format(), keyVals...) 324 if err != nil { 325 return types.Tuple{}, nil, err 326 } 327 328 return t, tv, nil 329 330 case keylessRow: 331 tv, err := typed.TaggedValues() 332 if err != nil { 333 return types.Tuple{}, nil, err 334 } 335 336 return typed.key, tv, nil 337 338 default: 339 panic("unknown row type") 340 } 341 }