go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/service/datastore/multiarg.go (about)

     1  // Copyright 2015 The LUCI Authors.
     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 datastore
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"sort"
    21  	"sync"
    22  
    23  	"go.chromium.org/luci/common/errors"
    24  )
    25  
    26  type metaMultiArgConstraints int
    27  
    28  const (
    29  	// mmaReadWrite allows a metaMultiArg to operate on any type that can be
    30  	// both read and written to.
    31  	mmaReadWrite metaMultiArgConstraints = iota
    32  	// mmaKeysOnly implies mmaReadWrite, with the further statement that the only
    33  	// operation that will be performed against the arguments will be key
    34  	// extraction.
    35  	mmaKeysOnly = iota
    36  	// mmaWriteKeys indicates that the caller is only going to write key
    37  	// values. This enables the same inputs as mmaReadWrite, but also allows
    38  	// []*Key.
    39  	mmaWriteKeys = iota
    40  )
    41  
    42  func (c metaMultiArgConstraints) allowSingleKey() bool {
    43  	return c == mmaKeysOnly
    44  }
    45  
    46  func (c metaMultiArgConstraints) keyOperationsOnly() bool {
    47  	return c >= mmaKeysOnly
    48  }
    49  
    50  type multiArgType struct {
    51  	getMGS  func(slot reflect.Value) MetaGetterSetter
    52  	getPLS  func(slot reflect.Value) PropertyLoadSaver
    53  	newElem func() reflect.Value
    54  }
    55  
    56  func (mat *multiArgType) getKey(kc KeyContext, slot reflect.Value) (*Key, error) {
    57  	return kc.NewKeyFromMeta(mat.getMGS(slot))
    58  }
    59  
    60  func (mat *multiArgType) getPM(slot reflect.Value) (PropertyMap, error) {
    61  	return mat.getPLS(slot).Save(true)
    62  }
    63  
    64  func (mat *multiArgType) getMetaPM(slot reflect.Value) PropertyMap {
    65  	return mat.getMGS(slot).GetAllMeta()
    66  }
    67  
    68  func (mat *multiArgType) setPM(slot reflect.Value, pm PropertyMap) error {
    69  	return mat.getPLS(slot).Load(pm)
    70  }
    71  
    72  func (mat *multiArgType) setKey(slot reflect.Value, k *Key) bool {
    73  	return populateKeyMGS(mat.getMGS(slot), k)
    74  }
    75  
    76  // parseArg checks that et is of type S, *S, I, P or *P, for some
    77  // struct type S, for some interface type I, or some non-interface non-pointer
    78  // type P such that P or *P implements PropertyLoadSaver.
    79  //
    80  // If et is a chan type that implements PropertyLoadSaver, new elements will be
    81  // allocated with a buffer of 0.
    82  //
    83  // If allowKey is true, et may additional be type *Key. Only MetaGetterSetter
    84  // fields will be populated in the result (see keyMGS).
    85  func parseArg(et reflect.Type, allowKeys bool) *multiArgType {
    86  	var mat multiArgType
    87  
    88  	if et == typeOfKey {
    89  		if !allowKeys {
    90  			return nil
    91  		}
    92  
    93  		mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return &keyMGS{slot: slot} }
    94  		return &mat
    95  	}
    96  
    97  	// If we do identify a structCodec for this type, retain it so we don't
    98  	// resolve it multiple times.
    99  	var codec *structCodec
   100  	initCodec := func(t reflect.Type) *structCodec {
   101  		if codec == nil {
   102  			codec = getCodec(t)
   103  		}
   104  		return codec
   105  	}
   106  
   107  	// Fill in MetaGetterSetter functions.
   108  	switch {
   109  	case et.Implements(typeOfMetaGetterSetter):
   110  		// MGS
   111  		mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return slot.Interface().(MetaGetterSetter) }
   112  
   113  	case reflect.PtrTo(et).Implements(typeOfMetaGetterSetter):
   114  		// *MGS
   115  		mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return slot.Addr().Interface().(MetaGetterSetter) }
   116  
   117  	default:
   118  		switch et.Kind() {
   119  		case reflect.Interface:
   120  			// I
   121  			mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return getMGS(slot.Elem().Interface()) }
   122  
   123  		case reflect.Ptr:
   124  			// *S
   125  			if et.Elem().Kind() != reflect.Struct {
   126  				// Not a struct pointer.
   127  				return nil
   128  			}
   129  
   130  			initCodec(et.Elem())
   131  			mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return &structPLS{slot.Elem(), codec, nil} }
   132  
   133  		case reflect.Struct:
   134  			// S
   135  			initCodec(et)
   136  			mat.getMGS = func(slot reflect.Value) MetaGetterSetter { return &structPLS{slot, codec, nil} }
   137  
   138  		default:
   139  			// Don't know how to get MGS for this type.
   140  			return nil
   141  		}
   142  	}
   143  
   144  	// Fill in PropertyLoadSaver functions.
   145  	switch {
   146  	case et.Implements(typeOfPropertyLoadSaver):
   147  		// PLS
   148  		mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { return slot.Interface().(PropertyLoadSaver) }
   149  
   150  	case reflect.PtrTo(et).Implements(typeOfPropertyLoadSaver):
   151  		// *PLS
   152  		mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { return slot.Addr().Interface().(PropertyLoadSaver) }
   153  
   154  	default:
   155  		switch et.Kind() {
   156  		case reflect.Interface:
   157  			// I
   158  			mat.getPLS = func(slot reflect.Value) PropertyLoadSaver {
   159  				obj := slot.Elem().Interface()
   160  				if pls, ok := obj.(PropertyLoadSaver); ok {
   161  					return pls
   162  				}
   163  				return GetPLS(obj)
   164  			}
   165  
   166  		case reflect.Ptr:
   167  			// *S
   168  			if et.Elem().Kind() != reflect.Struct {
   169  				// Not a struct pointer.
   170  				return nil
   171  			}
   172  			initCodec(et.Elem())
   173  			mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { return &structPLS{slot.Elem(), codec, nil} }
   174  
   175  		case reflect.Struct:
   176  			// S
   177  			initCodec(et)
   178  			mat.getPLS = func(slot reflect.Value) PropertyLoadSaver { return &structPLS{slot, codec, nil} }
   179  
   180  		default:
   181  			// Don't know how to get PLS for this type.
   182  			return nil
   183  		}
   184  	}
   185  
   186  	// Generate new element.
   187  	//
   188  	// If a map/chan type implements an interface, its pointer is also considered
   189  	// to implement that interface.
   190  	//
   191  	// In this case, we have special pointer-to-map/chan logic in multiArgTypePLS.
   192  	mat.newElem = func() reflect.Value {
   193  		return reflect.New(et).Elem()
   194  	}
   195  
   196  	switch et.Kind() {
   197  	case reflect.Map:
   198  		mat.newElem = func() reflect.Value {
   199  			return reflect.MakeMap(et)
   200  		}
   201  
   202  	case reflect.Chan:
   203  		mat.newElem = func() reflect.Value {
   204  			return reflect.MakeChan(et, 0)
   205  		}
   206  
   207  	case reflect.Ptr:
   208  		elem := et.Elem()
   209  		switch elem.Kind() {
   210  		case reflect.Map:
   211  			mat.newElem = func() reflect.Value {
   212  				ptr := reflect.New(elem)
   213  				ptr.Elem().Set(reflect.MakeMap(elem))
   214  				return ptr
   215  			}
   216  
   217  		case reflect.Chan:
   218  			mat.newElem = func() reflect.Value {
   219  				ptr := reflect.New(elem)
   220  				ptr.Elem().Set(reflect.MakeChan(elem, 0))
   221  				return ptr
   222  			}
   223  
   224  		default:
   225  			mat.newElem = func() reflect.Value { return reflect.New(et.Elem()) }
   226  		}
   227  
   228  	case reflect.Interface:
   229  		mat.newElem = nil
   230  	}
   231  
   232  	return &mat
   233  }
   234  
   235  // mustParseMultiArg checks that v has type []S, []*S, []I, []P or []*P, for
   236  // some struct type S, for some interface type I, or some non-interface
   237  // non-pointer type P such that P or *P implements PropertyLoadSaver.
   238  func mustParseMultiArg(et reflect.Type) *multiArgType {
   239  	if et.Kind() != reflect.Slice {
   240  		panic(fmt.Errorf("invalid argument type: expected slice, got %s", et))
   241  	}
   242  	return mustParseArg(et.Elem(), true)
   243  }
   244  
   245  func mustParseArg(et reflect.Type, sliceArg bool) *multiArgType {
   246  	if mat := parseArg(et, false); mat != nil {
   247  		return mat
   248  	}
   249  	panic(fmt.Errorf("invalid argument type: %s is not a PLS or pointer-to-struct", et))
   250  }
   251  
   252  func isOKSingleType(t reflect.Type, allowKey bool) error {
   253  	switch {
   254  	case t == nil:
   255  		return errors.New("no type information")
   256  	case t.Implements(typeOfPropertyLoadSaver):
   257  		return nil
   258  	case !allowKey && t == typeOfKey:
   259  		return errors.New("not user datatype")
   260  
   261  	case t.Kind() != reflect.Ptr:
   262  		return errors.New("not a pointer")
   263  	case t.Elem().Kind() != reflect.Struct:
   264  		return errors.New("does not point to a struct")
   265  
   266  	default:
   267  		return nil
   268  	}
   269  }
   270  
   271  func isNilValue(t reflect.Type, v reflect.Value) bool {
   272  	k := t.Kind()
   273  	// `var v Interface = nil` and `var v *Struct = nil`.
   274  	if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() {
   275  		return true
   276  	}
   277  	// `var v Interface = (*Struct)nil`.
   278  	if k == reflect.Interface && v.Elem().IsNil() {
   279  		return true
   280  	}
   281  	return false
   282  }
   283  
   284  // keyMGS is a MetaGetterSetter that wraps a single key value/slot. It only
   285  // implements operations on the "key" key.
   286  //
   287  // GetMeta will be implemented, returning the *Key for the "key" meta.
   288  //
   289  // If slot is addressable, SetMeta will allow it to be set to the supplied
   290  // Value.
   291  type keyMGS struct {
   292  	slot reflect.Value
   293  }
   294  
   295  func (mgs *keyMGS) GetAllMeta() PropertyMap {
   296  	return PropertyMap{"$key": MkPropertyNI(mgs.slot.Interface())}
   297  }
   298  
   299  func (mgs *keyMGS) GetMeta(key string) (any, bool) {
   300  	if key != "key" {
   301  		return nil, false
   302  	}
   303  	return mgs.slot.Interface(), true
   304  }
   305  
   306  func (mgs *keyMGS) SetMeta(key string, value any) bool {
   307  	if !(key == "key" && mgs.slot.CanAddr()) {
   308  		return false
   309  	}
   310  	mgs.slot.Set(reflect.ValueOf(value))
   311  	return true
   312  }
   313  
   314  // metaMultiArgIndex is a two-dimensional index into a metaMultiArg.
   315  type metaMultiArgIndex struct {
   316  	// elem is the index of the element in a metaMultiArg.
   317  	elem int
   318  	// slot is the index within the specified element. If the element is not a
   319  	// slice, slot will always be 0.
   320  	slot int
   321  }
   322  
   323  type metaMultiArgElement struct {
   324  	arg reflect.Value
   325  	mat *multiArgType
   326  
   327  	// size is -1 if this element is not a slice.
   328  	size int
   329  	// offset is the offset of the first element in the flattened space.
   330  	offset int
   331  }
   332  
   333  // length returns the number of elements in this metaMultiArgElement.
   334  //
   335  // If it represents a slice, the number of elements will be the length of that
   336  // slice. If it represents a single value, the length will be 1.
   337  func (e *metaMultiArgElement) length() int {
   338  	if e.size >= 0 {
   339  		return e.size
   340  	}
   341  	return 1
   342  }
   343  
   344  type metaMultiArg struct {
   345  	// elems is the set of metaMultiArgElement entries, each of which represents
   346  	// either a single value or a slice of single values.
   347  	elems    []metaMultiArgElement
   348  	keysOnly bool
   349  
   350  	// count is the total number of elements, flattening slices.
   351  	count int
   352  	// flat, if true, means that this metaMultiArg consists entirely of single
   353  	// elements (no slices). This is used as a lookup optimization to avoid binary
   354  	// index search.
   355  	flat bool
   356  }
   357  
   358  // makeMetaMultiArg returns a metaMultiArg for the supplied args.
   359  //
   360  // If an arg is a slice, a slice metaMultiArg will be returned, and errors for
   361  // that slice will be written into a positional MultiError if they occur.
   362  //
   363  // If keysOnly is true, the caller is instructing metaMultiArg to only extract
   364  // the datastore *Key from args. *Key entries will be permitted, but the caller
   365  // may not write to them (since keys are read-only structs).
   366  func makeMetaMultiArg(args []any, c metaMultiArgConstraints) (*metaMultiArg, error) {
   367  	mma := metaMultiArg{
   368  		elems:    make([]metaMultiArgElement, len(args)),
   369  		keysOnly: c.keyOperationsOnly(),
   370  		flat:     true,
   371  	}
   372  
   373  	lme := errors.NewLazyMultiError(len(args))
   374  	for i, arg := range args {
   375  		if arg == nil {
   376  			lme.Assign(i, errors.New("cannot use nil as single argument"))
   377  			continue
   378  		}
   379  
   380  		v := reflect.ValueOf(arg)
   381  		vt := v.Type()
   382  
   383  		// The arg can be a "typed nil", they are not allowed either.
   384  		if isNilValue(vt, v) {
   385  			lme.Assign(i, errors.New("cannot use typed nil as single argument"))
   386  			continue
   387  		}
   388  
   389  		// Try and treat the argument as a single-value first. This allows slices
   390  		// that implement PropertyLoadSaver to be properly treated as a single
   391  		// element.
   392  		var (
   393  			mat     *multiArgType
   394  			err     error
   395  			isSlice = false
   396  		)
   397  		if mat = parseArg(vt, c.allowSingleKey()); mat == nil {
   398  			// If this is a slice, treat it as a slice of arg candidates.
   399  			//
   400  			// If only keys are being read/written, we allow a []*Key to be accepted
   401  			// here, since slices are addressable (write).
   402  			if v.Kind() == reflect.Slice {
   403  				isSlice = true
   404  				mma.flat = false
   405  				mat = parseArg(vt.Elem(), c.keyOperationsOnly())
   406  			}
   407  		} else {
   408  			// Single types need to be able to be assigned to.
   409  			//
   410  			// We only allow *Key here when the keys cannot be written to, since *Key
   411  			// should not be modified in-place, as they are immutable.
   412  			err = isOKSingleType(vt, c.allowSingleKey())
   413  		}
   414  		if mat == nil && err == nil {
   415  			err = errors.New("not a PLS, pointer-to-struct, or slice thereof")
   416  		}
   417  		if err != nil {
   418  			lme.Assign(i, fmt.Errorf("invalid input type (%T): %s", arg, err))
   419  			continue
   420  		}
   421  
   422  		// The type checks out, but there may be nils sneaking inside the slice.
   423  		if isSlice {
   424  			for sliceIdx := 0; sliceIdx < v.Len(); sliceIdx++ {
   425  				e := v.Index(sliceIdx)
   426  				if isNilValue(e.Type(), e) {
   427  					err = fmt.Errorf("invalid input slice: has nil at index %d", sliceIdx)
   428  					break
   429  				}
   430  			}
   431  			if err != nil {
   432  				lme.Assign(i, err)
   433  				continue
   434  			}
   435  		}
   436  
   437  		elem := &mma.elems[i]
   438  		*elem = metaMultiArgElement{
   439  			arg:    v,
   440  			mat:    mat,
   441  			offset: mma.count,
   442  		}
   443  		if isSlice {
   444  			l := v.Len()
   445  			mma.count += l
   446  			elem.size = l
   447  		} else {
   448  			mma.count++
   449  			elem.size = -1
   450  		}
   451  	}
   452  	if err := lme.Get(); err != nil {
   453  		return nil, err
   454  	}
   455  
   456  	return &mma, nil
   457  }
   458  
   459  func (mma *metaMultiArg) index(idx int) (mmaIdx metaMultiArgIndex) {
   460  	if mma.flat {
   461  		mmaIdx.elem = idx
   462  		return
   463  	}
   464  
   465  	mmaIdx.elem = sort.Search(len(mma.elems), func(i int) bool { return mma.elems[i].offset > idx }) - 1
   466  
   467  	// Get the current slot value.
   468  	mmaIdx.slot = idx - mma.elems[mmaIdx.elem].offset
   469  	return
   470  }
   471  
   472  // get returns the element type and value at flattened index idx.
   473  func (mma *metaMultiArg) get(idx metaMultiArgIndex) (*multiArgType, reflect.Value) {
   474  	// Get the current slot value.
   475  	elem := &mma.elems[idx.elem]
   476  	slot := elem.arg
   477  	if elem.size >= 0 {
   478  		// slot is a slice type, get its member.
   479  		slot = slot.Index(idx.slot)
   480  	}
   481  
   482  	return elem.mat, slot
   483  }
   484  
   485  // getKeysPMs returns the keys and PropertyMap for the supplied argument items.
   486  func (mma *metaMultiArg) getKeysPMs(kc KeyContext, meta bool) ([]*Key, []PropertyMap, *errorTracker) {
   487  	et := newErrorTracker(mma)
   488  
   489  	// Determine our flattened keys and property maps.
   490  	retKey := make([]*Key, mma.count)
   491  	var retPM []PropertyMap
   492  	if !mma.keysOnly {
   493  		retPM = make([]PropertyMap, mma.count)
   494  	}
   495  
   496  	var index metaMultiArgIndex
   497  	for i := 0; i < mma.count; i++ {
   498  		// If we're past the end of the element, move onto the next.
   499  		for index.slot >= mma.elems[index.elem].length() {
   500  			index.elem++
   501  			index.slot = 0
   502  		}
   503  
   504  		mat, slot := mma.get(index)
   505  		key, err := mat.getKey(kc, slot)
   506  		if err != nil {
   507  			et.trackError(index, err)
   508  			index.slot++
   509  			continue
   510  		}
   511  		retKey[i] = key
   512  
   513  		if !mma.keysOnly {
   514  			var pm PropertyMap
   515  			if meta {
   516  				pm = mat.getMetaPM(slot)
   517  			} else {
   518  				var err error
   519  				if pm, err = mat.getPM(slot); err != nil {
   520  					et.trackError(index, err)
   521  					index.slot++
   522  					continue
   523  				}
   524  			}
   525  			retPM[i] = pm
   526  		}
   527  
   528  		index.slot++
   529  	}
   530  	return retKey, retPM, et
   531  }
   532  
   533  type errorTracker struct {
   534  	sync.Mutex
   535  
   536  	elemErrors errors.MultiError
   537  	mma        *metaMultiArg
   538  }
   539  
   540  func newErrorTracker(mma *metaMultiArg) *errorTracker {
   541  	return &errorTracker{
   542  		mma: mma,
   543  	}
   544  }
   545  
   546  func (et *errorTracker) trackError(index metaMultiArgIndex, err error) {
   547  	if err == nil {
   548  		return
   549  	}
   550  
   551  	et.Lock()
   552  	defer et.Unlock()
   553  	et.trackErrorLocked(index, err)
   554  }
   555  
   556  func (et *errorTracker) trackErrorLocked(index metaMultiArgIndex, err error) {
   557  	if err == nil {
   558  		return
   559  	}
   560  
   561  	if et.elemErrors == nil {
   562  		et.elemErrors = make(errors.MultiError, len(et.mma.elems))
   563  	}
   564  
   565  	// If this is a single element, assign the error directly.
   566  	elem := &et.mma.elems[index.elem]
   567  	if elem.size < 0 {
   568  		et.elemErrors[index.elem] = err
   569  	} else {
   570  		// This is a slice element. Use a slice-sized MultiError for its element
   571  		// error slot, then add this error to the inner MultiError's slot index.
   572  		serr, ok := et.elemErrors[index.elem].(errors.MultiError)
   573  		if !ok {
   574  			serr = make(errors.MultiError, elem.size)
   575  			et.elemErrors[index.elem] = serr
   576  		}
   577  		serr[index.slot] = err
   578  	}
   579  }
   580  
   581  func (et *errorTracker) error() error {
   582  	if et.elemErrors != nil {
   583  		return et.elemErrors
   584  	}
   585  	return nil
   586  }
   587  
   588  type boolTracker struct {
   589  	*errorTracker
   590  
   591  	res ExistsResult
   592  }
   593  
   594  func newBoolTracker(mma *metaMultiArg, et *errorTracker) *boolTracker {
   595  	bt := boolTracker{
   596  		errorTracker: et,
   597  	}
   598  
   599  	sizes := make([]int, len(mma.elems))
   600  	for i, e := range mma.elems {
   601  		if e.size < 0 {
   602  			sizes[i] = 1
   603  		} else {
   604  			sizes[i] = e.size
   605  		}
   606  	}
   607  	bt.res.init(sizes...)
   608  	return &bt
   609  }
   610  
   611  func (bt *boolTracker) trackExistsResult(index metaMultiArgIndex, err error) {
   612  	bt.Lock()
   613  	defer bt.Unlock()
   614  
   615  	switch err {
   616  	case nil:
   617  		bt.res.set(index.elem, index.slot)
   618  
   619  	case ErrNoSuchEntity:
   620  		break
   621  
   622  	default:
   623  		// Pass through to track as MultiError.
   624  		bt.trackErrorLocked(index, err)
   625  	}
   626  }
   627  
   628  func (bt *boolTracker) result() *ExistsResult {
   629  	bt.res.updateSlices()
   630  	return &bt.res
   631  }