mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-09 22:51:02 +00:00
Merge branch 'master' into options-call-docs
This commit is contained in:
commit
96893e2e9f
16 changed files with 463 additions and 107 deletions
22
api.go
22
api.go
|
|
@ -686,6 +686,12 @@ func (api *API) Import(_ context.Context, req *ImportRequest) error {
|
|||
timestamps[i] = &t
|
||||
}
|
||||
|
||||
// Import columnIDs into existence field.
|
||||
if err := importExistenceColumns(index, req.ColumnIDs); err != nil {
|
||||
api.server.logger.Printf("import existence error: index=%s, field=%s, shard=%d, columns=%d, err=%s", req.Index, req.Field, req.Shard, len(req.ColumnIDs), err)
|
||||
return errors.Wrap(err, "importing existence columns")
|
||||
}
|
||||
|
||||
// Import into fragment.
|
||||
err = field.Import(req.RowIDs, req.ColumnIDs, timestamps)
|
||||
if err != nil {
|
||||
|
|
@ -720,6 +726,12 @@ func (api *API) ImportValue(_ context.Context, req *ImportValueRequest) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Import columnIDs into existence field.
|
||||
if err := importExistenceColumns(index, req.ColumnIDs); err != nil {
|
||||
api.server.logger.Printf("import existence error: index=%s, field=%s, shard=%d, columns=%d, err=%s", req.Index, req.Field, req.Shard, len(req.ColumnIDs), err)
|
||||
return errors.Wrap(err, "importing existence columns")
|
||||
}
|
||||
|
||||
// Import into fragment.
|
||||
err = field.importValue(req.ColumnIDs, req.Values)
|
||||
if err != nil {
|
||||
|
|
@ -728,6 +740,16 @@ func (api *API) ImportValue(_ context.Context, req *ImportValueRequest) error {
|
|||
return errors.Wrap(err, "importing")
|
||||
}
|
||||
|
||||
func importExistenceColumns(index *Index, columnIDs []uint64) error {
|
||||
ef := index.existenceField()
|
||||
if ef == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
existenceRowIDs := make([]uint64, len(columnIDs))
|
||||
return ef.Import(existenceRowIDs, columnIDs, nil)
|
||||
}
|
||||
|
||||
// MaxShards returns the maximum shard number for each index in a map.
|
||||
func (api *API) MaxShards(_ context.Context) map[string]uint64 {
|
||||
m := make(map[string]uint64)
|
||||
|
|
|
|||
|
|
@ -509,7 +509,8 @@ func encodeCreateIndexMessage(m *pilosa.CreateIndexMessage) *internal.CreateInde
|
|||
|
||||
func encodeIndexMeta(m *pilosa.IndexOptions) *internal.IndexMeta {
|
||||
return &internal.IndexMeta{
|
||||
Keys: m.Keys,
|
||||
Keys: m.Keys,
|
||||
TrackExistence: m.TrackExistence,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -741,6 +742,7 @@ func decodeCreateIndexMessage(pb *internal.CreateIndexMessage, m *pilosa.CreateI
|
|||
|
||||
func decodeIndexMeta(pb *internal.IndexMeta, m *pilosa.IndexOptions) {
|
||||
m.Keys = pb.Keys
|
||||
m.TrackExistence = pb.TrackExistence
|
||||
}
|
||||
|
||||
func decodeDeleteIndexMessage(pb *internal.DeleteIndexMessage, m *pilosa.DeleteIndexMessage) {
|
||||
|
|
|
|||
|
|
@ -1131,6 +1131,13 @@ func (e *executor) executeSet(ctx context.Context, index string, c *pql.Call, op
|
|||
return false, ErrFieldNotFound
|
||||
}
|
||||
|
||||
// Set column on existence field.
|
||||
if ef := idx.existenceField(); ef != nil {
|
||||
if _, err := ef.SetBit(0, colID, nil); err != nil {
|
||||
return false, errors.Wrap(err, "setting existence column")
|
||||
}
|
||||
}
|
||||
|
||||
if f.Type() == FieldTypeInt {
|
||||
// Read remaining fields using labels.
|
||||
rowVal, ok, err := c.IntArg(fieldName)
|
||||
|
|
|
|||
107
executor_test.go
107
executor_test.go
|
|
@ -17,6 +17,7 @@ package pilosa_test
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -32,8 +33,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Ensure a bitmap query can be executed.
|
||||
func TestExecutor_Execute_Bitmap(t *testing.T) {
|
||||
// Ensure a row query can be executed.
|
||||
func TestExecutor_Execute_Row(t *testing.T) {
|
||||
t.Run("Row", func(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
defer c.Close()
|
||||
|
|
@ -282,7 +283,7 @@ func TestExecutor_Execute_SetBit(t *testing.T) {
|
|||
t.Run("OK", func(t *testing.T) {
|
||||
hldr.ClearBit("i", "f", 11, 1)
|
||||
if n := hldr.Row("i", "f", 11).Count(); n != 0 {
|
||||
t.Fatalf("unexpected bitmap count: %d", n)
|
||||
t.Fatalf("unexpected row count: %d", n)
|
||||
}
|
||||
|
||||
if res, err := cmd.API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Set(1, f=11)`}); err != nil {
|
||||
|
|
@ -294,7 +295,7 @@ func TestExecutor_Execute_SetBit(t *testing.T) {
|
|||
}
|
||||
|
||||
if n := hldr.Row("i", "f", 11).Count(); n != 1 {
|
||||
t.Fatalf("unexpected bitmap count: %d", n)
|
||||
t.Fatalf("unexpected row count: %d", n)
|
||||
}
|
||||
if res, err := cmd.API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Set(1, f=11)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -327,7 +328,7 @@ func TestExecutor_Execute_SetBit(t *testing.T) {
|
|||
t.Run("OK", func(t *testing.T) {
|
||||
hldr.SetBit("i", "f", 1, 0)
|
||||
if n := hldr.Row("i", "f", 11).Count(); n != 0 {
|
||||
t.Fatalf("unexpected bitmap count: %d", n)
|
||||
t.Fatalf("unexpected row count: %d", n)
|
||||
}
|
||||
|
||||
if res, err := cmd.API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Set("foo", f=11)`}); err != nil {
|
||||
|
|
@ -339,7 +340,7 @@ func TestExecutor_Execute_SetBit(t *testing.T) {
|
|||
}
|
||||
|
||||
if n := hldr.Row("i", "f", 11).Count(); n != 1 {
|
||||
t.Fatalf("unexpected bitmap count: %d", n)
|
||||
t.Fatalf("unexpected row count: %d", n)
|
||||
}
|
||||
if res, err := cmd.API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Set("foo", f=11)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -476,7 +477,7 @@ func TestExecutor_Execute_SetRowAttrs(t *testing.T) {
|
|||
}
|
||||
t.Run("rowID", func(t *testing.T) {
|
||||
// Set two attrs on f/10.
|
||||
// Also set attrs on other bitmaps and fields to test isolation.
|
||||
// Also set attrs on other rows and fields to test isolation.
|
||||
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `SetRowAttrs(f, 10, foo="bar")`}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -494,13 +495,13 @@ func TestExecutor_Execute_SetRowAttrs(t *testing.T) {
|
|||
if m, err := f.RowAttrStore().Attrs(10); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !reflect.DeepEqual(m, map[string]interface{}{"foo": "bar", "baz": int64(123), "bat": true}) {
|
||||
t.Fatalf("unexpected bitmap attr: %#v", m)
|
||||
t.Fatalf("unexpected row attr: %#v", m)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("rowKey", func(t *testing.T) {
|
||||
// Set two attrs on f/10.
|
||||
// Also set attrs on other bitmaps and fields to test isolation.
|
||||
// Also set attrs on other rows and fields to test isolation.
|
||||
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `SetRowAttrs(kf, "row10", foo="bar")`}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -663,7 +664,7 @@ func TestExecutor_Execute_TopN_fill_small(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure a TopN() query with a source bitmap can be executed.
|
||||
// Ensure a TopN() query with a source row can be executed.
|
||||
func TestExecutor_Execute_TopN_Src(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
defer c.Close()
|
||||
|
|
@ -723,7 +724,7 @@ func TestExecutor_Execute_TopN_Attr(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
//Ensure TopN handles Attribute filters with source bitmap
|
||||
//Ensure TopN handles Attribute filters with source row
|
||||
func TestExecutor_Execute_TopN_Attr_Src(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
defer c.Close()
|
||||
|
|
@ -1495,3 +1496,87 @@ func TestExecutor_QueryCall(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure an existence field is maintained.
|
||||
func TestExecutor_Execute_Existence(t *testing.T) {
|
||||
t.Run("Row", 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+2, 20),
|
||||
}); 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}) {
|
||||
t.Fatalf("unexpected columns: %+v", bits)
|
||||
}
|
||||
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(exists=0)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{3, ShardWidth + 1, ShardWidth + 2}) {
|
||||
t.Fatalf("unexpected existence columns: %+v", bits)
|
||||
}
|
||||
|
||||
// Reopen cluster to ensure existence field is reloaded.
|
||||
if err := c[0].Reopen(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(exists=0)`}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if bits := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(bits, []uint64{3, ShardWidth + 1, ShardWidth + 2}) {
|
||||
t.Fatalf("unexpected existence columns after reopen: %+v", bits)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func benchmarkExistence(nn bool, b *testing.B) {
|
||||
c := test.MustRunCluster(b, 1)
|
||||
defer c.Close()
|
||||
hldr := test.Holder{Holder: c[0].Server.Holder()}
|
||||
|
||||
indexName := "i"
|
||||
fieldName := "f"
|
||||
|
||||
index := hldr.MustCreateIndexIfNotExists(indexName, pilosa.IndexOptions{TrackExistence: nn})
|
||||
// Create field.
|
||||
if _, err := index.CreateFieldIfNotExists(fieldName); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
bitCount := 10000
|
||||
req := &pilosa.ImportRequest{
|
||||
Index: indexName,
|
||||
Field: fieldName,
|
||||
Shard: 0,
|
||||
RowIDs: make([]uint64, bitCount),
|
||||
ColumnIDs: make([]uint64, bitCount),
|
||||
}
|
||||
for i := 0; i < bitCount; i++ {
|
||||
req.RowIDs[i] = uint64(rand.Intn(100000))
|
||||
req.ColumnIDs[i] = uint64(rand.Intn(1 << 20))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := c[0].API.Import(context.Background(), req); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExecutor_Existence_True(b *testing.B) { benchmarkExistence(true, b) }
|
||||
func BenchmarkExecutor_Existence_False(b *testing.B) { benchmarkExistence(false, b) }
|
||||
|
|
|
|||
9
field.go
9
field.go
|
|
@ -159,12 +159,17 @@ func OptFieldTypeMutex(cacheType string, cacheSize uint32) FieldOption {
|
|||
func NewField(path, index, name string, opts FieldOption) (*Field, error) {
|
||||
err := validateName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "validating name")
|
||||
}
|
||||
|
||||
return newField(path, index, name, opts)
|
||||
}
|
||||
|
||||
// newField returns a new instance of field (without name validation).
|
||||
func newField(path, index, name string, opts FieldOption) (*Field, error) {
|
||||
// Apply functional option.
|
||||
fo := FieldOptions{}
|
||||
err = opts(&fo)
|
||||
err := opts(&fo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "applying option")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -746,7 +746,7 @@ func TestFragment_TopN_CacheSize(t *testing.T) {
|
|||
cacheSize := uint32(3)
|
||||
|
||||
// Create Index.
|
||||
index := mustOpenIndex()
|
||||
index := mustOpenIndex(IndexOptions{})
|
||||
defer index.Close()
|
||||
|
||||
// Create field.
|
||||
|
|
@ -912,7 +912,7 @@ func TestFragment_LRUCache_Persistence(t *testing.T) {
|
|||
|
||||
// Ensure a fragment's cache can be persisted between restarts.
|
||||
func TestFragment_RankCache_Persistence(t *testing.T) {
|
||||
index := mustOpenIndex()
|
||||
index := mustOpenIndex(IndexOptions{})
|
||||
defer index.Close()
|
||||
|
||||
// Create field.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ const (
|
|||
|
||||
// fileLimit is the maximum open file limit (ulimit -n) to automatically set.
|
||||
fileLimit = 262144 // (512^2)
|
||||
|
||||
// existenceFieldName is the name of the internal field used to store existence values.
|
||||
existenceFieldName = "exists"
|
||||
)
|
||||
|
||||
// Holder represents a container for indexes.
|
||||
|
|
@ -354,6 +357,7 @@ func (h *Holder) createIndex(name string, opt IndexOptions) (*Index, error) {
|
|||
}
|
||||
|
||||
index.keys = opt.Keys
|
||||
index.trackExistence = opt.TrackExistence
|
||||
|
||||
if err := index.Open(); err != nil {
|
||||
return nil, errors.Wrap(err, "opening")
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ func TestClient_Export(t *testing.T) {
|
|||
{RowID: 2, ColumnID: 203, RowKey: "row2", ColumnKey: "col203"},
|
||||
}
|
||||
|
||||
t.Run("Import unkeyed,unkeyedf", func(t *testing.T) {
|
||||
t.Run("Export unkeyed,unkeyedf", func(t *testing.T) {
|
||||
// Populate data.
|
||||
for _, bit := range data {
|
||||
_, err := c.Query(context.Background(), "unkeyed", &pilosa.QueryRequest{
|
||||
|
|
@ -230,7 +230,7 @@ func TestClient_Export(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("Import unkeyed,keyedf", func(t *testing.T) {
|
||||
t.Run("Export unkeyed,keyedf", func(t *testing.T) {
|
||||
// Populate data.
|
||||
for _, bit := range data {
|
||||
_, err := c.Query(context.Background(), "unkeyed", &pilosa.QueryRequest{
|
||||
|
|
@ -264,7 +264,7 @@ func TestClient_Export(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("Import keyed,unkeyedf", func(t *testing.T) {
|
||||
t.Run("Export keyed,unkeyedf", func(t *testing.T) {
|
||||
// Populate data.
|
||||
for _, bit := range data {
|
||||
_, err := c.Query(context.Background(), "keyed", &pilosa.QueryRequest{
|
||||
|
|
@ -298,7 +298,7 @@ func TestClient_Export(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("Import keyed,keyedf", func(t *testing.T) {
|
||||
t.Run("Export keyed,keyedf", func(t *testing.T) {
|
||||
// Populate data.
|
||||
for _, bit := range data {
|
||||
_, err := c.Query(context.Background(), "keyed", &pilosa.QueryRequest{
|
||||
|
|
@ -655,6 +655,83 @@ func TestClient_ImportValue(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure client can bulk import data while tracking existence.
|
||||
func TestClient_ImportExistence(t *testing.T) {
|
||||
cmd := test.MustRunCluster(t, 1)[0]
|
||||
host := cmd.URL()
|
||||
holder := cmd.Server.Holder()
|
||||
hldr := test.Holder{Holder: holder}
|
||||
|
||||
t.Run("Set", func(t *testing.T) {
|
||||
idxName := "iset"
|
||||
fldName := "fset"
|
||||
|
||||
index := hldr.MustCreateIndexIfNotExists(idxName, pilosa.IndexOptions{TrackExistence: true})
|
||||
_, err := index.CreateFieldIfNotExists(fldName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Send import request.
|
||||
c := MustNewClient(host, http.GetHTTPClient(nil))
|
||||
if err := c.Import(context.Background(), idxName, fldName, 0, []pilosa.Bit{
|
||||
{RowID: 0, ColumnID: 1},
|
||||
{RowID: 0, ColumnID: 5},
|
||||
{RowID: 200, ColumnID: 6},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Verify data.
|
||||
if a := hldr.Row(idxName, fldName, 0).Columns(); !reflect.DeepEqual(a, []uint64{1, 5}) {
|
||||
t.Fatalf("unexpected columns: %+v", a)
|
||||
}
|
||||
if a := hldr.Row(idxName, fldName, 200).Columns(); !reflect.DeepEqual(a, []uint64{6}) {
|
||||
t.Fatalf("unexpected columns: %+v", a)
|
||||
}
|
||||
|
||||
// Verify existence.
|
||||
if a := hldr.ReadRow(idxName, "exists", 0).Columns(); !reflect.DeepEqual(a, []uint64{1, 5, 6}) {
|
||||
t.Fatalf("unexpected existence columns: %+v", a)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Int", func(t *testing.T) {
|
||||
idxName := "iint"
|
||||
fldName := "fint"
|
||||
|
||||
index := hldr.MustCreateIndexIfNotExists(idxName, pilosa.IndexOptions{TrackExistence: true})
|
||||
field, err := index.CreateFieldIfNotExists(fldName, pilosa.OptFieldTypeInt(-100, 100))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Send import request.
|
||||
c := MustNewClient(host, http.GetHTTPClient(nil))
|
||||
if err := c.ImportValue(context.Background(), idxName, fldName, 0, []pilosa.FieldValue{
|
||||
{ColumnID: 1, Value: -10},
|
||||
{ColumnID: 2, Value: 20},
|
||||
{ColumnID: 3, Value: 40},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Verify Sum.
|
||||
sum, cnt, err := field.Sum(nil, fldName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sum != 50 || cnt != 3 {
|
||||
t.Fatalf("unexpected values: got sum=%v, count=%v; expected sum=50, cnt=3", sum, cnt)
|
||||
}
|
||||
|
||||
// Verify existence.
|
||||
if a := hldr.ReadRow(idxName, "exists", 0).Columns(); !reflect.DeepEqual(a, []uint64{1, 2, 3}) {
|
||||
t.Fatalf("unexpected existence columns: %+v", a)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure client can retrieve a list of all checksums for blocks in a fragment.
|
||||
func TestClient_FragmentBlocks(t *testing.T) {
|
||||
cmd := test.MustRunCluster(t, 1)[0]
|
||||
|
|
|
|||
64
index.go
64
index.go
|
|
@ -36,6 +36,10 @@ type Index struct {
|
|||
name string
|
||||
keys bool // use string keys
|
||||
|
||||
// Existence tracking.
|
||||
trackExistence bool
|
||||
existenceFld *Field
|
||||
|
||||
// Fields by name.
|
||||
fields map[string]*Field
|
||||
|
||||
|
|
@ -91,7 +95,10 @@ func (i *Index) Options() IndexOptions {
|
|||
}
|
||||
|
||||
func (i *Index) options() IndexOptions {
|
||||
return IndexOptions{Keys: i.keys}
|
||||
return IndexOptions{
|
||||
Keys: i.keys,
|
||||
TrackExistence: i.trackExistence,
|
||||
}
|
||||
}
|
||||
|
||||
// Open opens and initializes the index.
|
||||
|
|
@ -110,6 +117,12 @@ func (i *Index) Open() error {
|
|||
return errors.Wrap(err, "opening fields")
|
||||
}
|
||||
|
||||
if i.trackExistence {
|
||||
if err := i.openExistenceField(); err != nil {
|
||||
return errors.Wrap(err, "opening existence field")
|
||||
}
|
||||
}
|
||||
|
||||
if err := i.columnAttrs.Open(); err != nil {
|
||||
return errors.Wrap(err, "opening attrstore")
|
||||
}
|
||||
|
|
@ -147,6 +160,16 @@ func (i *Index) openFields() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// openExistenceField gets or creates the existence field and associates it to the index.
|
||||
func (i *Index) openExistenceField() error {
|
||||
f, err := i.createFieldIfNotExists(existenceFieldName, FieldOptions{CacheType: CacheTypeNone, CacheSize: 0})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating existence field")
|
||||
}
|
||||
i.existenceFld = f
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadMeta reads meta data for the index, if any.
|
||||
func (i *Index) loadMeta() error {
|
||||
var pb internal.IndexMeta
|
||||
|
|
@ -165,6 +188,7 @@ func (i *Index) loadMeta() error {
|
|||
|
||||
// Copy metadata fields.
|
||||
i.keys = pb.Keys
|
||||
i.trackExistence = pb.TrackExistence
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -173,7 +197,8 @@ func (i *Index) loadMeta() error {
|
|||
func (i *Index) saveMeta() error {
|
||||
// Marshal metadata.
|
||||
buf, err := proto.Marshal(&internal.IndexMeta{
|
||||
Keys: i.keys,
|
||||
Keys: i.keys,
|
||||
TrackExistence: i.trackExistence,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshalling")
|
||||
|
|
@ -250,6 +275,14 @@ func (i *Index) Fields() []*Field {
|
|||
return a
|
||||
}
|
||||
|
||||
// existenceField returns the internal field used to track column existence.
|
||||
func (i *Index) existenceField() *Field {
|
||||
i.mu.RLock()
|
||||
defer i.mu.RUnlock()
|
||||
|
||||
return i.existenceFld
|
||||
}
|
||||
|
||||
// recalculateCaches recalculates caches on every field in the index.
|
||||
func (i *Index) recalculateCaches() {
|
||||
for _, field := range i.Fields() {
|
||||
|
|
@ -259,6 +292,11 @@ func (i *Index) recalculateCaches() {
|
|||
|
||||
// CreateField creates a field.
|
||||
func (i *Index) CreateField(name string, opts ...FieldOption) (*Field, error) {
|
||||
err := validateName(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "validating name")
|
||||
}
|
||||
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
|
|
@ -281,6 +319,11 @@ func (i *Index) CreateField(name string, opts ...FieldOption) (*Field, error) {
|
|||
|
||||
// CreateFieldIfNotExists creates a field with the given options if it doesn't exist.
|
||||
func (i *Index) CreateFieldIfNotExists(name string, opts ...FieldOption) (*Field, error) {
|
||||
err := validateName(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "validating name")
|
||||
}
|
||||
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
|
||||
|
|
@ -349,7 +392,7 @@ func (i *Index) createField(name string, opt FieldOptions) (*Field, error) {
|
|||
}
|
||||
|
||||
func (i *Index) newField(path, name string) (*Field, error) {
|
||||
f, err := NewField(path, i.name, name, OptFieldTypeDefault())
|
||||
f, err := newField(path, i.name, name, OptFieldTypeDefault())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -381,6 +424,18 @@ func (i *Index) DeleteField(name string) error {
|
|||
return errors.Wrap(err, "removing directory")
|
||||
}
|
||||
|
||||
// If the field being deleted is the existence field,
|
||||
// turn off existence tracking on the index.
|
||||
if name == existenceFieldName {
|
||||
i.trackExistence = false
|
||||
i.existenceFld = nil
|
||||
|
||||
// Update meta data on disk.
|
||||
if err := i.saveMeta(); err != nil {
|
||||
return errors.Wrap(err, "saving existence meta data")
|
||||
}
|
||||
}
|
||||
|
||||
// Remove reference.
|
||||
delete(i.fields, name)
|
||||
|
||||
|
|
@ -408,7 +463,8 @@ func (p indexInfoSlice) Less(i, j int) bool { return p[i].Name < p[j].Name }
|
|||
|
||||
// IndexOptions represents options to set when initializing an index.
|
||||
type IndexOptions struct {
|
||||
Keys bool `json:"keys"`
|
||||
Keys bool `json:"keys"`
|
||||
TrackExistence bool `json:"trackExistence"`
|
||||
}
|
||||
|
||||
// hasTime returns true if a contains a non-nil time.
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ package pilosa
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mustOpenIndex returns a new, opened index at a temporary path. Panic on error.
|
||||
func mustOpenIndex() *Index {
|
||||
func mustOpenIndex(opt IndexOptions) *Index {
|
||||
path, err := ioutil.TempDir("", "pilosa-index-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -28,6 +29,10 @@ func mustOpenIndex() *Index {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
index.keys = opt.Keys
|
||||
index.trackExistence = opt.TrackExistence
|
||||
|
||||
if err := index.Open(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -44,3 +49,40 @@ func (i *Index) reopen() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure that deleting the existence field is handled properly.
|
||||
func TestIndex_Existence_Delete(t *testing.T) {
|
||||
// Create Index (with existence tracking).
|
||||
index := mustOpenIndex(IndexOptions{TrackExistence: true})
|
||||
defer index.Close()
|
||||
|
||||
// Ensure existence field has been created.
|
||||
ef := index.Field(existenceFieldName)
|
||||
if ef == nil {
|
||||
t.Fatalf("expected field to have been created: %s", existenceFieldName)
|
||||
} else if !index.trackExistence {
|
||||
t.Fatalf("expected index.trackExistence to be true")
|
||||
} else if index.existenceFld == nil {
|
||||
t.Fatalf("expected index.existenceField to be non-nil")
|
||||
}
|
||||
|
||||
// Delete existence field.
|
||||
if err := index.DeleteField(existenceFieldName); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Re-open index.
|
||||
if err := index.reopen(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Ensure existence field no longer exists.
|
||||
ef = index.Field(existenceFieldName)
|
||||
if ef != nil {
|
||||
t.Fatalf("expected field to have been deleted: %s", existenceFieldName)
|
||||
} else if index.trackExistence {
|
||||
t.Fatalf("expected index.trackExistence to be false")
|
||||
} else if index.existenceFld != nil {
|
||||
t.Fatalf("expected index.existenceField to be nil")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ var _ = math.Inf
|
|||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type IndexMeta struct {
|
||||
Keys bool `protobuf:"varint,3,opt,name=Keys,proto3" json:"Keys,omitempty"`
|
||||
Keys bool `protobuf:"varint,3,opt,name=Keys,proto3" json:"Keys,omitempty"`
|
||||
TrackExistence bool `protobuf:"varint,4,opt,name=TrackExistence,proto3" json:"TrackExistence,omitempty"`
|
||||
}
|
||||
|
||||
func (m *IndexMeta) Reset() { *m = IndexMeta{} }
|
||||
|
|
@ -77,6 +78,13 @@ func (m *IndexMeta) GetKeys() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (m *IndexMeta) GetTrackExistence() bool {
|
||||
if m != nil {
|
||||
return m.TrackExistence
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type FieldOptions struct {
|
||||
Type string `protobuf:"bytes,8,opt,name=Type,proto3" json:"Type,omitempty"`
|
||||
CacheType string `protobuf:"bytes,3,opt,name=CacheType,proto3" json:"CacheType,omitempty"`
|
||||
|
|
@ -1051,6 +1059,16 @@ func (m *IndexMeta) MarshalTo(dAtA []byte) (int, error) {
|
|||
}
|
||||
i++
|
||||
}
|
||||
if m.TrackExistence {
|
||||
dAtA[i] = 0x20
|
||||
i++
|
||||
if m.TrackExistence {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
|
@ -2300,6 +2318,9 @@ func (m *IndexMeta) Size() (n int) {
|
|||
if m.Keys {
|
||||
n += 2
|
||||
}
|
||||
if m.TrackExistence {
|
||||
n += 2
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
|
|
@ -2902,6 +2923,26 @@ func (m *IndexMeta) Unmarshal(dAtA []byte) error {
|
|||
}
|
||||
}
|
||||
m.Keys = bool(v != 0)
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field TrackExistence", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowPrivate
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.TrackExistence = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipPrivate(dAtA[iNdEx:])
|
||||
|
|
@ -7143,73 +7184,74 @@ var (
|
|||
func init() { proto.RegisterFile("private.proto", fileDescriptorPrivate) }
|
||||
|
||||
var fileDescriptorPrivate = []byte{
|
||||
// 1077 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x6e, 0x1b, 0x45,
|
||||
0x14, 0x66, 0x7f, 0xec, 0xda, 0xc7, 0x75, 0x9a, 0x6c, 0x69, 0xd8, 0x22, 0x94, 0x9a, 0x51, 0xa5,
|
||||
0x9a, 0x4a, 0x84, 0xaa, 0xbd, 0xe1, 0xaf, 0x52, 0x49, 0x1c, 0x60, 0x29, 0x09, 0x65, 0x36, 0xc9,
|
||||
0x5d, 0x2f, 0x26, 0xf6, 0xa8, 0x59, 0x65, 0xbd, 0xb3, 0xec, 0xce, 0x26, 0x71, 0x2f, 0xb8, 0x05,
|
||||
0x89, 0x17, 0x40, 0x3c, 0x09, 0x8f, 0xc0, 0x25, 0x8f, 0x80, 0xc2, 0x8b, 0xa0, 0x39, 0x33, 0xfb,
|
||||
0x13, 0xc7, 0x21, 0x55, 0xe0, 0x6e, 0xce, 0x77, 0xce, 0x9c, 0xf3, 0xed, 0xf9, 0x9b, 0x85, 0x7e,
|
||||
0x9a, 0x45, 0xc7, 0x4c, 0xf2, 0xf5, 0x34, 0x13, 0x52, 0x78, 0x9d, 0x28, 0x91, 0x3c, 0x4b, 0x58,
|
||||
0x4c, 0xee, 0x41, 0x37, 0x48, 0x26, 0xfc, 0x74, 0x9b, 0x4b, 0xe6, 0x79, 0xe0, 0x3e, 0xe7, 0xb3,
|
||||
0xdc, 0x77, 0x06, 0xd6, 0xb0, 0x43, 0xf1, 0x4c, 0x7e, 0xb7, 0xe0, 0xe6, 0x97, 0x11, 0x8f, 0x27,
|
||||
0xdf, 0xa5, 0x32, 0x12, 0x49, 0xee, 0xbd, 0x07, 0xdd, 0x4d, 0x36, 0x3e, 0xe4, 0xbb, 0xb3, 0x94,
|
||||
0xa3, 0x65, 0x97, 0xd6, 0x40, 0xa5, 0x0d, 0xa3, 0xd7, 0xdc, 0x77, 0x07, 0xd6, 0xb0, 0x4f, 0x6b,
|
||||
0xc0, 0x1b, 0x40, 0x6f, 0x37, 0x9a, 0xf2, 0xef, 0x0b, 0x96, 0xc8, 0x62, 0xea, 0xb7, 0xf0, 0x76,
|
||||
0x13, 0x52, 0x14, 0xd0, 0x71, 0x07, 0x55, 0x78, 0xf6, 0x96, 0xc1, 0xd9, 0x8e, 0x12, 0xbf, 0x3b,
|
||||
0xb0, 0x86, 0x0e, 0x55, 0x47, 0x44, 0xd8, 0xa9, 0x0f, 0x06, 0x61, 0xa7, 0x15, 0xf5, 0x5e, 0x83,
|
||||
0x3a, 0x81, 0xa5, 0x60, 0x9a, 0x8a, 0x4c, 0x52, 0x9e, 0xa7, 0x22, 0xc9, 0xd1, 0xd3, 0x56, 0x96,
|
||||
0xf9, 0x16, 0x3a, 0x57, 0x47, 0xf2, 0x23, 0x2c, 0x6f, 0xc4, 0x62, 0x7c, 0x34, 0x62, 0x92, 0x51,
|
||||
0xfe, 0x43, 0xc1, 0x73, 0xe9, 0xbd, 0x0d, 0x2d, 0xcc, 0x89, 0xb1, 0xd3, 0x82, 0x42, 0x31, 0x0f,
|
||||
0xbe, 0xad, 0x51, 0x14, 0x14, 0x8a, 0xf7, 0x31, 0x13, 0x2e, 0xd5, 0x82, 0x42, 0xc3, 0x43, 0x96,
|
||||
0x4d, 0x30, 0x03, 0x2e, 0xd5, 0x82, 0xe2, 0xb8, 0x1f, 0xf1, 0x13, 0xf3, 0xd9, 0x78, 0x26, 0x01,
|
||||
0xac, 0x34, 0xe2, 0x1b, 0x9a, 0xab, 0xd0, 0xa6, 0xe2, 0x24, 0x18, 0xe5, 0xbe, 0x35, 0x70, 0x86,
|
||||
0x2e, 0x35, 0x12, 0x26, 0x57, 0xc4, 0xc5, 0x34, 0x51, 0x2a, 0x1b, 0x55, 0x35, 0x40, 0xee, 0x42,
|
||||
0x0b, 0x33, 0xad, 0xbe, 0xb2, 0xbe, 0xab, 0x8e, 0xe4, 0x27, 0x0b, 0xba, 0xdb, 0xec, 0x14, 0x69,
|
||||
0xe4, 0xde, 0x53, 0xe8, 0x84, 0x92, 0x25, 0x13, 0x45, 0x50, 0x19, 0xf5, 0x1e, 0xbf, 0xbf, 0x5e,
|
||||
0x36, 0xc4, 0x7a, 0x65, 0xb6, 0x5e, 0xda, 0x6c, 0x25, 0x32, 0x9b, 0xd1, 0xea, 0xca, 0xbb, 0x9f,
|
||||
0x41, 0xff, 0x9c, 0x4a, 0xc5, 0x3b, 0xe2, 0xb3, 0x32, 0xab, 0x47, 0x7c, 0xa6, 0xbe, 0xff, 0x98,
|
||||
0xc5, 0x05, 0xc7, 0x5c, 0xb9, 0x54, 0x0b, 0x9f, 0xda, 0x1f, 0x5b, 0x64, 0x1f, 0xbc, 0xcd, 0x8c,
|
||||
0x33, 0xc9, 0x31, 0xc8, 0x36, 0xcf, 0x73, 0xf6, 0x8a, 0x5f, 0x9e, 0x71, 0x9d, 0x45, 0xbb, 0x99,
|
||||
0xc5, 0xaa, 0x0e, 0x4e, 0xa3, 0x0e, 0xe4, 0x21, 0x78, 0x23, 0x1e, 0x73, 0xc9, 0x4d, 0x37, 0xff,
|
||||
0x8b, 0x5f, 0x12, 0x96, 0x1c, 0xae, 0xb6, 0xf5, 0x1e, 0x80, 0xab, 0x46, 0x03, 0x29, 0xf4, 0x1e,
|
||||
0xdf, 0xae, 0xf3, 0x54, 0x4d, 0x0d, 0x45, 0x03, 0x12, 0x97, 0x4e, 0x91, 0xcf, 0x95, 0x1f, 0xb6,
|
||||
0xa0, 0x95, 0x1e, 0x9a, 0x50, 0x0e, 0x86, 0x5a, 0xad, 0x43, 0x35, 0xc7, 0xcf, 0x44, 0x7b, 0x56,
|
||||
0x7e, 0xee, 0x75, 0xa3, 0x91, 0x97, 0x06, 0x55, 0x5d, 0xb9, 0xc3, 0xa6, 0xdc, 0xdc, 0xc1, 0x73,
|
||||
0x45, 0xc5, 0xbe, 0x9a, 0x8a, 0x72, 0xaf, 0x3a, 0x59, 0x6d, 0x0d, 0x47, 0xb9, 0x47, 0x81, 0x3c,
|
||||
0x81, 0x76, 0x38, 0x3e, 0xe4, 0x53, 0xe6, 0x7d, 0x00, 0x37, 0x90, 0x07, 0xcf, 0x4d, 0xb3, 0xdd,
|
||||
0x9a, 0x4b, 0x22, 0x2d, 0xf5, 0x64, 0x64, 0xf8, 0x2f, 0xe4, 0xf4, 0x00, 0xda, 0x18, 0x3d, 0xf7,
|
||||
0xdd, 0x79, 0x37, 0x88, 0x53, 0xa3, 0x26, 0x5b, 0xe0, 0xec, 0xd1, 0x40, 0x0d, 0x11, 0x32, 0x28,
|
||||
0xbd, 0x18, 0x49, 0xf9, 0xfe, 0x5a, 0xe4, 0xd2, 0x64, 0x03, 0xcf, 0x0a, 0x7b, 0x21, 0x32, 0x89,
|
||||
0xa9, 0xef, 0x53, 0x3c, 0x93, 0x97, 0xe0, 0xee, 0x88, 0x09, 0xf7, 0x96, 0xc0, 0x0e, 0x46, 0xc6,
|
||||
0x87, 0x1d, 0x8c, 0xbc, 0x7b, 0xe8, 0xde, 0xa4, 0xa6, 0x5f, 0x93, 0xd8, 0xa3, 0x01, 0xc5, 0xc0,
|
||||
0xf7, 0xa1, 0x1f, 0xe4, 0x9b, 0x42, 0x64, 0x93, 0x28, 0x61, 0x52, 0x64, 0x66, 0x9d, 0x9e, 0x07,
|
||||
0xc9, 0x33, 0x58, 0x56, 0xee, 0x43, 0xc9, 0x24, 0x2f, 0xeb, 0xb7, 0x0a, 0x6d, 0x85, 0x55, 0xe1,
|
||||
0x8c, 0x84, 0x83, 0xa0, 0xec, 0xca, 0x0a, 0xa2, 0x40, 0xbe, 0xd5, 0x1e, 0xb6, 0x8e, 0x79, 0x22,
|
||||
0x1b, 0x1d, 0x80, 0x32, 0x3a, 0xe8, 0x53, 0x2d, 0x78, 0x44, 0x7f, 0x8a, 0xe1, 0xbc, 0x54, 0x73,
|
||||
0x56, 0x28, 0x45, 0x1d, 0xf9, 0xc5, 0x02, 0x28, 0x09, 0x15, 0x79, 0x75, 0xc5, 0xba, 0xfc, 0x8a,
|
||||
0x37, 0x2c, 0x6b, 0x6c, 0x5a, 0x76, 0xb9, 0xb6, 0xd2, 0x38, 0x2d, 0x7b, 0xe0, 0xa3, 0xba, 0x07,
|
||||
0x74, 0xf1, 0xee, 0xcc, 0xf5, 0x80, 0x8e, 0x5a, 0x77, 0xc2, 0x0b, 0xe8, 0x35, 0xf0, 0x85, 0xfd,
|
||||
0xf0, 0x61, 0xd5, 0x0f, 0xf6, 0xbc, 0x4b, 0xc4, 0x8d, 0xcb, 0xb2, 0x2b, 0x9e, 0x43, 0xaf, 0x01,
|
||||
0x2f, 0xf4, 0x38, 0x84, 0x5b, 0x5f, 0x1c, 0xb3, 0x28, 0x66, 0x07, 0xb1, 0x5e, 0x4f, 0xe5, 0x92,
|
||||
0x9d, 0x87, 0x49, 0x04, 0xfd, 0xcd, 0xb8, 0xc8, 0x25, 0xcf, 0x8c, 0x3b, 0xb5, 0x99, 0x35, 0x50,
|
||||
0x15, 0xaf, 0x06, 0x16, 0xd7, 0xcf, 0xbb, 0x0f, 0x2d, 0x95, 0x46, 0x3d, 0x38, 0x17, 0x73, 0xac,
|
||||
0x95, 0x64, 0x1f, 0x3a, 0x1b, 0x61, 0xf0, 0x55, 0x26, 0x8a, 0x74, 0x21, 0xe9, 0xf2, 0xc1, 0xb4,
|
||||
0x2f, 0x3e, 0x98, 0xce, 0x85, 0x07, 0xd3, 0xad, 0x1e, 0x4c, 0x12, 0xc2, 0x8a, 0xde, 0x57, 0x6a,
|
||||
0x5e, 0xaf, 0xb3, 0xae, 0xca, 0xd7, 0xcc, 0x69, 0xbc, 0x66, 0x21, 0xac, 0xe8, 0xb5, 0xf4, 0x7f,
|
||||
0x3a, 0xfd, 0xcd, 0x86, 0x15, 0xca, 0xf3, 0xe8, 0x35, 0x0f, 0x92, 0x5c, 0x66, 0xc5, 0x58, 0x6d,
|
||||
0x1f, 0x75, 0xff, 0x1b, 0x71, 0x60, 0xb2, 0xed, 0x50, 0x2d, 0xbc, 0x49, 0xa7, 0x7b, 0x8f, 0xa0,
|
||||
0x37, 0x3f, 0x9d, 0x17, 0x4d, 0x9b, 0x26, 0xde, 0x23, 0xb8, 0x11, 0x8a, 0x22, 0x1b, 0x57, 0xed,
|
||||
0xdb, 0xd8, 0x88, 0x9a, 0x99, 0x56, 0xd3, 0xd2, 0xac, 0x31, 0x1a, 0xad, 0x2b, 0x46, 0xe3, 0xe9,
|
||||
0x5c, 0x2b, 0xf9, 0x6d, 0xbc, 0xf0, 0x4e, 0x7d, 0xe1, 0x9c, 0x9a, 0x9e, 0xb7, 0x26, 0x3f, 0x5b,
|
||||
0x70, 0xb3, 0x49, 0xe1, 0x8d, 0x06, 0xb7, 0xaa, 0x88, 0xbd, 0xb0, 0x22, 0xce, 0xa2, 0x8a, 0xb8,
|
||||
0x75, 0x45, 0xea, 0x87, 0xb9, 0xd5, 0x78, 0x98, 0xc9, 0x11, 0xdc, 0xbd, 0x50, 0xa6, 0x4d, 0x31,
|
||||
0x4d, 0x55, 0x3f, 0xfc, 0x87, 0x72, 0xa9, 0x95, 0x96, 0x65, 0xa6, 0x50, 0x5d, 0xaa, 0x05, 0xf2,
|
||||
0x09, 0xdc, 0x09, 0xb9, 0x6c, 0x14, 0xa9, 0xec, 0xb6, 0x01, 0x38, 0x3b, 0xfc, 0xe4, 0x92, 0xcf,
|
||||
0x57, 0x2a, 0xf2, 0x39, 0xf8, 0x7b, 0xe9, 0x84, 0x49, 0x7e, 0xad, 0xdb, 0x1b, 0xd0, 0xd9, 0x15,
|
||||
0xa9, 0x88, 0xc5, 0xab, 0xd9, 0x15, 0x53, 0xef, 0xc3, 0x0d, 0xbd, 0xbf, 0xf5, 0x1a, 0xe9, 0xd2,
|
||||
0x52, 0x24, 0xb7, 0x55, 0x43, 0x8f, 0x59, 0x3c, 0x2e, 0x62, 0x45, 0x43, 0xfd, 0xb4, 0xe5, 0x1b,
|
||||
0xcb, 0x7f, 0x9c, 0xad, 0x59, 0x7f, 0x9e, 0xad, 0x59, 0x7f, 0x9d, 0xad, 0x59, 0xbf, 0xfe, 0xbd,
|
||||
0xf6, 0xd6, 0x41, 0x1b, 0x7f, 0xd6, 0x9f, 0xfc, 0x13, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x7c, 0x6d,
|
||||
0x22, 0xbd, 0x0b, 0x00, 0x00,
|
||||
// 1095 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x6e, 0xdc, 0x44,
|
||||
0x14, 0xc6, 0x3f, 0xbb, 0xd9, 0x3d, 0xdb, 0x4d, 0x13, 0x97, 0x06, 0x17, 0xa1, 0x10, 0x46, 0x15,
|
||||
0x0d, 0x95, 0x08, 0x55, 0x7b, 0xc3, 0x5f, 0xa5, 0x92, 0x6c, 0x28, 0xa6, 0x24, 0x94, 0x71, 0x92,
|
||||
0xbb, 0x5e, 0x4c, 0x76, 0x47, 0x8d, 0x15, 0xaf, 0xc7, 0xd8, 0xe3, 0x24, 0xdb, 0x0b, 0x6e, 0x41,
|
||||
0xe2, 0x05, 0x10, 0x4f, 0xc2, 0x23, 0x70, 0xc9, 0x23, 0xa0, 0xf0, 0x22, 0x68, 0xce, 0x8c, 0x7f,
|
||||
0xb2, 0xd9, 0xb0, 0x55, 0xe8, 0xdd, 0x9c, 0xef, 0x9c, 0x39, 0xe7, 0x9b, 0xf3, 0x67, 0x43, 0x3f,
|
||||
0xcd, 0xa2, 0x13, 0x26, 0xf9, 0x46, 0x9a, 0x09, 0x29, 0xbc, 0x4e, 0x94, 0x48, 0x9e, 0x25, 0x2c,
|
||||
0x26, 0x4f, 0xa1, 0x1b, 0x24, 0x23, 0x7e, 0xb6, 0xc3, 0x25, 0xf3, 0x3c, 0x70, 0x9f, 0xf1, 0x49,
|
||||
0xee, 0x3b, 0x6b, 0xd6, 0x7a, 0x87, 0xe2, 0xd9, 0xfb, 0x10, 0x16, 0xf7, 0x32, 0x36, 0x3c, 0xde,
|
||||
0x3e, 0x8b, 0x72, 0xc9, 0x93, 0x21, 0xf7, 0x5d, 0xd4, 0x4e, 0xa1, 0xe4, 0x0f, 0x0b, 0x6e, 0x7c,
|
||||
0x1d, 0xf1, 0x78, 0xf4, 0x7d, 0x2a, 0x23, 0x91, 0xe4, 0xde, 0x7b, 0xd0, 0xdd, 0x62, 0xc3, 0x23,
|
||||
0xbe, 0x37, 0x49, 0x39, 0x7a, 0xec, 0xd2, 0x1a, 0xa8, 0xb4, 0x61, 0xf4, 0x4a, 0x7b, 0xec, 0xd3,
|
||||
0x1a, 0xf0, 0xd6, 0xa0, 0xb7, 0x17, 0x8d, 0xf9, 0x0f, 0x05, 0x4b, 0x64, 0x31, 0xf6, 0x5b, 0x78,
|
||||
0xbb, 0x09, 0x29, 0xaa, 0xe8, 0xb8, 0x83, 0x2a, 0x3c, 0x7b, 0x4b, 0xe0, 0xec, 0x44, 0x89, 0xdf,
|
||||
0x5d, 0xb3, 0xd6, 0x1d, 0xaa, 0x8e, 0x88, 0xb0, 0x33, 0x1f, 0x0c, 0xc2, 0xce, 0xaa, 0x27, 0xf6,
|
||||
0xea, 0x27, 0x12, 0x02, 0x8b, 0xc1, 0x38, 0x15, 0x99, 0xa4, 0x3c, 0x4f, 0x45, 0x92, 0xa3, 0xa7,
|
||||
0xed, 0x2c, 0xf3, 0x2d, 0x74, 0xae, 0x8e, 0xe4, 0x27, 0x58, 0xda, 0x8c, 0xc5, 0xf0, 0x78, 0xc0,
|
||||
0x24, 0xa3, 0xfc, 0xc7, 0x82, 0xe7, 0xd2, 0x7b, 0x1b, 0x5a, 0x98, 0x3b, 0x63, 0xa7, 0x05, 0x85,
|
||||
0x62, 0x1e, 0x7c, 0x5b, 0xa3, 0x28, 0x28, 0x14, 0xef, 0x63, 0x26, 0x5c, 0xaa, 0x05, 0x85, 0x86,
|
||||
0x47, 0x2c, 0x1b, 0x61, 0x06, 0x5c, 0xaa, 0x05, 0xc5, 0xf1, 0x20, 0xe2, 0xa7, 0xe6, 0xd9, 0x78,
|
||||
0x26, 0x01, 0x2c, 0x37, 0xe2, 0x1b, 0x9a, 0x2b, 0xd0, 0xa6, 0xe2, 0x34, 0x18, 0xe4, 0xbe, 0xb5,
|
||||
0xe6, 0xac, 0xbb, 0xd4, 0x48, 0x98, 0x5c, 0x11, 0x17, 0xe3, 0x44, 0xa9, 0x6c, 0x54, 0xd5, 0x00,
|
||||
0xb9, 0x03, 0x2d, 0xcc, 0xb4, 0x7a, 0x65, 0x7d, 0x57, 0x1d, 0xc9, 0xcf, 0x16, 0x74, 0x77, 0xd8,
|
||||
0x19, 0xd2, 0xc8, 0xbd, 0xc7, 0xd0, 0x09, 0x25, 0x4b, 0x46, 0x8a, 0xa0, 0x32, 0xea, 0x3d, 0xfc,
|
||||
0x60, 0xa3, 0x6c, 0x9c, 0x8d, 0xca, 0x6c, 0xa3, 0xb4, 0xd9, 0x4e, 0x64, 0x36, 0xa1, 0xd5, 0x95,
|
||||
0x77, 0xbf, 0x80, 0xfe, 0x05, 0x95, 0x8a, 0x77, 0xcc, 0x27, 0x65, 0x56, 0x8f, 0xf9, 0x44, 0xbd,
|
||||
0xff, 0x84, 0xc5, 0x05, 0xc7, 0x5c, 0xb9, 0x54, 0x0b, 0x9f, 0xdb, 0x9f, 0x5a, 0xe4, 0x00, 0xbc,
|
||||
0xad, 0x8c, 0x33, 0xc9, 0x31, 0xc8, 0x0e, 0xcf, 0x73, 0xf6, 0x92, 0x5f, 0x9d, 0x71, 0x9d, 0x45,
|
||||
0xbb, 0x99, 0xc5, 0xaa, 0x0e, 0x4e, 0xa3, 0x0e, 0xe4, 0x3e, 0x78, 0x03, 0x1e, 0x73, 0xc9, 0x4d,
|
||||
0xd7, 0xff, 0x87, 0x5f, 0x12, 0x96, 0x1c, 0xe6, 0xdb, 0x7a, 0xf7, 0xc0, 0x55, 0x23, 0x84, 0x14,
|
||||
0x7a, 0x0f, 0x6f, 0xd5, 0x79, 0xaa, 0xa6, 0x8b, 0xa2, 0x01, 0x89, 0x4b, 0xa7, 0xc8, 0x67, 0xee,
|
||||
0xc3, 0x66, 0xb4, 0xd2, 0x7d, 0x13, 0xca, 0xc1, 0x50, 0x2b, 0x75, 0xa8, 0xe6, 0xf8, 0x99, 0x68,
|
||||
0x4f, 0xca, 0xe7, 0x5e, 0x37, 0x1a, 0x79, 0x61, 0x50, 0xd5, 0x95, 0xbb, 0x6c, 0xcc, 0xcd, 0x1d,
|
||||
0x3c, 0x57, 0x54, 0xec, 0xf9, 0x54, 0x94, 0x7b, 0xd5, 0xc9, 0x6a, 0xbb, 0x38, 0xca, 0x3d, 0x0a,
|
||||
0xe4, 0x11, 0xb4, 0xc3, 0xe1, 0x11, 0x1f, 0x33, 0xef, 0x23, 0x58, 0x40, 0x1e, 0x3c, 0x37, 0xcd,
|
||||
0x76, 0x73, 0x2a, 0x89, 0xb4, 0xd4, 0x93, 0x81, 0xe1, 0x3f, 0x93, 0xd3, 0x3d, 0x68, 0x63, 0xf4,
|
||||
0xdc, 0x77, 0xa7, 0xdd, 0x20, 0x4e, 0x8d, 0x9a, 0x6c, 0x83, 0xb3, 0x4f, 0x03, 0x35, 0x44, 0xc8,
|
||||
0xa0, 0xf4, 0x62, 0x24, 0xe5, 0xfb, 0x1b, 0x91, 0x4b, 0x93, 0x0d, 0x3c, 0x2b, 0xec, 0xb9, 0xc8,
|
||||
0x24, 0xa6, 0xbe, 0x4f, 0xf1, 0x4c, 0x5e, 0x80, 0xbb, 0x2b, 0x46, 0xdc, 0x5b, 0x04, 0x3b, 0x18,
|
||||
0x18, 0x1f, 0x76, 0x30, 0xf0, 0xde, 0x47, 0xf7, 0x26, 0x35, 0xfd, 0x9a, 0xc4, 0x3e, 0x0d, 0x28,
|
||||
0x06, 0xbe, 0x0b, 0xfd, 0x20, 0xdf, 0x12, 0x22, 0x1b, 0x45, 0x09, 0x93, 0x22, 0x33, 0x6b, 0xf7,
|
||||
0x22, 0x48, 0x9e, 0xc0, 0x92, 0x72, 0x1f, 0x4a, 0x26, 0x79, 0x59, 0xbf, 0x15, 0x68, 0x2b, 0xac,
|
||||
0x0a, 0x67, 0x24, 0x1c, 0x04, 0x65, 0x57, 0x56, 0x10, 0x05, 0xf2, 0x9d, 0xf6, 0xb0, 0x7d, 0xc2,
|
||||
0x13, 0xd9, 0xe8, 0x00, 0x94, 0xd1, 0x41, 0x9f, 0x6a, 0xc1, 0x23, 0xfa, 0x29, 0x86, 0xf3, 0x62,
|
||||
0xcd, 0x59, 0xa1, 0x14, 0x75, 0xe4, 0x57, 0x0b, 0xa0, 0x24, 0x54, 0xe4, 0xd5, 0x15, 0xeb, 0xea,
|
||||
0x2b, 0xde, 0x7a, 0x59, 0x63, 0xd3, 0xb2, 0x4b, 0xb5, 0x95, 0xc6, 0x69, 0xd9, 0x03, 0x9f, 0xd4,
|
||||
0x3d, 0xa0, 0x8b, 0x77, 0x7b, 0xaa, 0x07, 0x74, 0xd4, 0xba, 0x13, 0x9e, 0x43, 0xaf, 0x81, 0xcf,
|
||||
0xec, 0x87, 0x8f, 0xab, 0x7e, 0xb0, 0xa7, 0x5d, 0x22, 0x6e, 0x5c, 0x96, 0x5d, 0xf1, 0x0c, 0x7a,
|
||||
0x0d, 0x78, 0xa6, 0xc7, 0x75, 0xb8, 0xf9, 0xd5, 0x09, 0x8b, 0x62, 0x76, 0x18, 0xeb, 0xf5, 0x54,
|
||||
0x2e, 0xd9, 0x69, 0x98, 0x44, 0xd0, 0xdf, 0x8a, 0x8b, 0x5c, 0xf2, 0xcc, 0xb8, 0x53, 0x9b, 0x59,
|
||||
0x03, 0x55, 0xf1, 0x6a, 0x60, 0x76, 0xfd, 0xbc, 0xbb, 0xd0, 0x52, 0x69, 0xd4, 0x83, 0x73, 0x39,
|
||||
0xc7, 0x5a, 0x49, 0x0e, 0xa0, 0xb3, 0x19, 0x06, 0x4f, 0x33, 0x51, 0xa4, 0x33, 0x49, 0x97, 0x1f,
|
||||
0x4c, 0xfb, 0xf2, 0x07, 0xd3, 0xb9, 0xf4, 0xc1, 0x74, 0xab, 0x0f, 0x26, 0x09, 0x61, 0x59, 0xef,
|
||||
0x2b, 0x35, 0xaf, 0xd7, 0x59, 0x57, 0xe5, 0xd7, 0xcc, 0x69, 0x7c, 0xcd, 0x42, 0x58, 0xd6, 0x6b,
|
||||
0xe9, 0x4d, 0x3a, 0xfd, 0xdd, 0x86, 0x65, 0xca, 0xf3, 0xe8, 0x15, 0x0f, 0x92, 0x5c, 0x66, 0xc5,
|
||||
0x50, 0x6d, 0x1f, 0x75, 0xff, 0x5b, 0x71, 0x68, 0xb2, 0xed, 0x50, 0x2d, 0xbc, 0x4e, 0xa7, 0x7b,
|
||||
0x0f, 0xa0, 0x37, 0x3d, 0x9d, 0x97, 0x4d, 0x9b, 0x26, 0xde, 0x03, 0x58, 0x08, 0x45, 0x91, 0x0d,
|
||||
0xab, 0xf6, 0x6d, 0x6c, 0x44, 0xcd, 0x4c, 0xab, 0x69, 0x69, 0xd6, 0x18, 0x8d, 0xd6, 0x9c, 0xd1,
|
||||
0x78, 0x3c, 0xd5, 0x4a, 0x7e, 0x1b, 0x2f, 0xbc, 0x53, 0x5f, 0xb8, 0xa0, 0xa6, 0x17, 0xad, 0xc9,
|
||||
0x2f, 0x16, 0xdc, 0x68, 0x52, 0x78, 0xad, 0xc1, 0xad, 0x2a, 0x62, 0xcf, 0xac, 0x88, 0x33, 0xab,
|
||||
0x22, 0x6e, 0x5d, 0x91, 0xfa, 0xc3, 0xdc, 0x6a, 0x7c, 0x98, 0xc9, 0x31, 0xdc, 0xb9, 0x54, 0xa6,
|
||||
0x2d, 0x31, 0x4e, 0x55, 0x3f, 0xfc, 0x8f, 0x72, 0xa9, 0x95, 0x96, 0x65, 0xa6, 0x50, 0x5d, 0xaa,
|
||||
0x05, 0xf2, 0x19, 0xdc, 0x0e, 0xb9, 0x6c, 0x14, 0xa9, 0xec, 0xb6, 0x35, 0x70, 0x76, 0xf9, 0xe9,
|
||||
0x15, 0xcf, 0x57, 0x2a, 0xf2, 0x25, 0xf8, 0xfb, 0xe9, 0x88, 0x49, 0x7e, 0xad, 0xdb, 0x9b, 0xd0,
|
||||
0xd9, 0x13, 0xa9, 0x88, 0xc5, 0xcb, 0xc9, 0x9c, 0xa9, 0xf7, 0x61, 0x41, 0xef, 0x6f, 0xbd, 0x46,
|
||||
0xba, 0xb4, 0x14, 0xc9, 0x2d, 0xd5, 0xd0, 0x43, 0x16, 0x0f, 0x8b, 0x58, 0xd1, 0x50, 0x3f, 0x6d,
|
||||
0xf9, 0xe6, 0xd2, 0x9f, 0xe7, 0xab, 0xd6, 0x5f, 0xe7, 0xab, 0xd6, 0xdf, 0xe7, 0xab, 0xd6, 0x6f,
|
||||
0xff, 0xac, 0xbe, 0x75, 0xd8, 0xc6, 0x9f, 0xfa, 0x47, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x31,
|
||||
0x07, 0xf3, 0xac, 0xe5, 0x0b, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package internal;
|
|||
|
||||
message IndexMeta {
|
||||
bool Keys = 3;
|
||||
bool TrackExistence = 4;
|
||||
}
|
||||
|
||||
message FieldOptions {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ var (
|
|||
ErrName = errors.New("invalid index or field name, must match [a-z0-9_-]")
|
||||
ErrLabel = errors.New("invalid row or column label, must match [A-Za-z0-9_-]")
|
||||
|
||||
ErrReservedName = errors.New("reserved index or field name")
|
||||
|
||||
// ErrFragmentNotFound is returned when a fragment does not exist.
|
||||
ErrFragmentNotFound = errors.New("fragment not found")
|
||||
ErrQueryRequired = errors.New("query required")
|
||||
|
|
@ -125,6 +127,9 @@ const TimeFormat = "2006-01-02T15:04"
|
|||
|
||||
// validateName ensures that the name is a valid format.
|
||||
func validateName(name string) error {
|
||||
if name == existenceFieldName {
|
||||
return ErrReservedName
|
||||
}
|
||||
if !nameRegexp.Match([]byte(name)) {
|
||||
return ErrName
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ func TestValidateNameInvalid(t *testing.T) {
|
|||
names := []string{
|
||||
"", "'", "^", "/", "\\", "A", "*", "a:b", "valid?no", "yüce", "1", "_", "-",
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1",
|
||||
"exists",
|
||||
}
|
||||
for _, name := range names {
|
||||
if validateName(name) == nil {
|
||||
|
|
|
|||
|
|
@ -91,6 +91,20 @@ func (h *Holder) Row(index, field string, rowID uint64) *pilosa.Row {
|
|||
return row
|
||||
}
|
||||
|
||||
// ReadRow returns a Row for a given field. If the field does not exist,
|
||||
// it panics rather than creating the field.
|
||||
func (h *Holder) ReadRow(index, field string, rowID uint64) *pilosa.Row {
|
||||
f := h.Holder.Field(index, field)
|
||||
if f == nil {
|
||||
panic(pilosa.ErrFieldNotFound)
|
||||
}
|
||||
row, err := f.Row(rowID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func (h *Holder) RowAttrStore(index, field string) pilosa.AttrStore {
|
||||
idx := h.MustCreateIndexIfNotExists(index, pilosa.IndexOptions{})
|
||||
f, err := idx.CreateFieldIfNotExists(field, pilosa.OptFieldTypeDefault())
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
gohttp "net/http"
|
||||
"os"
|
||||
|
|
@ -40,10 +39,6 @@ type Command struct {
|
|||
*server.Command
|
||||
|
||||
commandOptions []server.CommandOption
|
||||
|
||||
stdin bytes.Buffer
|
||||
stdout bytes.Buffer
|
||||
stderr bytes.Buffer
|
||||
}
|
||||
|
||||
func OptAllowedOrigins(origins []string) server.CommandOption {
|
||||
|
|
@ -65,17 +60,15 @@ func newCommand(opts ...server.CommandOption) *Command {
|
|||
// beginning of the option slice so that it can be overridden by user-passed
|
||||
// options.
|
||||
opts = append([]server.CommandOption{server.OptCommandCloseTimeout(time.Millisecond * 2)}, opts...)
|
||||
m := &Command{Command: server.NewCommand(os.Stdin, os.Stdout, os.Stderr, opts...), commandOptions: opts}
|
||||
m := &Command{commandOptions: opts}
|
||||
m.Command = server.NewCommand(bytes.NewReader(nil), ioutil.Discard, ioutil.Discard, opts...)
|
||||
m.Config.DataDir = path
|
||||
m.Config.Bind = "http://localhost:0"
|
||||
m.Config.Cluster.Disabled = true
|
||||
m.Command.Stdin = &m.stdin
|
||||
m.Command.Stdout = &m.stdout
|
||||
m.Command.Stderr = &m.stderr
|
||||
|
||||
if testing.Verbose() {
|
||||
m.Command.Stdout = io.MultiWriter(os.Stdout, m.Command.Stdout)
|
||||
m.Command.Stderr = io.MultiWriter(os.Stderr, m.Command.Stderr)
|
||||
m.Command.Stdout = os.Stdout
|
||||
m.Command.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
return m
|
||||
|
|
@ -120,7 +113,7 @@ func (m *Command) Reopen() error {
|
|||
|
||||
// Create new main with the same config.
|
||||
config := m.Command.Config
|
||||
m.Command = server.NewCommand(os.Stdin, os.Stdout, os.Stderr, m.commandOptions...)
|
||||
m.Command = server.NewCommand(bytes.NewReader(nil), ioutil.Discard, ioutil.Discard, m.commandOptions...)
|
||||
m.Command.Config = config
|
||||
|
||||
// Run new program.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue