diff --git a/document/array.go b/document/array.go index a0d13fc4a..08c3f675c 100644 --- a/document/array.go +++ b/document/array.go @@ -1,8 +1,6 @@ package document import ( - "sort" - "github.com/buger/jsonparser" "github.com/genjidb/genji/internal/errors" "github.com/genjidb/genji/types" @@ -241,84 +239,3 @@ func (vb *ValueBuffer) UnmarshalJSON(data []byte) error { return nil } - -func (vb *ValueBuffer) Types() []types.ValueType { - types := make([]types.ValueType, len(vb.Values)) - - for i, v := range vb.Values { - types[i] = v.Type() - } - - return types -} - -// IsEqual compares two ValueBuffer and returns true if and only if -// both each values and types are respectively equal. -func (vb *ValueBuffer) IsEqual(other *ValueBuffer) bool { - if vb.Len() != other.Len() { - return false - } - - // empty buffers are always equal eh - if vb.Len() == 0 && other.Len() == 0 { - return true - } - - otherTypes := other.Types() - tps := vb.Types() - - for i, typ := range tps { - if typ != otherTypes[i] { - return false - } - } - - for i, v := range vb.Values { - if eq, err := types.IsEqual(v, other.Values[i]); err != nil || !eq { - return false - } - } - - return true -} - -func (vb *ValueBuffer) Swap(i, j int) { - vb.Values[i], vb.Values[j] = vb.Values[j], vb.Values[i] -} - -func (vb *ValueBuffer) Less(i, j int) (ok bool) { - it, jt := vb.Values[i].Type(), vb.Values[j].Type() - if it == jt || (it.IsNumber() && jt.IsNumber()) { - // TODO(asdine) make the types package work with static documents - // to avoid having to deal with errors? - ok, _ = types.IsLesserThan(vb.Values[i], vb.Values[j]) - return - } - - return it < jt -} - -// SortArray creates a new sorted array. -// Types are sorted in the following ascending order: -// - NULL -// - Booleans -// - Numbers -// - Text -// - Blob -// - Arrays -// - Documents -// It doesn't sort nested arrays. -func SortArray(a types.Array) (*ValueBuffer, error) { - vb, ok := a.(*ValueBuffer) - if !ok { - vb := NewValueBuffer() - err := vb.Copy(a) - if err != nil { - return nil, err - } - } - - sort.Sort(vb) - - return vb, nil -} diff --git a/document/array_test.go b/document/array_test.go index 1917fd231..710ffb551 100644 --- a/document/array_test.go +++ b/document/array_test.go @@ -30,33 +30,6 @@ func TestArrayContains(t *testing.T) { require.False(t, ok) } -func TestSortArray(t *testing.T) { - tests := []struct { - name string - arr string - expected string - }{ - {"empty array", `[]`, `[]`}, - {"numbers", `[1.4,3,2.1,-5]`, `[-5,1.4,2.1,3]`}, - {"text", `["foo","bar",""]`, `["","bar","foo"]`}, - {"arrays", `[[1, 2],[-1,10],[]]`, `[[],[-1,10],[1,2]]`}, - {"documents", `[{"z":10},{"a":40},{}]`, `[{},{"a":40},{"z":10}]`}, - {"mixed", `["foo",["a"],{},null,true,10]`, `[null,true,10,"foo",["a"],{}]`}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var arr document.ValueBuffer - assert.NoError(t, arr.UnmarshalJSON([]byte(test.arr))) - output, err := document.SortArray(&arr) - assert.NoError(t, err) - actual, err := json.Marshal(output) - assert.NoError(t, err) - require.Equal(t, test.expected, string(actual)) - }) - } -} - func TestValueBufferCopy(t *testing.T) { tests := []struct { name string diff --git a/document/document.go b/document/document.go index c696f9cb5..3d59f593e 100644 --- a/document/document.go +++ b/document/document.go @@ -2,7 +2,6 @@ package document import ( - "sort" "strconv" "strings" @@ -57,16 +56,6 @@ func Length(d types.Document) (int, error) { return len, err } -// Fields returns a list of all the fields at the root of the document -// sorted lexicographically. -func Fields(d types.Document) ([]string, error) { - if fb, ok := d.(*FieldBuffer); ok { - return fb.Fields(), nil - } - - return types.Fields(d) -} - // FieldBuffer stores a group of fields in memory. It implements the Document interface. type FieldBuffer struct { fields []fieldValue @@ -342,14 +331,6 @@ func (fb *FieldBuffer) Copy(d types.Document) error { return nil } -// Clone the buffer. -func (fb *FieldBuffer) Clone() *FieldBuffer { - var newFb FieldBuffer - - _ = newFb.Copy(fb) - return &newFb -} - // Apply a function to all the values of the buffer. func (fb *FieldBuffer) Apply(fn func(p Path, v types.Value) (types.Value, error)) error { path := Path{PathFragment{}} @@ -415,18 +396,6 @@ func (fb *FieldBuffer) Reset() { fb.fields = fb.fields[:0] } -// Fields returns a sorted list of root field names. -func (fb *FieldBuffer) Fields() []string { - fields := make([]string, len(fb.fields)) - - for i := range fb.fields { - fields[i] = fb.fields[i].Field - } - - sort.Strings(fields) - return fields -} - // A Path represents the path to a particular value within a document. type Path []PathFragment diff --git a/document/document_test.go b/document/document_test.go index 3120954f8..348a187c9 100644 --- a/document/document_test.go +++ b/document/document_test.go @@ -81,11 +81,6 @@ func TestFieldBuffer(t *testing.T) { require.Zero(t, v) }) - t.Run("Fields", func(t *testing.T) { - require.Equal(t, []string{}, document.NewFieldBuffer().Fields()) - require.Equal(t, []string{"a", "b"}, buf.Fields()) - }) - t.Run("Set", func(t *testing.T) { tests := []struct { name string diff --git a/internal/database/table_test.go b/internal/database/table_test.go index de03973b8..f71591045 100644 --- a/internal/database/table_test.go +++ b/internal/database/table_test.go @@ -124,11 +124,11 @@ func TestTableInsert(t *testing.T) { defer cleanup() doc := newDocument() - key1, _, err := tb.Insert(doc.Clone()) + key1, _, err := tb.Insert(testutil.CloneDocument(t, doc)) assert.NoError(t, err) require.NotEmpty(t, key1) - key2, _, err := tb.Insert(doc.Clone()) + key2, _, err := tb.Insert(testutil.CloneDocument(t, doc)) assert.NoError(t, err) require.NotEmpty(t, key2) @@ -532,9 +532,9 @@ func TestTableDelete(t *testing.T) { doc1.Add("fieldc", types.NewIntegerValue(40)) doc2 := newDocument() - key1, _, err := tb.Insert(doc1.Clone()) + key1, _, err := tb.Insert(testutil.CloneDocument(t, doc1)) assert.NoError(t, err) - key2, _, err := tb.Insert(doc2.Clone()) + key2, _, err := tb.Insert(testutil.CloneDocument(t, doc2)) assert.NoError(t, err) // delete the document diff --git a/internal/environment/env.go b/internal/environment/env.go index 054663dc3..92763301a 100644 --- a/internal/environment/env.go +++ b/internal/environment/env.go @@ -1,8 +1,6 @@ package environment import ( - "strings" - "github.com/genjidb/genji/document" "github.com/genjidb/genji/internal/database" "github.com/genjidb/genji/internal/stringutil" @@ -12,7 +10,6 @@ import ( var ( TableKey = document.Path{document.PathFragment{FieldName: "$table"}} DocPKKey = document.Path{document.PathFragment{FieldName: "$pk"}} - // OriginalDocumentKey = document.Path{document.PathFragment{FieldName: "$originalDocument"}} ) // A Param represents a parameter passed by the user to the statement. @@ -162,99 +159,3 @@ func (e *Environment) GetCatalog() *database.Catalog { return nil } - -func (e *Environment) Clone() (*Environment, error) { - var newEnv Environment - - newEnv.Params = e.Params - newEnv.Tx = e.Tx - newEnv.Catalog = e.Catalog - - if e.Doc != nil { - fb := document.NewFieldBuffer() - err := fb.Copy(e.Doc) - if err != nil { - return nil, err - } - - newEnv.Doc = fb - } - - if e.Vars != nil { - fb := document.NewFieldBuffer() - err := fb.Copy(e.Vars) - if err != nil { - return nil, err - } - - newEnv.Vars = fb - } - - if e.Outer != nil { - newOuter, err := e.Outer.Clone() - if err != nil { - return nil, err - } - newEnv.Outer = newOuter - } - - return &newEnv, nil -} - -func (e *Environment) MarshalJSON() ([]byte, error) { - var sb strings.Builder - - var needComa bool - sb.WriteByte('{') - if e.Doc != nil { - sb.WriteString("\"Doc\":") - b, err := types.NewDocumentValue(e.Doc).MarshalJSON() - if err != nil { - return nil, err - } - sb.Write(b) - needComa = true - } - - if e.Vars != nil { - if needComa { - sb.WriteByte(',') - } - sb.WriteString("\"Vars\":") - b, err := types.NewDocumentValue(e.Vars).MarshalJSON() - if err != nil { - return nil, err - } - sb.Write(b) - needComa = true - } - - if e.Params != nil { - if needComa { - sb.WriteByte(',') - } - sb.WriteString("\"Params\":") - for i, p := range e.Params { - if i > 0 { - sb.WriteByte(',') - } - sb.WriteString(stringutil.Sprintf("{\"Name\":\"%s\",\"Value\":%v}", p.Name, p.Value)) - } - - needComa = true - } - - if e.Outer != nil { - if needComa { - sb.WriteByte(',') - } - sb.WriteString("\"Outer\":") - b, _ := e.Outer.MarshalJSON() - sb.Write(b) - needComa = true - } - - sb.WriteByte('}') - - return []byte(sb.String()), nil -} diff --git a/internal/environment/env_test.go b/internal/environment/env_test.go deleted file mode 100644 index 5ea3f23ca..000000000 --- a/internal/environment/env_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package environment_test - -import ( - "testing" - - "github.com/genjidb/genji/document" - "github.com/genjidb/genji/internal/database" - "github.com/genjidb/genji/internal/environment" - "github.com/genjidb/genji/internal/testutil/assert" - "github.com/genjidb/genji/types" - "github.com/stretchr/testify/require" -) - -func TestEnvironmentClone(t *testing.T) { - d := document.NewFieldBuffer() - d.Add("answer", types.NewIntegerValue(42)) - - p := environment.Param{Name: "param", Value: 1} - tx := &database.Transaction{} - catalog := database.NewCatalog() - - vars := document.NewFieldBuffer() - vars.Add("var", types.NewIntegerValue(2)) - - var env, outer environment.Environment - - env.Doc = d - env.Params = []environment.Param{p} - env.Catalog = catalog - env.Tx = tx - env.Catalog = catalog - env.Outer = &outer - env.Vars = vars - - newEnv, err := env.Clone() - assert.NoError(t, err) - - require.Equal(t, d, newEnv.Doc) - require.Equal(t, 1, len(newEnv.Params)) - require.Equal(t, p, newEnv.Params[0]) - require.Equal(t, tx, newEnv.Tx) - require.Equal(t, catalog, newEnv.Catalog) - require.Equal(t, vars, newEnv.Vars) - require.Equal(t, &outer, newEnv.Outer) -} diff --git a/internal/testutil/document.go b/internal/testutil/document.go index 500062d4c..210e3a7c9 100644 --- a/internal/testutil/document.go +++ b/internal/testutil/document.go @@ -152,3 +152,14 @@ func RequireDocEqual(t testing.TB, d1, d2 types.Document) { require.Failf(t, "mismatched documents, (-want, +got)", "%s", diff) } } + +func CloneDocument(t testing.TB, d types.Document) *document.FieldBuffer { + t.Helper() + + var newFb document.FieldBuffer + + err := newFb.Copy(d) + assert.NoError(t, err) + + return &newFb +} diff --git a/internal/testutil/stream.go b/internal/testutil/stream.go index 49c1ff742..3f35de966 100644 --- a/internal/testutil/stream.go +++ b/internal/testutil/stream.go @@ -79,8 +79,10 @@ func RequireStreamEqf(t *testing.T, raw string, res *genji.Result, sorted bool, assert.NoError(t, err) if sorted { - sort.Sort(want) - sort.Sort(got) + swant := sortableValueBuffer(*want) + sgot := sortableValueBuffer(*got) + sort.Sort(&swant) + sort.Sort(&sgot) } expected, err := types.MarshalTextIndent(types.NewArrayValue(want), "\n", " ") @@ -95,3 +97,25 @@ func RequireStreamEqf(t *testing.T, raw string, res *genji.Result, sorted bool, require.Equal(t, string(expected), string(actual)) } } + +type sortableValueBuffer document.ValueBuffer + +func (vb *sortableValueBuffer) Len() int { + return len(vb.Values) +} + +func (vb *sortableValueBuffer) Swap(i, j int) { + vb.Values[i], vb.Values[j] = vb.Values[j], vb.Values[i] +} + +func (vb *sortableValueBuffer) Less(i, j int) (ok bool) { + it, jt := vb.Values[i].Type(), vb.Values[j].Type() + if it == jt || (it.IsNumber() && jt.IsNumber()) { + // TODO(asdine) make the types package work with static documents + // to avoid having to deal with errors? + ok, _ = types.IsLesserThan(vb.Values[i], vb.Values[j]) + return + } + + return it < jt +}