mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
292 lines
6.8 KiB
Go
292 lines
6.8 KiB
Go
// Copyright 2017 Pilosa Corp.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package pql_test
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/molecula/featurebase/v2/pql"
|
|
_ "github.com/molecula/featurebase/v2/test"
|
|
)
|
|
|
|
// Ensure the parser can parse PQL.
|
|
func TestParser_Parse(t *testing.T) {
|
|
// Parse with no children or arguments.
|
|
t.Run("Empty", func(t *testing.T) {
|
|
q, err := pql.ParseString(`Bitmap()`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "Bitmap",
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %s", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
// Parse with only children.
|
|
t.Run("ChildrenOnly", func(t *testing.T) {
|
|
q, err := pql.ParseString(`Union( Bitmap() , Count() )`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "Union",
|
|
Children: []*pql.Call{
|
|
{Name: "Bitmap"},
|
|
{Name: "Count"},
|
|
},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %s", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
// Parse a single child with a single argument.
|
|
t.Run("ChildWithArgument", func(t *testing.T) {
|
|
q, err := pql.ParseString(`Count( Bitmap( id=100))`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "Count",
|
|
Children: []*pql.Call{
|
|
{Name: "Bitmap", Args: map[string]interface{}{"id": int64(100)}},
|
|
},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %s", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
// Parse with only arguments.
|
|
t.Run("ArgumentsOnly", func(t *testing.T) {
|
|
q, err := pql.ParseString(`Row( key= value, foo='bar', age = 12 , bool0=true, bool1=false, x=null, escape="\" \\escape\n\\\\" )`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "Row",
|
|
Args: map[string]interface{}{
|
|
"key": "value",
|
|
"foo": "bar",
|
|
"age": int64(12),
|
|
"bool0": true,
|
|
"bool1": false,
|
|
"x": nil,
|
|
"escape": "\" \\escape\n\\\\",
|
|
},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %#v", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
// Parse with decimal arguments.
|
|
t.Run("WithDecimalArgs", func(t *testing.T) {
|
|
q, err := pql.ParseString(`Row( key=12.25, foo= 13.167, bar=2., baz=0.9)`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "Row",
|
|
Args: map[string]interface{}{
|
|
"key": pql.Decimal{1225, 2},
|
|
"foo": pql.Decimal{13167, 3},
|
|
"bar": pql.Decimal{2, 0},
|
|
"baz": pql.Decimal{9, 1},
|
|
},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %#v", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
// Parse with float arguments.
|
|
t.Run("WithNegativeArgs", func(t *testing.T) {
|
|
q, err := pql.ParseString(`Row( key=-12.25, foo= -13)`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "Row",
|
|
Args: map[string]interface{}{
|
|
"key": pql.Decimal{-1225, 2},
|
|
"foo": int64(-13),
|
|
},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %#v", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
// Parse with both child calls and arguments.
|
|
t.Run("ChildrenAndArguments", func(t *testing.T) {
|
|
q, err := pql.ParseString(`TopN(f, Bitmap(id=100, field=other), n=3)`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "TopN",
|
|
Children: []*pql.Call{{
|
|
Name: "Bitmap",
|
|
Args: map[string]interface{}{"id": int64(100), "field": "other"},
|
|
}},
|
|
Args: map[string]interface{}{"n": int64(3), "_field": "f"},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %#v", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
// Parse a list argument.
|
|
t.Run("ListArgument", func(t *testing.T) {
|
|
q, err := pql.ParseString(`TopN(f, ids=[0,10,30])`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "TopN",
|
|
Args: map[string]interface{}{
|
|
"_field": "f",
|
|
"ids": []interface{}{int64(0), int64(10), int64(30)},
|
|
},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %#v", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
// Parse with condition arguments.
|
|
t.Run("WithCondition", func(t *testing.T) {
|
|
q, err := pql.ParseString(`Row(key=foo, x == 12.25, y >= 100, z >< [4,8], m != null, n == null)`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "Row",
|
|
Args: map[string]interface{}{
|
|
"key": "foo",
|
|
"x": &pql.Condition{Op: pql.EQ, Value: pql.Decimal{1225, 2}},
|
|
"y": &pql.Condition{Op: pql.GTE, Value: int64(100)},
|
|
"z": &pql.Condition{Op: pql.BETWEEN, Value: []interface{}{int64(4), int64(8)}},
|
|
"m": &pql.Condition{Op: pql.NEQ, Value: nil},
|
|
"n": &pql.Condition{Op: pql.EQ, Value: nil},
|
|
},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %#v", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
t.Run("MixedCase", func(t *testing.T) {
|
|
q, err := pql.ParseString(`roW(x=3)`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "Row",
|
|
Args: map[string]interface{}{
|
|
"x": int64(3),
|
|
},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %#v", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
t.Run("VariousSpaces", func(t *testing.T) {
|
|
q, err := pql.ParseString(`TopN( x )`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if !reflect.DeepEqual(q.Calls[0],
|
|
&pql.Call{
|
|
Name: "TopN",
|
|
Args: map[string]interface{}{"_field": "x"},
|
|
},
|
|
) {
|
|
t.Fatalf("unexpected call: %#v", q.Calls[0])
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
func TestUnquote(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
exp string
|
|
expErr string
|
|
}{
|
|
{
|
|
name: "simple double",
|
|
value: `"hello"`,
|
|
exp: "hello",
|
|
},
|
|
{
|
|
name: "simple single",
|
|
value: `'hello'`,
|
|
exp: "hello",
|
|
},
|
|
{
|
|
name: "double with esc",
|
|
value: `"he\"llo"`,
|
|
exp: "he\"llo",
|
|
},
|
|
{
|
|
name: "single with esc",
|
|
value: `'he\'llo'`,
|
|
exp: "he'llo",
|
|
},
|
|
{
|
|
name: "single with backslash and esc",
|
|
value: `'he\\\'llo'`,
|
|
exp: `he\'llo`,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
got, err := pql.Unquote(test.value)
|
|
if testErr(t, test.expErr, err) {
|
|
return
|
|
}
|
|
if got != test.exp {
|
|
t.Errorf("exp: '%s'\ngot: '%s'", test.exp, got)
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func testErr(t *testing.T, exp string, actual error) (done bool) {
|
|
t.Helper()
|
|
if exp == "" && actual == nil {
|
|
return false
|
|
}
|
|
if exp == "" && actual != nil {
|
|
t.Fatalf("unexpected error: %v", actual)
|
|
}
|
|
if exp != "" && actual == nil {
|
|
t.Fatalf("expected error like '%s'", exp)
|
|
}
|
|
if !strings.Contains(actual.Error(), exp) {
|
|
t.Fatalf("unmatched errs exp/got\n%s\n%v", exp, actual)
|
|
}
|
|
return true
|
|
}
|