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  }