func (t *txnBufState) updateRootsLocked(roots stringset.Set) error { curRootLen := t.roots.Len() proposedRoots := stringset.New(1) roots.Iter(func(root string) bool { if !t.roots.Has(root) { proposedRoots.Add(root) } return proposedRoots.Len()+curRootLen <= t.rootLimit }) if proposedRoots.Len()+curRootLen > t.rootLimit { return ErrTooManyRoots } // only need to update the roots if they did something that required updating if proposedRoots.Len() > 0 { proposedRoots.Iter(func(root string) bool { t.roots.Add(root) return true }) } return nil }
// maybeAddDefinition possibly adds a new indexDefinitionSortable to this slice. // It's only added if it could be useful in servicing q, otherwise this function // is a noop. // // This returns true iff the proposed index is OK and depletes missingTerms to // empty. // // If the proposed index is PERFECT (e.g. contains enough columns to cover all // equality filters, and also has the correct suffix), idxs will be replaced // with JUST that index, and this will return true. func (idxs *indexDefinitionSortableSlice) maybeAddDefinition(q *reducedQuery, s *memStore, missingTerms stringset.Set, id *ds.IndexDefinition) bool { // Kindless queries are handled elsewhere. if id.Kind != q.kind { impossible( fmt.Errorf("maybeAddDefinition given index with wrong kind %q v %q", id.Kind, q.kind)) } // If we're an ancestor query, and the index is compound, but doesn't include // an Ancestor field, it doesn't work. Builtin indexes can be used for // ancestor queries (and have !Ancestor), assuming that it's only equality // filters (plus inequality on __key__), or a single inequality. if q.eqFilters["__ancestor__"] != nil && !id.Ancestor && !id.Builtin() { impossible( fmt.Errorf("maybeAddDefinition given compound index with wrong ancestor info: %s %#v", id, q)) } // add __ancestor__ if necessary sortBy := id.GetFullSortOrder() // If the index has fewer fields than we need for the suffix, it can't // possibly help. if len(sortBy) < len(q.suffixFormat) { return false } numEqFilts := len(sortBy) - len(q.suffixFormat) // make sure the orders are precisely the same for i, sb := range sortBy[numEqFilts:] { if q.suffixFormat[i] != sb { return false } } if id.Builtin() && numEqFilts == 0 { if len(q.eqFilters) > 1 || (len(q.eqFilters) == 1 && q.eqFilters["__ancestor__"] == nil) { return false } if len(sortBy) > 1 && q.eqFilters["__ancestor__"] != nil { return false } } // Make sure the equalities section doesn't contain any properties we don't // want in our query. // // numByProp && totalEqFilts will be used to see if this is a perfect match // later. numByProp := make(map[string]int, len(q.eqFilters)) totalEqFilts := 0 eqFilts := sortBy[:numEqFilts] for _, p := range eqFilts { if _, ok := q.eqFilters[p.Property]; !ok { return false } numByProp[p.Property]++ totalEqFilts++ } // ok, we can actually use this // Grab the collection for convenience later. We don't want to invalidate this // index's potential just because the collection doesn't exist. If it's // a builtin and it doesn't exist, it still needs to be one of the 'possible' // indexes... it just means that the user's query will end up with no results. coll := s.GetCollection( fmt.Sprintf("idx:%s:%s", q.ns, serialize.ToBytes(*id.PrepForIdxTable()))) // First, see if it's a perfect match. If it is, then our search is over. // // A perfect match contains ALL the equality filter columns (or more, since // we can use residuals to fill in the extras). toAdd := indexDefinitionSortable{coll: coll} toAdd.eqFilts = eqFilts for _, sb := range toAdd.eqFilts { missingTerms.Del(sb.Property) } perfect := false if len(sortBy) == q.numCols { perfect = true for k, num := range numByProp { if num < q.eqFilters[k].Len() { perfect = false break } } } if perfect { *idxs = indexDefinitionSortableSlice{toAdd} } else { *idxs = append(*idxs, toAdd) } return missingTerms.Len() == 0 }