Skip to content
This repository was archived by the owner on Feb 21, 2024. It is now read-only.

Commit 2b4d49e

Browse files
committed
standardize existence-tracking logic a bit better
We distinguish between "TrackExistence option is set" and "we are actually doing existence tracking", to avoid mishaps like accidentally creating an "existence" view for a BSI field or something like that. This logic was being done probably-correctly in one place, and ignored or handwaved in some, so this is an attempt to just make it more consistent.
1 parent a3a0de2 commit 2b4d49e

2 files changed

Lines changed: 54 additions & 24 deletions

File tree

batch/batch.go

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
const (
2424
DefaultKeyTranslateBatchSize = 100000
2525
existenceFieldName = "_exists"
26+
existenceViewName = "existence" // this should match top level featurebase viewExistence
2627
)
2728

2829
// TODO if using column translation, column ids might get way out of
@@ -573,6 +574,10 @@ func (b *Batch) Add(rec Row) error {
573574
case int64:
574575
b.values[field.Name] = append(b.values[field.Name], val)
575576
case []string:
577+
// note that a length of 0 can be valid, and represents an
578+
// empty set. an empty set counts as a non-NULL value for
579+
// SQL purposes -- it means the existence view bit should
580+
// get set.
576581
if val == nil {
577582
continue
578583
}
@@ -608,7 +613,10 @@ func (b *Batch) Add(rec Row) error {
608613
}
609614
b.rowIDSets[field.Name] = append(rowIDSets, rowIDs)
610615
case []uint64:
611-
// if length is 0, that's still a valid, empty, set
616+
// note that a length of 0 can be valid, and represents an
617+
// empty set. an empty set counts as a non-NULL value for
618+
// SQL purposes -- it means the existence view bit should
619+
// get set.
612620
if val == nil {
613621
continue
614622
}
@@ -1363,8 +1371,8 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
13631371
// the API treats "" as standard
13641372
curBM = frags.GetOrCreate(curShard, field.Name, "")
13651373
clearBM = clearFrags.GetOrCreate(curShard, field.Name, "")
1366-
if opts.TrackExistence {
1367-
existCurBM = frags.GetOrCreate(curShard, field.Name, "existence")
1374+
if opts.ActuallyTrackingExistence() {
1375+
existCurBM = frags.GetOrCreate(curShard, field.Name, existenceViewName)
13681376
}
13691377
}
13701378
if row != nilSentinel {
@@ -1375,7 +1383,7 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
13751383
// the NoStandardView case would be great.
13761384
if !(opts.Type == featurebase.FieldTypeTime && opts.NoStandardView) {
13771385
curBM.DirectAdd(row*shardWidth + (col % shardWidth))
1378-
if opts.TrackExistence {
1386+
if opts.ActuallyTrackingExistence() {
13791387
existCurBM.DirectAdd(col % shardWidth)
13801388
}
13811389
}
@@ -1398,9 +1406,14 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
13981406
// we want to make sure that at this point, the "set"
13991407
// fragments don't contain the bit that we're clearing
14001408
curBM.DirectRemoveN(clearRow*shardWidth + (col % shardWidth))
1401-
// don't set the existence bit, probably? i don't actually quite
1402-
// understand the higher level semantics here.
1403-
if opts.TrackExistence {
1409+
// Because this is RowIDs, not RowIDSets, there's only one
1410+
// bit. We should not be setting the existence bit based on
1411+
// this value, if we're actually clearing it. This doesn't
1412+
// mean we will clear an existing existence bit, though.
1413+
// The case where we would clear an existence bit is the
1414+
// case where someone specified row[mutexField].Clears = nil,
1415+
// which is far from here.
1416+
if opts.ActuallyTrackingExistence() {
14041417
existCurBM.DirectRemoveN(col % shardWidth)
14051418
}
14061419
}
@@ -1427,13 +1440,14 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
14271440
if col/shardWidth != curShard {
14281441
curShard = col / shardWidth
14291442
curBM = frags.GetOrCreate(curShard, fname, "")
1430-
if opts.TrackExistence {
1431-
existCurBM = frags.GetOrCreate(curShard, fname, "existence")
1443+
if opts.ActuallyTrackingExistence() {
1444+
existCurBM = frags.GetOrCreate(curShard, fname, existenceViewName)
14321445
}
14331446
}
14341447
if len(rowIDs) == 0 {
1435-
// you can validly specify an empty set, which is not the same as a null
1436-
if opts.TrackExistence && !(opts.Type == featurebase.FieldTypeTime && opts.NoStandardView) && rowIDs != nil {
1448+
// you can validly specify an empty set, which is not the same as a null,
1449+
// but which still ought to set the existence bit if we're tracking that.
1450+
if opts.ActuallyTrackingExistence() && rowIDs != nil {
14371451
existCurBM.DirectAdd(col % shardWidth)
14381452
}
14391453
continue
@@ -1447,7 +1461,7 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
14471461
for _, row := range rowIDs {
14481462
curBM.DirectAdd(row*shardWidth + (col % shardWidth))
14491463
}
1450-
if opts.TrackExistence {
1464+
if opts.ActuallyTrackingExistence() {
14511465
existCurBM.DirectAdd(col % shardWidth)
14521466
}
14531467
}
@@ -1578,9 +1592,9 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
15781592
bitmap := frags.GetOrCreate(shard, field.Name, "standard")
15791593
clearBM := clearFrags.GetOrCreate(shard, field.Name, "standard")
15801594
var existBM, existClearBM *roaring.Bitmap
1581-
if field.Options.TrackExistence {
1582-
existBM = frags.GetOrCreate(shard, field.Name, "existence")
1583-
existClearBM = clearFrags.GetOrCreate(shard, field.Name, "existence")
1595+
if field.Options.ActuallyTrackingExistence() {
1596+
existBM = frags.GetOrCreate(shard, field.Name, existenceViewName)
1597+
existClearBM = clearFrags.GetOrCreate(shard, field.Name, existenceViewName)
15841598
}
15851599
for i, id := range ids {
15861600
if i+1 < len(ids) {
@@ -1594,9 +1608,9 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
15941608
shard = id / shardWidth
15951609
bitmap = frags.GetOrCreate(shard, field.Name, "standard")
15961610
clearBM = clearFrags.GetOrCreate(shard, field.Name, "standard")
1597-
if field.Options.TrackExistence {
1598-
existBM = frags.GetOrCreate(shard, field.Name, "existence")
1599-
existClearBM = clearFrags.GetOrCreate(shard, field.Name, "existence")
1611+
if field.Options.ActuallyTrackingExistence() {
1612+
existBM = frags.GetOrCreate(shard, field.Name, existenceViewName)
1613+
existClearBM = clearFrags.GetOrCreate(shard, field.Name, existenceViewName)
16001614
}
16011615
}
16021616
fragmentColumn := id % shardWidth
@@ -1605,10 +1619,10 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
16051619
// clearSentinel is used for deletion
16061620
// so this value should only be added if its not clearSentinel
16071621
bitmap.Add(row*shardWidth + fragmentColumn)
1608-
if field.Options.TrackExistence {
1622+
if field.Options.ActuallyTrackingExistence() {
16091623
existBM.Add(fragmentColumn)
16101624
}
1611-
} else if field.Options.TrackExistence {
1625+
} else if field.Options.ActuallyTrackingExistence() {
16121626
existClearBM.Add(fragmentColumn)
16131627
}
16141628
}
@@ -1638,8 +1652,8 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
16381652
fragmentColumn := recID % shardWidth
16391653

16401654
clearBM.Add(fragmentColumn)
1641-
if field.Options.TrackExistence {
1642-
existClearBM := clearFrags.GetOrCreate(shard, field.Name, "existence")
1655+
if field.Options.ActuallyTrackingExistence() {
1656+
existClearBM := clearFrags.GetOrCreate(shard, field.Name, existenceViewName)
16431657

16441658
existClearBM.Add(fragmentColumn)
16451659
}
@@ -1665,8 +1679,8 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
16651679

16661680
fragmentColumn := recID % shardWidth
16671681
clearBM.Add(fragmentColumn)
1668-
if field.Options.TrackExistence {
1669-
exist := frags.GetOrCreate(shard, field.Name, "existence")
1682+
if field.Options.ActuallyTrackingExistence() {
1683+
exist := frags.GetOrCreate(shard, field.Name, existenceViewName)
16701684
exist.Add(fragmentColumn)
16711685
}
16721686

field.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2233,6 +2233,22 @@ func applyDefaultOptions(o *FieldOptions) FieldOptions {
22332233
return *o
22342234
}
22352235

2236+
// ActuallyTrackingExistence reflects the distinction between the
2237+
// TrackExistence bool, which is enabled by default for most fields,
2238+
// and whether we actually do existence tracking. Specifically,
2239+
// we don't do existence tracking for time quantum fields which don't
2240+
// have a standard view, or for BSI fields.
2241+
func (o *FieldOptions) ActuallyTrackingExistence() bool {
2242+
switch o.Type {
2243+
case FieldTypeTime:
2244+
return o.TrackExistence && !o.NoStandardView
2245+
case FieldTypeInt, FieldTypeDecimal, FieldTypeTimestamp:
2246+
return false
2247+
default:
2248+
return o.TrackExistence
2249+
}
2250+
}
2251+
22362252
// MarshalJSON marshals FieldOptions to JSON such that
22372253
// only those attributes associated to the field type
22382254
// are included.

0 commit comments

Comments
 (0)