mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
Updated with master
This commit is contained in:
commit
37d1cfbbd7
7 changed files with 1691 additions and 1227 deletions
|
|
@ -266,6 +266,40 @@ ClearRow(stargazer=1)
|
|||
|
||||
This represents removing the relationship between the user with id=1 and all repositories.
|
||||
|
||||
#### Store
|
||||
|
||||
**Spec:**
|
||||
|
||||
```
|
||||
Store(<ROW_CALL>, <FIELD>=<ROW>)
|
||||
```
|
||||
|
||||
**Description:**
|
||||
|
||||
`Store` writes the results of <ROW_CALL> to the specified row. If the row already exists, it will be replaced. The destination field must be of field type `set`.
|
||||
|
||||
**Result Type:** boolean
|
||||
|
||||
Upon success, this method always returns `true`. A future version of Pilosa may use this boolean result to indicate whether or not the data in the destination row was changed by the `Store` call.
|
||||
|
||||
**Examples:**
|
||||
|
||||
Store the contents of stargazer row 1 into stargazer row 2:
|
||||
```request
|
||||
Store(Row(stargazer=1), stargazer=2)
|
||||
```
|
||||
```response
|
||||
{"results":[true]}
|
||||
```
|
||||
|
||||
Store the results of the intersection of stargazer rows 10 and 11 into stargazer row 20.
|
||||
```request
|
||||
Store(Intersect(Row(stargazer=10), Row(stargazer=11)), stargazer=20)
|
||||
```
|
||||
```response
|
||||
{"results":[true]}
|
||||
```
|
||||
|
||||
### Read Operations
|
||||
|
||||
#### Row
|
||||
|
|
|
|||
104
executor.go
104
executor.go
|
|
@ -243,6 +243,8 @@ func (e *executor) executeCall(ctx context.Context, index string, c *pql.Call, s
|
|||
return e.executeClearBit(ctx, index, c, opt)
|
||||
case "ClearRow":
|
||||
return e.executeClearRow(ctx, index, c, shards, opt)
|
||||
case "Store":
|
||||
return e.executeSetRow(ctx, index, c, shards, opt)
|
||||
case "Count":
|
||||
e.Holder.Stats.CountWithCustomTags(c.Name, 1, 1.0, []string{indexTag})
|
||||
return e.executeCount(ctx, index, c, shards, opt)
|
||||
|
|
@ -1272,6 +1274,94 @@ func (e *executor) executeClearRowShard(_ context.Context, index string, c *pql.
|
|||
return changed, nil
|
||||
}
|
||||
|
||||
// executeSetRow executes a SetRow() call.
|
||||
func (e *executor) executeSetRow(ctx context.Context, index string, c *pql.Call, shards []uint64, opt *execOptions) (bool, error) {
|
||||
// Ensure the field type supports SetRow().
|
||||
fieldName, err := c.FieldArg()
|
||||
if err != nil {
|
||||
return false, errors.New("SetRow() argument required: field")
|
||||
}
|
||||
field := e.Holder.Field(index, fieldName)
|
||||
if field == nil {
|
||||
return false, ErrFieldNotFound
|
||||
}
|
||||
if field.Type() != FieldTypeSet {
|
||||
return false, fmt.Errorf("SetRow() is not supported on %s field types", field.Type())
|
||||
}
|
||||
|
||||
// Execute calls in bulk on each remote node and merge.
|
||||
mapFn := func(shard uint64) (interface{}, error) {
|
||||
return e.executeSetRowShard(ctx, index, c, shard)
|
||||
}
|
||||
|
||||
// Merge returned results at coordinating node.
|
||||
reduceFn := func(prev, v interface{}) interface{} {
|
||||
val := v.(bool)
|
||||
if prev == nil {
|
||||
return val
|
||||
}
|
||||
return val || prev.(bool)
|
||||
}
|
||||
|
||||
result, err := e.mapReduce(ctx, index, shards, c, opt, mapFn, reduceFn)
|
||||
return result.(bool), err
|
||||
}
|
||||
|
||||
// executeSetRowShard executes a SetRow() call for a single shard.
|
||||
func (e *executor) executeSetRowShard(ctx context.Context, index string, c *pql.Call, shard uint64) (bool, error) {
|
||||
fieldName, err := c.FieldArg()
|
||||
if err != nil {
|
||||
return false, errors.New("SetRow() argument required: field")
|
||||
}
|
||||
|
||||
// Read fields using labels.
|
||||
rowID, ok, err := c.UintArg(fieldName)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("reading SetRow() row: %v", err)
|
||||
} else if !ok {
|
||||
return false, fmt.Errorf("SetRow() row argument '%v' required", rowLabel)
|
||||
}
|
||||
|
||||
field := e.Holder.Field(index, fieldName)
|
||||
if field == nil {
|
||||
return false, ErrFieldNotFound
|
||||
}
|
||||
|
||||
// Retrieve source row.
|
||||
var src *Row
|
||||
if len(c.Children) == 1 {
|
||||
row, err := e.executeBitmapCallShard(ctx, index, c.Children[0], shard)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "getting source row")
|
||||
}
|
||||
src = row
|
||||
} else {
|
||||
return false, errors.New("SetRow() requires a source row")
|
||||
}
|
||||
|
||||
// Set the row on the standard view.
|
||||
changed := false
|
||||
fragment := e.Holder.fragment(index, fieldName, viewStandard, shard)
|
||||
if fragment == nil {
|
||||
// Since the destination fragment doesn't exist, create one.
|
||||
view, err := field.createViewIfNotExists(viewStandard)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "creating view")
|
||||
}
|
||||
fragment, err = view.createFragmentIfNotExists(shard)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "creating fragment: %d", shard)
|
||||
}
|
||||
}
|
||||
set, err := fragment.setRow(src, rowID)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "setting row %d on view %s shard %d", rowID, viewStandard, shard)
|
||||
}
|
||||
changed = changed || set
|
||||
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
// executeSet executes a Set() call.
|
||||
func (e *executor) executeSet(ctx context.Context, index string, c *pql.Call, opt *execOptions) (bool, error) {
|
||||
// Read colID.
|
||||
|
|
@ -1765,25 +1855,19 @@ func (e *executor) mapperLocal(ctx context.Context, shards []uint64, mapFn mapFu
|
|||
}
|
||||
}
|
||||
|
||||
var translateCallCol = map[string]struct{}{
|
||||
"Set": {},
|
||||
"Clear": {},
|
||||
"Row": {},
|
||||
"SetColumnAttrs": {},
|
||||
}
|
||||
|
||||
func (e *executor) translateCall(index string, idx *Index, c *pql.Call) error {
|
||||
var colKey, rowKey, fieldName string
|
||||
if _, ok := translateCallCol[c.Name]; ok {
|
||||
switch c.Name {
|
||||
case "Set", "Clear", "Row", "Range", "SetColumnAttrs":
|
||||
// Positional args in new PQL syntax require special handling here.
|
||||
colKey = "_" + columnLabel
|
||||
fieldName, _ = c.FieldArg()
|
||||
rowKey = fieldName
|
||||
} else if c.Name == "SetRowAttrs" {
|
||||
case "SetRowAttrs":
|
||||
// Positional args in new PQL syntax require special handling here.
|
||||
rowKey = "_" + rowLabel
|
||||
fieldName = callArgString(c, "_field")
|
||||
} else {
|
||||
default:
|
||||
colKey = "col"
|
||||
fieldName = callArgString(c, "field")
|
||||
rowKey = "row"
|
||||
|
|
|
|||
191
executor_test.go
191
executor_test.go
|
|
@ -1020,6 +1020,58 @@ func TestExecutor_Execute_Range(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// Ensure a range query with keys can be executed.
|
||||
func TestExecutor_Execute_Range_WithKeys(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
defer c.Close()
|
||||
hldr := test.Holder{Holder: c[0].Server.Holder()}
|
||||
|
||||
// Create index.
|
||||
index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{})
|
||||
|
||||
// Create field.
|
||||
if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeTime(pilosa.TimeQuantum("YMDH")), pilosa.OptFieldKeys()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set columns.
|
||||
cc := `
|
||||
Set(2, f="foo", 1999-12-31T00:00)
|
||||
Set(3, f="foo", 2000-01-01T00:00)
|
||||
Set(4, f="foo", 2000-01-02T00:00)
|
||||
Set(5, f="foo", 2000-02-01T00:00)
|
||||
Set(6, f="foo", 2001-01-01T00:00)
|
||||
Set(7, f="foo", 2002-01-01T02:00)
|
||||
|
||||
Set(2, f="foo", 1999-12-30T00:00)
|
||||
Set(2, f="foo", 2002-02-01T00:00)
|
||||
Set(2, f="bar", 2001-01-01T00:00)
|
||||
`
|
||||
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: cc}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("Standard", func(t *testing.T) {
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Range(f="foo", 1999-12-31T00:00, 2002-01-01T03:00)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if columns := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(columns, []uint64{2, 3, 4, 5, 6, 7}) {
|
||||
t.Fatalf("unexpected columns: %+v", columns)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Clear", func(t *testing.T) {
|
||||
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Clear( 2, f="foo")`}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Range(f="foo", 1999-12-31T00:00, 2002-01-01T03:00)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if columns := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(columns, []uint64{3, 4, 5, 6, 7}) {
|
||||
t.Fatalf("unexpected columns: %+v", columns)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure a Range(bsiGroup) query can be executed.
|
||||
func TestExecutor_Execute_BSIGroupRange(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
|
|
@ -1943,6 +1995,145 @@ func TestExecutor_Execute_ClearRow(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// Ensure a row can be set.
|
||||
func TestExecutor_Execute_SetRow(t *testing.T) {
|
||||
t.Run("Set_NewRow", func(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
defer c.Close()
|
||||
hldr := test.Holder{Holder: c[0].Server.Holder()}
|
||||
index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{TrackExistence: true})
|
||||
if _, err := index.CreateField("f", pilosa.OptFieldTypeDefault()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := index.CreateField("tmp", pilosa.OptFieldTypeDefault()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set bits.
|
||||
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `` +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", 3, 10) +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", ShardWidth-1, 10) +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", ShardWidth+1, 10),
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(f=10)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{3, ShardWidth - 1, ShardWidth + 1}) {
|
||||
t.Fatalf("unexpected columns: %+v", bits)
|
||||
}
|
||||
|
||||
// Store row 10 into a different row.
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Store(Row(f=10), tmp=20)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if res := res.Results[0].(bool); !res {
|
||||
t.Fatalf("unexpected set row result: %+v", res)
|
||||
}
|
||||
|
||||
// Ensure the row was populated.
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(tmp=20)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{3, ShardWidth - 1, ShardWidth + 1}) {
|
||||
t.Fatalf("unexpected columns: %+v", bits)
|
||||
}
|
||||
})
|
||||
t.Run("Set_NoSource", func(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
defer c.Close()
|
||||
hldr := test.Holder{Holder: c[0].Server.Holder()}
|
||||
index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{TrackExistence: true})
|
||||
_, err := index.CreateField("f", pilosa.OptFieldTypeDefault())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set bits.
|
||||
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `` +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", 3, 10) +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", ShardWidth-1, 10) +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", ShardWidth+1, 10),
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(f=10)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{3, ShardWidth - 1, ShardWidth + 1}) {
|
||||
t.Fatalf("unexpected columns: %+v", bits)
|
||||
}
|
||||
|
||||
// Store row 9 (which doesn't exist) into a different row.
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Store(Row(f=9), f=20)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if res := res.Results[0].(bool); !res {
|
||||
t.Fatalf("unexpected set row result: %+v", res)
|
||||
}
|
||||
|
||||
// Ensure the row was populated.
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(f=20)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{}) {
|
||||
t.Fatalf("unexpected columns: %+v", bits)
|
||||
}
|
||||
|
||||
// Store row 9 (which doesn't exist) into a row that does exist.
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Store(Row(f=9), f=10)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if res := res.Results[0].(bool); !res {
|
||||
t.Fatalf("unexpected set row result: %+v", res)
|
||||
}
|
||||
|
||||
// Ensure the row was populated.
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(f=10)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{}) {
|
||||
t.Fatalf("unexpected columns: %+v", bits)
|
||||
}
|
||||
})
|
||||
t.Run("Set_ExistingDestination", func(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
defer c.Close()
|
||||
hldr := test.Holder{Holder: c[0].Server.Holder()}
|
||||
index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{TrackExistence: true})
|
||||
_, err := index.CreateField("f", pilosa.OptFieldTypeDefault())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set bits.
|
||||
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `` +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", 3, 10) +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", ShardWidth-1, 10) +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", ShardWidth+1, 10) +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", 1, 20) +
|
||||
fmt.Sprintf("Set(%d, f=%d)\n", ShardWidth+1, 20),
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(f=20)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{1, ShardWidth + 1}) {
|
||||
t.Fatalf("unexpected columns: %+v", bits)
|
||||
}
|
||||
|
||||
// Store row 10 into an existing row.
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Store(Row(f=10), f=20)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if res := res.Results[0].(bool); !res {
|
||||
t.Fatalf("unexpected set row result: %+v", res)
|
||||
}
|
||||
|
||||
// Ensure the row was populated.
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(f=20)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{3, ShardWidth - 1, ShardWidth + 1}) {
|
||||
t.Fatalf("unexpected columns: %+v", bits)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmarkExistence(nn bool, b *testing.B) {
|
||||
c := test.MustRunCluster(b, 1)
|
||||
defer c.Close()
|
||||
|
|
|
|||
54
fragment.go
54
fragment.go
|
|
@ -352,11 +352,11 @@ func (f *fragment) unprotectedRow(rowID uint64) *Row {
|
|||
}
|
||||
|
||||
// Only use a subset of the containers.
|
||||
// NOTE: The start & end ranges must be divisible by
|
||||
// NOTE: The start & end ranges must be divisible by container width.
|
||||
data := f.storage.OffsetRange(f.shard*ShardWidth, rowID*ShardWidth, (rowID+1)*ShardWidth)
|
||||
|
||||
// Reference bitmap subrange in storage.
|
||||
// We Clone() data because otherwise row will contains pointers to containers in storage.
|
||||
// We Clone() data because otherwise row will contain pointers to containers in storage.
|
||||
// This causes unexpected results when we cache the row and try to use it later.
|
||||
row := &Row{
|
||||
segments: []rowSegment{{
|
||||
|
|
@ -491,6 +491,56 @@ func (f *fragment) unprotectedClearBit(rowID, columnID uint64) (changed bool, er
|
|||
return changed, nil
|
||||
}
|
||||
|
||||
// setRow replaces an existing row (specified by rowID) with the given
|
||||
// Row. This updates both the on-disk storage and the in-cache bitmap.
|
||||
func (f *fragment) setRow(row *Row, rowID uint64) (bool, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return f.unprotectedSetRow(row, rowID)
|
||||
}
|
||||
|
||||
func (f *fragment) unprotectedSetRow(row *Row, rowID uint64) (changed bool, err error) {
|
||||
// TODO: In order to return `changed`, we need to first compare
|
||||
// the existing row with the given row. Determine if the overhead
|
||||
// of this is worth having `changed`.
|
||||
// For now we will assume changed is always true.
|
||||
changed = true
|
||||
|
||||
// First container of the row in storage.
|
||||
headContainerKey := rowID << shardVsContainerExponent
|
||||
|
||||
// Remove every existing container in the row.
|
||||
for i := uint64(0); i < (1 << shardVsContainerExponent); i++ {
|
||||
f.storage.Containers.Remove(headContainerKey + i)
|
||||
}
|
||||
|
||||
// From the given row, get the rowSegment for this shard.
|
||||
seg := row.segment(f.shard)
|
||||
if seg == nil {
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
// Put each container from rowSegment to fragment storage.
|
||||
citer, _ := seg.data.Containers.Iterator(f.shard << shardVsContainerExponent)
|
||||
for citer.Next() {
|
||||
k, c := citer.Value()
|
||||
f.storage.Containers.Put(headContainerKey+(k%(1<<shardVsContainerExponent)), c)
|
||||
}
|
||||
|
||||
// Update the row in cache.
|
||||
n := f.storage.CountRange(rowID*ShardWidth, (rowID+1)*ShardWidth)
|
||||
f.cache.BulkAdd(rowID, n)
|
||||
|
||||
// Snapshot storage.
|
||||
if err := f.snapshot(); err != nil {
|
||||
return false, errors.Wrap(err, "snapshotting")
|
||||
}
|
||||
|
||||
f.stats.Count("setRow", 1, 1.0)
|
||||
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
// ClearRow clears a row for a given rowID within the fragment.
|
||||
// This updates both the on-disk storage and the in-cache bitmap.
|
||||
func (f *fragment) clearRow(rowID uint64) (bool, error) {
|
||||
|
|
|
|||
|
|
@ -122,6 +122,54 @@ func TestFragment_ClearRow(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure a fragment can set a row.
|
||||
func TestFragment_SetRow(t *testing.T) {
|
||||
f := mustOpenFragment("i", "f", viewStandard, 7, "")
|
||||
defer f.Close()
|
||||
|
||||
rowID := uint64(1000)
|
||||
|
||||
// Set bits on the fragment.
|
||||
if _, err := f.setBit(rowID, 8000001); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if _, err := f.setBit(rowID, 8065536); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Verify data on row.
|
||||
if cols := f.row(rowID).Columns(); !reflect.DeepEqual(cols, []uint64{8000001, 8065536}) {
|
||||
t.Fatalf("unexpected columns: %+v", cols)
|
||||
}
|
||||
// Verify count on row.
|
||||
if n := f.row(rowID).Count(); n != 2 {
|
||||
t.Fatalf("unexpected count: %d", n)
|
||||
}
|
||||
|
||||
// Set row (overwrite existing data).
|
||||
row := NewRow(8000002, 8065537, 8131074)
|
||||
if changed, err := f.unprotectedSetRow(row, rowID); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !changed {
|
||||
t.Fatalf("expected changed value: %v", changed)
|
||||
}
|
||||
|
||||
// Verify data on row.
|
||||
if cols := f.row(rowID).Columns(); !reflect.DeepEqual(cols, []uint64{8000002, 8065537, 8131074}) {
|
||||
t.Fatalf("unexpected columns after set row: %+v", cols)
|
||||
}
|
||||
// Verify count on row.
|
||||
if n := f.row(rowID).Count(); n != 3 {
|
||||
t.Fatalf("unexpected count after set row: %d", n)
|
||||
}
|
||||
|
||||
// Close and reopen the fragment & verify the data.
|
||||
if err := f.reopen(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n := f.row(rowID).Count(); n != 3 {
|
||||
t.Fatalf("unexpected count (reopen): %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure a fragment can set & read a value.
|
||||
func TestFragment_SetValue(t *testing.T) {
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ Call <- 'Set' {p.startCall("Set")} open col comma args (comma timestamp)? close
|
|||
/ 'SetColumnAttrs' {p.startCall("SetColumnAttrs")} open col comma args close {p.endCall()}
|
||||
/ 'Clear' {p.startCall("Clear")} open col comma args close {p.endCall()}
|
||||
/ 'ClearRow' {p.startCall("ClearRow")} open arg close {p.endCall()}
|
||||
/ 'Store' {p.startCall("Store")} open Call comma arg close {p.endCall()}
|
||||
/ 'TopN' {p.startCall("TopN")} open posfield (comma allargs)? close {p.endCall()}
|
||||
/ 'Range' {p.startCall("Range")} open (timerange / conditional / arg) close {p.endCall()}
|
||||
/ < IDENT > { p.startCall(buffer[begin:end] ) } open allargs comma? close { p.endCall() }
|
||||
|
|
|
|||
2486
pql/pql.peg.go
2486
pql/pql.peg.go
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue