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  }