A bug fix roundup (#2242)

* fb-1940 re-implemented some changes that got missed private-public

* fb-1939 fixes to between + decimals

* fb-1935 - avg() on and id type + fixed some tests

* fb-1953 add min/max for string types

* fb-1938 - remove internal_type column from show columns

* fb-1964 - fix space_used in fb_cluster_nodes to be int

* fb-1996 - make sure all Idents that are being used as object references to schema objects are lowercased

* fixed failing test

* added some missed changes

* fb-1969 found another case issue with identifier used for column idents
This commit is contained in:
Pat Okeeffe 2023-02-13 12:28:34 -06:00 committed by GitHub
parent e803a000b8
commit 5c74b64722
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1150 additions and 207 deletions

View file

@ -810,7 +810,7 @@ func (e *executor) executeCall(ctx context.Context, qcx *Qcx, index string, c *p
return res, errors.Wrap(err, "executeUnionRows")
case "ConstRow":
statFn(CounterQueryConstRowTotal)
res, err := e.executeConstRow(ctx, index, c)
res, err := e.executeConstRow(ctx, qcx, index, c, opt)
return res, errors.Wrap(err, "executeConstRow")
case "Limit":
statFn(CounterQueryLimitTotal)
@ -5356,14 +5356,96 @@ func (e *executor) executeNotShard(ctx context.Context, qcx *Qcx, index string,
return existenceRow.Difference(row), nil
}
func (e *executor) executeConstRow(ctx context.Context, index string, c *pql.Call) (res *Row, err error) {
func (e *executor) executeConstRow(ctx context.Context, qcx *Qcx, index string, c *pql.Call, opt *ExecOptions) (res *Row, err error) {
idx := e.Holder.Index(index)
if idx == nil {
return nil, newNotFoundError(ErrIndexNotFound, index)
} else if idx.existenceField() == nil {
ids, ok := c.Args["columns"].([]uint64)
if !ok {
return nil, errors.New("missing columns list")
}
return NewRow(ids...), nil
}
// Fetch user-provided columns list.
ids, ok := c.Args["columns"].([]uint64)
if !ok {
return nil, errors.New("missing columns list")
availableBitmap := roaring.NewBitmap()
var records []uint64
if opt.Remote {
ids, _ := c.Args["columns"].([]interface{})
if len(ids) == 0 {
return NewRow(), nil
}
for _, idi := range ids {
id := idi.(int64)
availableBitmap.Add(uint64(id) / ShardWidth)
records = append(records, uint64(id))
}
} else {
ids, ok := c.Args["columns"].([]uint64)
if !ok {
return nil, errors.New("missing columns list")
}
if len(ids) == 0 {
return NewRow(), nil
}
for _, id := range ids {
availableBitmap.Add(id / ShardWidth)
records = append(records, id)
}
}
return NewRow(ids...), nil
filteredShards := availableBitmap.Slice()
// Execute calls in bulk on each remote node and merge.
mapFn := func(ctx context.Context, shard uint64, mopt *mapOptions) (_ interface{}, err error) {
return e.executeConstRowShard(ctx, qcx, index, c, shard, NewRow(records...))
}
// Merge returned results at coordinating node.
reduceFn := func(ctx context.Context, prev, v interface{}) interface{} {
other, _ := prev.(*Row)
if other == nil {
other = NewRow()
}
if err := ctx.Err(); err != nil {
return err
}
other.Merge(v.(*Row))
return other
}
other, err := e.mapReduce(ctx, index, filteredShards, c, opt, mapFn, reduceFn)
if err != nil {
return nil, err
}
if err != nil {
return nil, errors.Wrap(err, "map reduce")
}
row, _ := other.(*Row)
return row, nil
}
func (e *executor) executeConstRowShard(ctx context.Context, qcx *Qcx, index string, c *pql.Call, shard uint64, src *Row) (res *Row, err error) {
idx := e.Holder.Index(index)
var existenceRow *Row
existenceFrag := e.Holder.fragment(index, existenceFieldName, viewStandard, shard)
if existenceFrag == nil {
existenceRow = NewRow()
} else {
tx, finisher, err := qcx.GetTx(Txo{Write: !writable, Index: idx, Fragment: existenceFrag, Shard: shard})
if err != nil {
return nil, err
}
defer finisher(&err)
if existenceRow, err = existenceFrag.row(tx, 0); err != nil {
return nil, err
}
}
return src.Intersect(existenceRow), nil
}
func (e *executor) executeUnionRows(ctx context.Context, qcx *Qcx, index string, c *pql.Call, shards []uint64, opt *ExecOptions) (*Row, error) {

View file

@ -38,9 +38,7 @@ import (
"github.com/stretchr/testify/assert"
)
var (
TempDir = getTempDirString()
)
var TempDir = getTempDirString()
func getTempDirString() (td *string) {
tdflag := flag.Lookup("temp-dir")
@ -1024,7 +1022,6 @@ func TestExecutor(t *testing.T) {
if columns := responses[4].Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(columns, []uint64{2}) {
t.Fatalf("unexpected columns: %+v", columns)
}
})
// Ensure that ClearRow returns false when the row to clear needs translation.
t.Run("WithKeys", func(t *testing.T) {
@ -1198,6 +1195,7 @@ func TestExecutor_Execute_ConstRow(t *testing.T) {
c := test.MustRunCluster(t, 3)
defer c.Close()
// without track existnce you just get back the columns you request
c.CreateField(t, c.Idx(), pilosa.IndexOptions{}, "h")
c.ImportBits(t, c.Idx(), "h", [][2]uint64{
{1, 2},
@ -1205,7 +1203,27 @@ func TestExecutor_Execute_ConstRow(t *testing.T) {
{5, 6},
})
resp := c.Query(t, c.Idx(), `ConstRow(columns=[2,6])`)
resp := c.Query(t, c.Idx(), `ConstRow(columns=[2,6,7])`)
expect := []uint64{2, 6, 7}
got := resp.Results[0].(*pilosa.Row).Columns()
if !reflect.DeepEqual(expect, got) {
t.Errorf("expected %v but got %v", expect, got)
}
}
func TestExecutor_Execute_ConstRowTrackExistence(t *testing.T) {
c := test.MustRunCluster(t, 3)
defer c.Close()
// with track existnce you
c.CreateField(t, c.Idx(), pilosa.IndexOptions{TrackExistence: true}, "h")
c.ImportBits(t, c.Idx(), "h", [][2]uint64{
{1, 2},
{3, 4},
{5, 6},
})
resp := c.Query(t, c.Idx(), `ConstRow(columns=[2,6,7])`)
expect := []uint64{2, 6}
got := resp.Results[0].(*pilosa.Row).Columns()
if !reflect.DeepEqual(expect, got) {
@ -1332,7 +1350,6 @@ func TestExecutor_Execute_Xor(t *testing.T) {
t.Fatalf("unexpected columns: %+v", columns)
}
})
}
// Ensure a count query can be executed.
@ -1476,7 +1493,6 @@ func TestExecutor_Execute_Set(t *testing.T) {
} else if !res.Results[0].(bool) {
t.Fatalf("expected column changed with integer column key")
}
})
})
}
@ -3049,7 +3065,8 @@ func TestExecutor_Execute_Row_BSIGroup(t *testing.T) {
// EQ null
if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: c.Idx(), Query: `Row(other == null)`}); err != nil {
t.Fatal(err)
} else if !reflect.DeepEqual([]uint64{1,
} else if !reflect.DeepEqual([]uint64{
1,
50,
ShardWidth,
ShardWidth + 1,
@ -3148,7 +3165,7 @@ func TestExecutor_Execute_Row_BSIGroup(t *testing.T) {
}
for i, test := range tests {
t.Run(fmt.Sprintf("#%d_%s", i, test.q), func(t *testing.T) {
var expected = []uint64{}
expected := []uint64{}
if test.exp {
expected = []uint64{0}
}
@ -3585,7 +3602,7 @@ func TestExecutor_Execute_Remote_Row(t *testing.T) {
})
t.Run("json format groupBy on timestamps", func(t *testing.T) {
//SUP-138
// SUP-138
c.CreateField(t, c.Idx("t"), pilosa.IndexOptions{TrackExistence: true}, "timestamp", pilosa.OptFieldTypeTimestamp(pilosa.DefaultEpoch, pilosa.TimeUnitSeconds))
c.Query(t, c.Idx("t"), `
Set(8, timestamp='2021-01-27T08:00:00Z')
@ -3816,7 +3833,7 @@ func TestExecutor_Time_Clear_Quantums(t *testing.T) {
defer c.Close()
hldr := c.GetHolder(0)
var rangeTests = []struct {
rangeTests := []struct {
quantum pilosa.TimeQuantum
expected []uint64
}{
@ -3942,10 +3959,11 @@ func TestExecutor_Execute_Existence(t *testing.T) {
node0 := c.GetNode(0)
// Set bits.
if _, err := node0.API.Query(context.Background(), &pilosa.QueryRequest{Index: c.Idx(), 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),
if _, err := node0.API.Query(context.Background(), &pilosa.QueryRequest{
Index: c.Idx(), 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)
}
@ -9199,6 +9217,8 @@ func TestExternalLookup(t *testing.T) {
// Populate a field with some data that can be used in queries.
c.CreateField(t, c.Idx(), pilosa.IndexOptions{TrackExistence: true}, "f")
c.ImportBits(t, c.Idx(), "f", [][2]uint64{
{0, 0},
{0, 4},
{1, 1},
{1, 3},
{2, 2},

View file

@ -17,26 +17,27 @@ const (
ErrUnknownType errors.Code = "ErrUnknownType"
ErrUnknownIdentifier errors.Code = "ErrUnknownIdentifier"
ErrTypeIncompatibleWithBitwiseOperator errors.Code = "ErrTypeIncompatibleWithBitwiseOperator"
ErrTypeIncompatibleWithLogicalOperator errors.Code = "ErrTypeIncompatibleWithLogicalOperator"
ErrTypeIncompatibleWithEqualityOperator errors.Code = "ErrTypeIncompatibleWithEqualityOperator"
ErrTypeIncompatibleWithComparisonOperator errors.Code = "ErrTypeIncompatibleWithComparisonOperator"
ErrTypeIncompatibleWithArithmeticOperator errors.Code = "ErrTypeIncompatibleWithArithmeticOperator"
ErrTypeIncompatibleWithConcatOperator errors.Code = "ErrTypeIncompatibleWithConcatOperator"
ErrTypeIncompatibleWithLikeOperator errors.Code = "ErrTypeIncompatibleWithLikeOperator"
ErrTypeIncompatibleWithBetweenOperator errors.Code = "ErrTypeIncompatibleWithBetweenOperator"
ErrTypeCannotBeUsedAsRangeSubscript errors.Code = "ErrTypeCannotBeUsedAsRangeSubscript"
ErrTypesAreNotEquatable errors.Code = "ErrTypesAreNotEquatable"
ErrTypeMismatch errors.Code = "ErrTypeMismatch"
ErrIncompatibleTypesForRangeSubscripts errors.Code = "ErrIncompatibleTypesForRangeSubscripts"
ErrExpressionListExpected errors.Code = "ErrExpressionListExpected"
ErrBooleanExpressionExpected errors.Code = "ErrBooleanExpressionExpected"
ErrIntExpressionExpected errors.Code = "ErrIntExpressionExpected"
ErrIntOrDecimalExpressionExpected errors.Code = "ErrIntOrDecimalExpressionExpected"
ErrIntOrDecimalOrTimestampExpressionExpected errors.Code = "ErrIntOrDecimalOrTimestampExpressionExpected"
ErrStringExpressionExpected errors.Code = "ErrStringExpressionExpected"
ErrSetExpressionExpected errors.Code = "ErrSetExpressionExpected"
ErrSingleRowExpected errors.Code = "ErrSingleRowExpected"
ErrTypeIncompatibleWithBitwiseOperator errors.Code = "ErrTypeIncompatibleWithBitwiseOperator"
ErrTypeIncompatibleWithLogicalOperator errors.Code = "ErrTypeIncompatibleWithLogicalOperator"
ErrTypeIncompatibleWithEqualityOperator errors.Code = "ErrTypeIncompatibleWithEqualityOperator"
ErrTypeIncompatibleWithComparisonOperator errors.Code = "ErrTypeIncompatibleWithComparisonOperator"
ErrTypeIncompatibleWithArithmeticOperator errors.Code = "ErrTypeIncompatibleWithArithmeticOperator"
ErrTypeIncompatibleWithConcatOperator errors.Code = "ErrTypeIncompatibleWithConcatOperator"
ErrTypeIncompatibleWithLikeOperator errors.Code = "ErrTypeIncompatibleWithLikeOperator"
ErrTypeIncompatibleWithBetweenOperator errors.Code = "ErrTypeIncompatibleWithBetweenOperator"
ErrTypeCannotBeUsedAsRangeSubscript errors.Code = "ErrTypeCannotBeUsedAsRangeSubscript"
ErrTypesAreNotEquatable errors.Code = "ErrTypesAreNotEquatable"
ErrTypeMismatch errors.Code = "ErrTypeMismatch"
ErrIncompatibleTypesForRangeSubscripts errors.Code = "ErrIncompatibleTypesForRangeSubscripts"
ErrExpressionListExpected errors.Code = "ErrExpressionListExpected"
ErrBooleanExpressionExpected errors.Code = "ErrBooleanExpressionExpected"
ErrIntExpressionExpected errors.Code = "ErrIntExpressionExpected"
ErrIntOrDecimalExpressionExpected errors.Code = "ErrIntOrDecimalExpressionExpected"
ErrIntOrDecimalOrTimestampExpressionExpected errors.Code = "ErrIntOrDecimalOrTimestampExpressionExpected"
ErrIntOrDecimalOrTimestampOrStringExpressionExpected errors.Code = "ErrIntOrDecimalOrTimestampOrStringExpressionExpected"
ErrStringExpressionExpected errors.Code = "ErrStringExpressionExpected"
ErrSetExpressionExpected errors.Code = "ErrSetExpressionExpected"
ErrSingleRowExpected errors.Code = "ErrSingleRowExpected"
// type related errors
@ -369,7 +370,7 @@ func NewErrTypeCannotBeUsedAsRangeSubscript(line, col int, type1 string) error {
func NewErrIncompatibleTypesForRangeSubscripts(line, col int, type1 string, type2 string) error {
return errors.New(
ErrIncompatibleTypesForRangeSubscripts,
fmt.Sprintf("[%d:%d] incompatible types '%s' and '%s' useds as range subscripts", line, col, type1, type2),
fmt.Sprintf("[%d:%d] incompatible types '%s' and '%s' used as range subscripts", line, col, type1, type2),
)
}
@ -422,6 +423,13 @@ func NewErrIntOrDecimalOrTimestampExpressionExpected(line, col int) error {
)
}
func NewErrIntOrDecimalOrTimestampOrStringExpressionExpected(line, col int) error {
return errors.New(
ErrIntOrDecimalOrTimestampOrStringExpressionExpected,
fmt.Sprintf("[%d:%d] integer, decimal, timestamp or string expression expected", line, col),
)
}
func NewErrStringExpressionExpected(line, col int) error {
return errors.New(
ErrStringExpressionExpected,

View file

@ -4,6 +4,7 @@ package planner
import (
"context"
"strings"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/sql3"
@ -14,7 +15,7 @@ import (
// compileAlterDatabaseStatement compiles an ALTER DATABASE statement into a
// PlanOperator.
func (p *ExecutionPlanner) compileAlterDatabaseStatement(ctx context.Context, stmt *parser.AlterDatabaseStatement) (_ types.PlanOperator, err error) {
databaseName := parser.IdentName(stmt.Name)
databaseName := strings.ToLower(parser.IdentName(stmt.Name))
// does the database exist
dbname := dax.DatabaseName(databaseName)

View file

@ -24,7 +24,7 @@ const (
// compileAlterTableStatement compiles an ALTER TABLE statement into a
// PlanOperator.
func (p *ExecutionPlanner) compileAlterTableStatement(ctx context.Context, stmt *parser.AlterTableStatement) (_ types.PlanOperator, err error) {
tableName := parser.IdentName(stmt.Name)
tableName := strings.ToLower(parser.IdentName(stmt.Name))
// does the table exist
tname := dax.TableName(tableName)
@ -37,7 +37,7 @@ func (p *ExecutionPlanner) compileAlterTableStatement(ctx context.Context, stmt
}
if stmt.Drop.IsValid() {
columnName := parser.IdentName(stmt.DropColumnName)
columnName := strings.ToLower(parser.IdentName(stmt.DropColumnName))
// does this column exist
found := false
@ -54,7 +54,7 @@ func (p *ExecutionPlanner) compileAlterTableStatement(ctx context.Context, stmt
return NewPlanOpQuery(p, NewPlanOpAlterTable(p, tableName, alterOpDrop, columnName, "", nil), p.sql), nil
} else if stmt.Add.IsValid() {
col := stmt.ColumnDef
columnName := parser.IdentName(col.Name)
columnName := strings.ToLower(parser.IdentName(col.Name))
// does this column exist
for _, f := range tbl.Fields {
@ -70,8 +70,8 @@ func (p *ExecutionPlanner) compileAlterTableStatement(ctx context.Context, stmt
return NewPlanOpQuery(p, NewPlanOpAlterTable(p, tableName, alterOpAdd, "", columnName, column), p.sql), nil
} else if stmt.Rename.IsValid() {
oldColumnName := parser.IdentName(stmt.OldColumnName)
newColumnName := parser.IdentName(stmt.NewColumnName)
oldColumnName := strings.ToLower(parser.IdentName(stmt.OldColumnName))
newColumnName := strings.ToLower(parser.IdentName(stmt.NewColumnName))
return NewPlanOpQuery(p, NewPlanOpAlterTable(p, tableName, alterOpRename, oldColumnName, newColumnName, nil), p.sql), nil
} else {
return nil, sql3.NewErrInternal("unhandled alter operation")
@ -86,7 +86,7 @@ func (p *ExecutionPlanner) analyzeAlterTableStatement(stmt *parser.AlterTableSta
//no checks for now
} else if stmt.Add.IsValid() {
col := stmt.ColumnDef
columnName := parser.IdentName(col.Name)
columnName := strings.ToLower(parser.IdentName(col.Name))
typeName := parser.IdentName(col.Type.Name)
if !parser.IsValidTypeName(typeName) {
return sql3.NewErrUnknownType(col.Type.Name.NamePos.Line, col.Type.Name.NamePos.Column, typeName)
@ -103,8 +103,8 @@ func (p *ExecutionPlanner) analyzeAlterTableStatement(stmt *parser.AlterTableSta
}
} else if stmt.Rename.IsValid() {
//check the new and old are not the same
oldColumnName := parser.IdentName(stmt.OldColumnName)
newColumnName := parser.IdentName(stmt.NewColumnName)
oldColumnName := strings.ToLower(parser.IdentName(stmt.OldColumnName))
newColumnName := strings.ToLower(parser.IdentName(stmt.NewColumnName))
if strings.EqualFold(oldColumnName, newColumnName) {
return sql3.NewErrDuplicateColumn(stmt.NewColumnName.NamePos.Line, stmt.NewColumnName.NamePos.Column, newColumnName)
}

View file

@ -20,7 +20,7 @@ import (
// compileBulkInsertStatement compiles a BULK INSERT statement into a
// PlanOperator.
func (p *ExecutionPlanner) compileBulkInsertStatement(ctx context.Context, stmt *parser.BulkInsertStatement) (_ types.PlanOperator, err error) {
tableName := parser.IdentName(stmt.Table)
tableName := strings.ToLower(parser.IdentName(stmt.Table))
tname := dax.TableName(tableName)
tbl, err := p.schemaAPI.TableByName(ctx, tname)
@ -114,7 +114,7 @@ func (p *ExecutionPlanner) compileBulkInsertStatement(ctx context.Context, stmt
for _, m := range stmt.Columns {
for idx, fld := range tbl.Fields {
if strings.EqualFold(string(fld.Name), m.Name) {
options.targetColumns = append(options.targetColumns, newQualifiedRefPlanExpression(tableName, m.Name, idx, fieldSQLDataType(pilosa.FieldToFieldInfo(fld))))
options.targetColumns = append(options.targetColumns, newQualifiedRefPlanExpression(tableName, strings.ToLower(m.Name), idx, fieldSQLDataType(pilosa.FieldToFieldInfo(fld))))
break
}
}
@ -159,7 +159,7 @@ func (p *ExecutionPlanner) compileBulkInsertStatement(ctx context.Context, stmt
// error if anything is invalid.
func (p *ExecutionPlanner) analyzeBulkInsertStatement(ctx context.Context, stmt *parser.BulkInsertStatement) error {
// check referred to table exists
tableName := parser.IdentName(stmt.Table)
tableName := strings.ToLower(parser.IdentName(stmt.Table))
tname := dax.TableName(tableName)
tbl, err := p.schemaAPI.TableByName(ctx, tname)
if err != nil {

View file

@ -4,6 +4,7 @@ package planner
import (
"strconv"
"strings"
"github.com/featurebasedb/featurebase/v3/sql3"
"github.com/featurebasedb/featurebase/v3/sql3/parser"
@ -13,7 +14,7 @@ import (
// compileCreateDatabaseStatement compiles a CREATE DATABASE statement into a
// PlanOperator.
func (p *ExecutionPlanner) compileCreateDatabaseStatement(stmt *parser.CreateDatabaseStatement) (_ types.PlanOperator, err error) {
databaseName := parser.IdentName(stmt.Name)
databaseName := strings.ToLower(parser.IdentName(stmt.Name))
failIfExists := !stmt.IfNotExists.IsValid()
units := 0

View file

@ -26,7 +26,7 @@ type createTableField struct {
// compileCreateTableStatement compiles a CREATE TABLE statement into a
// PlanOperator.
func (p *ExecutionPlanner) compileCreateTableStatement(ctx context.Context, stmt *parser.CreateTableStatement) (_ types.PlanOperator, err error) {
tableName := parser.IdentName(stmt.Name)
tableName := strings.ToLower(parser.IdentName(stmt.Name))
failIfExists := !stmt.IfNotExists.IsValid()
// apply table options
@ -51,7 +51,7 @@ func (p *ExecutionPlanner) compileCreateTableStatement(ctx context.Context, stmt
var columns = []*createTableField{}
for _, col := range stmt.Columns {
columnName := parser.IdentName(col.Name)
columnName := strings.ToLower(parser.IdentName(col.Name))
typeName := parser.IdentName(col.Type.Name)
if strings.ToLower(columnName) == "_id" {
@ -78,7 +78,7 @@ func (p *ExecutionPlanner) compileCreateTableStatement(ctx context.Context, stmt
// compiles a column def
func (p *ExecutionPlanner) compileColumn(ctx context.Context, col *parser.ColumnDefinition) (*createTableField, error) {
var err error
columnName := parser.IdentName(col.Name)
columnName := strings.ToLower(parser.IdentName(col.Name))
typeName := parser.IdentName(col.Type.Name)
column := &createTableField{
@ -251,7 +251,7 @@ func (p *ExecutionPlanner) analyzeCreateTableStatement(stmt *parser.CreateTableS
//iterate columns, check types, check constraints, ensure we have no dupe names and make sure there is an _id column
checkedColumns := make(map[string]string)
for _, col := range stmt.Columns {
columnName := parser.IdentName(col.Name)
columnName := strings.ToLower(parser.IdentName(col.Name))
_, ok := checkedColumns[strings.ToLower(columnName)]
if ok {
return sql3.NewErrDuplicateColumn(col.Name.NamePos.Line, col.Name.NamePos.Column, columnName)

View file

@ -4,6 +4,7 @@ package planner
import (
"context"
"strings"
"github.com/featurebasedb/featurebase/v3/sql3/parser"
"github.com/featurebasedb/featurebase/v3/sql3/planner/types"
@ -11,7 +12,7 @@ import (
// compileCreateViewStatement compiles a parser.CreateViewStatement AST into a PlanOperator
func (p *ExecutionPlanner) compileCreateViewStatement(stmt *parser.CreateViewStatement) (types.PlanOperator, error) {
viewName := parser.IdentName(stmt.Name)
viewName := strings.ToLower(parser.IdentName(stmt.Name))
view := &viewSystemObject{
name: viewName,
}
@ -29,7 +30,7 @@ func (p *ExecutionPlanner) compileCreateViewStatement(stmt *parser.CreateViewSta
// compileAlterViewStatement compiles a parser.AlterViewStatement AST into a PlanOperator
func (p *ExecutionPlanner) compileAlterViewStatement(stmt *parser.AlterViewStatement) (types.PlanOperator, error) {
viewName := parser.IdentName(stmt.Name)
viewName := strings.ToLower(parser.IdentName(stmt.Name))
view := &viewSystemObject{
name: viewName,
}

View file

@ -4,6 +4,7 @@ package planner
import (
"context"
"strings"
"github.com/featurebasedb/featurebase/v3/sql3/parser"
"github.com/featurebasedb/featurebase/v3/sql3/planner/types"
@ -13,7 +14,7 @@ import (
func (p *ExecutionPlanner) compileDeleteStatement(stmt *parser.DeleteStatement) (types.PlanOperator, error) {
query := NewPlanOpQuery(p, NewPlanOpNullTable(), p.sql)
tableName := parser.IdentName(stmt.TableName.Name)
tableName := strings.ToLower(parser.IdentName(stmt.TableName.Name))
// source expression
source, err := p.compileSource(query, stmt.Source)

View file

@ -4,6 +4,7 @@ package planner
import (
"context"
"strings"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/sql3"
@ -14,7 +15,7 @@ import (
// compileDropDatabaseStatement compiles a DROP DATABASE statement into a
// PlanOperator.
func (p *ExecutionPlanner) compileDropDatabaseStatement(ctx context.Context, stmt *parser.DropDatabaseStatement) (_ types.PlanOperator, err error) {
databaseName := parser.IdentName(stmt.Name)
databaseName := strings.ToLower(parser.IdentName(stmt.Name))
dbname := dax.DatabaseName(databaseName)
db, err := p.schemaAPI.DatabaseByName(ctx, dbname)
if err != nil {

View file

@ -4,6 +4,7 @@ package planner
import (
"context"
"strings"
pilosa "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/dax"
@ -15,7 +16,7 @@ import (
// compileDropTableStatement compiles a DROP TABLE statement into a
// PlanOperator.
func (p *ExecutionPlanner) compileDropTableStatement(ctx context.Context, stmt *parser.DropTableStatement) (_ types.PlanOperator, err error) {
tableName := parser.IdentName(stmt.Name)
tableName := strings.ToLower(parser.IdentName(stmt.Name))
tname := dax.TableName(tableName)
tbl, err := p.schemaAPI.TableByName(ctx, tname)
if err != nil {

View file

@ -4,6 +4,7 @@ package planner
import (
"context"
"strings"
"github.com/featurebasedb/featurebase/v3/sql3"
"github.com/featurebasedb/featurebase/v3/sql3/parser"
@ -12,7 +13,7 @@ import (
// compileDropViewStatement compiles a DROP VIEW statement into a PlanOperator.
func (p *ExecutionPlanner) compileDropViewStatement(ctx context.Context, stmt *parser.DropViewStatement) (_ types.PlanOperator, err error) {
viewName := parser.IdentName(stmt.Name)
viewName := strings.ToLower(parser.IdentName(stmt.Name))
v, err := p.getViewByName(ctx, viewName)
if err != nil {
return nil, err

View file

@ -15,7 +15,7 @@ import (
// compileInsertStatement compiles an INSERT statement into a PlanOperator.
func (p *ExecutionPlanner) compileInsertStatement(ctx context.Context, stmt *parser.InsertStatement) (_ types.PlanOperator, err error) {
tableName := parser.IdentName(stmt.Table)
tableName := strings.ToLower(parser.IdentName(stmt.Table))
targetColumns := []*qualifiedRefPlanExpression{}
insertValues := [][]types.PlanExpression{}
@ -31,7 +31,7 @@ func (p *ExecutionPlanner) compileInsertStatement(ctx context.Context, stmt *par
if len(stmt.Columns) > 0 {
for _, columnIdent := range stmt.Columns {
colName := parser.IdentName(columnIdent)
colName := strings.ToLower(parser.IdentName(columnIdent))
if strings.EqualFold(colName, "_id") {
targetColumns = append(targetColumns, newQualifiedRefPlanExpression(tableName, colName, 0, parser.NewDataTypeID()))
@ -74,7 +74,7 @@ func (p *ExecutionPlanner) compileInsertStatement(ctx context.Context, stmt *par
// anything is invalid.
func (p *ExecutionPlanner) analyzeInsertStatement(ctx context.Context, stmt *parser.InsertStatement) error {
// Check that referred table exists.
tableName := parser.IdentName(stmt.Table)
tableName := strings.ToLower(parser.IdentName(stmt.Table))
tname := dax.TableName(tableName)
tbl, err := p.schemaAPI.TableByName(ctx, tname)
if err != nil {
@ -108,7 +108,7 @@ func (p *ExecutionPlanner) analyzeInsertStatement(ctx context.Context, stmt *par
// dupes.
columnNameMap := make(map[string]struct{})
for _, columnIdent := range stmt.Columns {
colName := parser.IdentName(columnIdent)
colName := strings.ToLower(parser.IdentName(columnIdent))
var typeName parser.ExprDataType
if strings.EqualFold(colName, "_id") {

View file

@ -309,10 +309,10 @@ func (p *ExecutionPlanner) compileSource(scope *PlanOpQuery, source parser.Sourc
case *parser.QualifiedTableName:
tableName := parser.IdentName(sourceExpr.Name)
tableName := strings.ToLower(parser.IdentName(sourceExpr.Name))
// doing this check here because we don't have a 'system' flag that exists in the FB schema
st, ok := systemTables[strings.ToLower(tableName)]
st, ok := systemTables[tableName]
if ok {
var op types.PlanOperator
op = NewPlanOpSystemTable(p, st)
@ -418,7 +418,7 @@ func (p *ExecutionPlanner) analyzeSource(ctx context.Context, source parser.Sour
case *parser.QualifiedTableName:
objectName := parser.IdentName(source.Name)
objectName := strings.ToLower(parser.IdentName(source.Name))
// check views first
view, err := p.getViewByName(ctx, objectName)
@ -620,7 +620,7 @@ func (p *ExecutionPlanner) analyzeSelectStatementWildcards(stmt *parser.SelectSt
} else {
//handle the case of a qualified ref with a *
if ref, ok := col.Expr.(*parser.QualifiedRef); ok && ref.Star.IsValid() {
refName := parser.IdentName(ref.Table)
refName := strings.ToLower(parser.IdentName(ref.Table))
src := stmt.Source.SourceFromAlias(refName)
if src == nil {
return sql3.NewErrTableNotFound(ref.Table.NamePos.Line, ref.Table.NamePos.Column, refName)

View file

@ -139,7 +139,7 @@ func (p *ExecutionPlanner) compileShowTablesStatement(ctx context.Context, stmt
}
func (p *ExecutionPlanner) compileShowColumnsStatement(ctx context.Context, stmt *parser.ShowColumnsStatement) (_ types.PlanOperator, err error) {
tableName := parser.IdentName(stmt.TableName)
tableName := strings.ToLower(parser.IdentName(stmt.TableName))
tname := dax.TableName(tableName)
tbl, err := p.schemaAPI.TableByName(ctx, tname)
if err != nil {
@ -159,70 +159,65 @@ func (p *ExecutionPlanner) compileShowColumnsStatement(ctx context.Context, stmt
columnName: "name",
columnIndex: 1,
dataType: parser.NewDataTypeString(),
}, &qualifiedRefPlanExpression{ // the SQL3 data type description
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "type",
columnIndex: 2,
dataType: parser.NewDataTypeString(),
}, &qualifiedRefPlanExpression{ // the FeatureBase 'native' data type description
tableName: "fb_table_columns",
columnName: "internal_type",
columnIndex: 3,
dataType: parser.NewDataTypeString(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "created_at",
columnIndex: 4,
columnIndex: 3,
dataType: parser.NewDataTypeTimestamp(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "keys",
columnIndex: 5,
columnIndex: 4,
dataType: parser.NewDataTypeBool(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "cache_type",
columnIndex: 6,
columnIndex: 5,
dataType: parser.NewDataTypeString(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "cache_size",
columnIndex: 7,
columnIndex: 6,
dataType: parser.NewDataTypeInt(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "scale",
columnIndex: 8,
columnIndex: 7,
dataType: parser.NewDataTypeInt(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "min",
columnIndex: 9,
columnIndex: 8,
dataType: parser.NewDataTypeInt(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "max",
columnIndex: 10,
columnIndex: 9,
dataType: parser.NewDataTypeInt(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "timeunit",
columnIndex: 11,
columnIndex: 10,
dataType: parser.NewDataTypeString(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "epoch",
columnIndex: 12,
columnIndex: 11,
dataType: parser.NewDataTypeInt(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "timequantum",
columnIndex: 13,
columnIndex: 12,
dataType: parser.NewDataTypeString(),
}, &qualifiedRefPlanExpression{
tableName: "fb_table_columns",
columnName: "ttl",
columnIndex: 14,
columnIndex: 13,
dataType: parser.NewDataTypeString(),
}}
@ -230,7 +225,7 @@ func (p *ExecutionPlanner) compileShowColumnsStatement(ctx context.Context, stmt
}
func (p *ExecutionPlanner) compileShowCreateTableStatement(ctx context.Context, stmt *parser.ShowCreateTableStatement) (_ types.PlanOperator, err error) {
tableName := parser.IdentName(stmt.TableName)
tableName := strings.ToLower(parser.IdentName(stmt.TableName))
tname := dax.TableName(tableName)
if _, err := p.schemaAPI.TableByName(ctx, tname); err != nil {
if isTableNotFoundError(err) {

View file

@ -1170,7 +1170,7 @@ func (n *betweenOpPlanExpression) Evaluate(currentRow []interface{}) (interface{
switch rType := n.rhs.Type().(type) {
case *parser.DataTypeRange:
switch rType.SubscriptType.(type) {
switch sType := rType.SubscriptType.(type) {
case *parser.DataTypeInt:
nl, nlok := evalLhs.(int64)
@ -1201,8 +1201,40 @@ func (n *betweenOpPlanExpression) Evaluate(currentRow []interface{}) (interface{
}
return result, nil
case *parser.DataTypeDecimal:
nl, nlok := evalLhs.(pql.Decimal)
if !(nlok) {
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", nlok)
}
crl, err := coerceValue(exprRange.lhs.Type(), sType, rangeLower, parser.Pos{Line: 0, Column: 0})
if err != nil {
return nil, err
}
rl, ok := crl.(pql.Decimal)
if !(ok) {
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", crl)
}
cru, err := coerceValue(exprRange.rhs.Type(), sType, rangeUpper, parser.Pos{Line: 0, Column: 0})
if err != nil {
return nil, err
}
ru, ok := cru.(pql.Decimal)
if !(ok) {
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", cru)
}
result := nl.GreaterThanOrEqualTo(rl) && nl.LessThanOrEqualTo(ru)
if n.op == parser.NOTBETWEEN {
result = !result
}
return result, nil
default:
return nil, sql3.NewErrInternalf("unexpected range type '%T'", rType.SubscriptType)
return nil, sql3.NewErrInternalf("unexpected range type '%T'", sType)
}
default:
@ -2560,7 +2592,7 @@ func (p *ExecutionPlanner) compileExpr(expr parser.Expr) (_ types.PlanExpression
return ref, nil
case *parser.QualifiedRef:
ref := newQualifiedRefPlanExpression(parser.IdentName(expr.Table), parser.IdentName(expr.Column), expr.ColumnIndex, expr.DataType())
ref := newQualifiedRefPlanExpression(strings.ToLower(parser.IdentName(expr.Table)), strings.ToLower(parser.IdentName(expr.Column)), expr.ColumnIndex, expr.DataType())
return ref, nil
case *parser.Range:

View file

@ -435,6 +435,15 @@ func (a *aggregateAvg) Update(ctx context.Context, row types.Row) error {
thisVal := pql.FromInt64(thisIVal, returnType.Scale)
a.sum = pql.AddDecimal(thisVal, aggVal)
case *parser.DataTypeID:
thisIVal, ok := v.(uint64)
if !ok {
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
}
thisVal := pql.FromInt64(int64(thisIVal), returnType.Scale)
a.sum = pql.AddDecimal(thisVal, aggVal)
default:
return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", dataType)
}
@ -610,6 +619,21 @@ func (m *aggregateMin) Update(ctx context.Context, row types.Row) error {
m.val = thisVal
}
case *parser.DataTypeString:
thisVal, ok := v.(string)
if !ok {
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
}
aggVal, ok := m.val.(string)
if !ok {
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
}
if thisVal < aggVal {
m.val = thisVal
}
default:
return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", dataType)
}
@ -752,6 +776,21 @@ func (m *aggregateMax) Update(ctx context.Context, row types.Row) error {
m.val = thisVal
}
case *parser.DataTypeString:
thisVal, ok := v.(string)
if !ok {
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
}
aggVal, ok := m.val.(string)
if !ok {
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
}
if thisVal > aggVal {
m.val = thisVal
}
default:
return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", dataType)
}

View file

@ -440,6 +440,19 @@ func (p *ExecutionPlanner) analyzeBinaryExpression(ctx context.Context, expr *pa
if !typeIsCompatibleWithEqualityOperator(y.DataType()) {
return nil, sql3.NewErrTypeIncompatibleWithEqualityOperator(y.Pos().Line, y.Pos().Column, op.String(), y.DataType().TypeDescription())
}
if typeIsTimestamp(x.DataType()) && y.IsLiteral() && typeIsString(y.DataType()) {
// we have a string literal on the rhs being compared to a date so
// try to convert to a date literal
rhs, ok := y.(*parser.StringLit)
if !ok {
return nil, sql3.NewErrInternalf("unexpected expression type '%T'", y)
}
newRhs := rhs.ConvertToTimestamp()
if newRhs != nil {
expr.Y = newRhs
y = newRhs
}
}
if !typesAreComparable(x.DataType(), y.DataType()) {
return nil, sql3.NewErrTypesAreNotEquatable(x.Pos().Line, x.Pos().Column, x.DataType().TypeDescription(), y.DataType().TypeDescription())
}
@ -794,11 +807,11 @@ func (p *ExecutionPlanner) analyzeRangeExpression(ctx context.Context, expr *par
if !typeCanBeUsedInRange(expr.Y.DataType()) {
return nil, sql3.NewErrTypeCannotBeUsedAsRangeSubscript(expr.Y.Pos().Line, expr.Y.Pos().Column, expr.Y.DataType().TypeDescription())
}
if !typesOfRangeBoundsAreTheSame(expr.X.DataType(), expr.Y.DataType()) {
canbeUsed, coercedType := typesOfRangeBoundsAreTheSame(expr.X.DataType(), expr.Y.DataType())
if !canbeUsed {
return nil, sql3.NewErrIncompatibleTypesForRangeSubscripts(expr.Pos().Line, expr.Pos().Column, expr.X.DataType().TypeDescription(), expr.Y.DataType().TypeDescription())
}
expr.ResultDataType = parser.NewDataTypeRange(expr.X.DataType())
expr.ResultDataType = parser.NewDataTypeRange(coercedType)
return expr, nil
}

View file

@ -165,8 +165,8 @@ func (p *ExecutionPlanner) analyzeCallExpression(ctx context.Context, call *pars
}
// make sure the ref is min/max-able
if !(typeIsInteger(ref.DataType()) || typeIsDecimal(ref.DataType()) || typeIsTimestamp(ref.DataType())) {
return nil, sql3.NewErrIntOrDecimalOrTimestampExpressionExpected(ref.Table.NamePos.Line, ref.Table.NamePos.Column)
if !(typeIsInteger(ref.DataType()) || typeIsDecimal(ref.DataType()) || typeIsTimestamp(ref.DataType()) || typeIsString(ref.DataType())) {
return nil, sql3.NewErrIntOrDecimalOrTimestampOrStringExpressionExpected(ref.Table.NamePos.Line, ref.Table.NamePos.Column)
}
// return the data type of the referenced column

View file

@ -180,7 +180,166 @@ func (p *ExecutionPlanner) generatePQLCallFromBinaryExpr(ctx context.Context, ex
Children: []*pql.Call{x, y},
}, nil
case parser.EQ, parser.NE, parser.LT, parser.LE, parser.GT, parser.GE:
case parser.EQ:
lhs, ok := expr.lhs.(*qualifiedRefPlanExpression)
if !ok {
return nil, sql3.NewErrInternalf("unexpected lhs %T", expr.lhs)
}
pqlValue, err := planExprToValue(expr.rhs)
if err != nil {
return nil, err
}
switch typ := expr.lhs.Type().(type) {
case *parser.DataTypeInt:
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: &pql.Condition{
Op: pql.EQ,
Value: pqlValue,
},
},
}, nil
case *parser.DataTypeID:
if strings.EqualFold(lhs.columnName, "_id") {
return &pql.Call{
Name: "ConstRow",
Args: map[string]interface{}{
"columns": []interface{}{pqlValue},
},
Type: pql.PrecallGlobal,
}, nil
}
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: pqlValue,
},
}, nil
case *parser.DataTypeString:
if strings.EqualFold(lhs.columnName, "_id") {
return &pql.Call{
Name: "ConstRow",
Args: map[string]interface{}{
"columns": []interface{}{pqlValue},
},
Type: pql.PrecallGlobal,
}, nil
}
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: pqlValue,
},
}, nil
case *parser.DataTypeTimestamp:
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: &pql.Condition{
Op: pql.EQ,
Value: pqlValue,
},
},
}, nil
case *parser.DataTypeBool:
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: pqlValue,
},
}, nil
case *parser.DataTypeDecimal:
val, ok := pqlValue.(float64)
if !ok {
return nil, sql3.NewErrInternalf("unexpected type '%T", pqlValue)
}
d := pql.FromFloat64(val)
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: &pql.Condition{
Op: pql.EQ,
Value: d,
},
},
}, nil
default:
return nil, sql3.NewErrInternalf("unsupported type for binary expression: %v (%T)", typ, typ)
}
case parser.NE:
lhs, ok := expr.lhs.(*qualifiedRefPlanExpression)
if !ok {
return nil, sql3.NewErrInternalf("unexpected lhs %T", expr.lhs)
}
pqlValue, err := planExprToValue(expr.rhs)
if err != nil {
return nil, err
}
switch typ := expr.lhs.Type().(type) {
case *parser.DataTypeInt:
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: &pql.Condition{
Op: pql.NEQ,
Value: pqlValue,
},
},
}, nil
case *parser.DataTypeID:
return nil, sql3.NewErrUnsupported(0, 0, true, "not equal operator on id typed columns")
case *parser.DataTypeString:
return nil, sql3.NewErrUnsupported(0, 0, true, "not equal operator on string typed columns")
case *parser.DataTypeTimestamp:
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: &pql.Condition{
Op: pql.NEQ,
Value: pqlValue,
},
},
}, nil
case *parser.DataTypeBool:
return nil, sql3.NewErrUnsupported(0, 0, true, "not equal operator on bool typed columns")
case *parser.DataTypeDecimal:
val, ok := pqlValue.(float64)
if !ok {
return nil, sql3.NewErrInternalf("unexpected type '%T", pqlValue)
}
d := pql.FromFloat64(val)
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: &pql.Condition{
Op: pql.NEQ,
Value: d,
},
},
}, nil
default:
return nil, sql3.NewErrInternalf("unsupported type for binary expression: %v (%T)", typ, typ)
}
case parser.LT, parser.LE, parser.GT, parser.GE:
lhs, ok := expr.lhs.(*qualifiedRefPlanExpression)
if !ok {
return nil, sql3.NewErrInternalf("unexpected lhs %T", expr.lhs)
@ -208,82 +367,32 @@ func (p *ExecutionPlanner) generatePQLCallFromBinaryExpr(ctx context.Context, ex
}, nil
case *parser.DataTypeID:
// TODO (pok) range queries on _id are not supported
if strings.EqualFold(lhs.columnName, "_id") {
cr := &pql.Call{
Name: "ConstRow",
Args: map[string]interface{}{
"columns": []interface{}{pqlValue},
},
Type: pql.PrecallGlobal,
}
// TODO (pok) when we fix FB-1828 (https://molecula.atlassian.net/browse/FB-1828)
// we can remove this - ConstRow returns a ghost record, thus to eliminate
// we interset with All
return &pql.Call{
Name: "Intersect",
Children: []*pql.Call{
{
Name: "All",
},
cr,
},
}, nil
}
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: pqlValue,
},
}, nil
return nil, sql3.NewErrUnsupported(0, 0, false, "range queries on id typed columns")
case *parser.DataTypeString:
// TODO (pok) range queries on _id are not supported
if strings.EqualFold(lhs.columnName, "_id") {
cr := &pql.Call{
Name: "ConstRow",
Args: map[string]interface{}{
"columns": []interface{}{pqlValue},
},
Type: pql.PrecallGlobal,
}
// TODO (pok) when we fix FB-1828 (https://molecula.atlassian.net/browse/FB-1828)
// we can remove this - ConstRow returns a ghost record, thus to eliminate
// we interset with All
return &pql.Call{
Name: "Intersect",
Children: []*pql.Call{
{
Name: "All",
},
cr,
},
}, nil
return nil, sql3.NewErrUnsupported(0, 0, false, "range queries on string typed columns")
case *parser.DataTypeTimestamp:
pqlOp, err := sqlToPQLOp(op)
if err != nil {
return nil, err
}
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: pqlValue,
},
}, nil
case *parser.DataTypeTimestamp:
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: pqlValue,
},
}, nil
case *parser.DataTypeBool:
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: pqlValue,
lhs.columnName: &pql.Condition{
Op: pqlOp,
Value: pqlValue,
},
},
}, nil
case *parser.DataTypeDecimal:
pqlOp, err := sqlToPQLOp(op)
if err != nil {
return nil, err
}
val, ok := pqlValue.(float64)
if !ok {
return nil, sql3.NewErrInternalf("unexpected type '%T", pqlValue)
@ -292,7 +401,10 @@ func (p *ExecutionPlanner) generatePQLCallFromBinaryExpr(ctx context.Context, ex
return &pql.Call{
Name: "Row",
Args: map[string]interface{}{
lhs.columnName: d,
lhs.columnName: &pql.Condition{
Op: pqlOp,
Value: d,
},
},
}, nil

View file

@ -181,20 +181,34 @@ func typesAreRangeComparable(testTypeL parser.ExprDataType, testTypeR parser.Exp
default:
return false, sql3.NewErrInternalf("unhandled rhs type '%T' for lhs type '%T'", rhsType, lhsType)
}
case *parser.DataTypeInt, *parser.DataTypeID:
switch lhsType := testTypeL.(type) {
case *parser.DataTypeID:
return true, nil
case *parser.DataTypeInt:
return true, nil
case *parser.DataTypeDecimal:
// change subscript type to be decimal
rhsType.SubscriptType = lhsType
return true, nil
default:
return false, sql3.NewErrInternalf("unhandled rhs type '%T' for lhs type '%T'", rhsType, lhsType)
}
case *parser.DataTypeDecimal:
switch lhsType := testTypeL.(type) {
case *parser.DataTypeDecimal:
return true, nil
default:
return false, sql3.NewErrInternalf("unhandled rhs type '%T' for lhs type '%T'", rhsType, lhsType)
}
}
default:
return false, sql3.NewErrInternalf("type '%T' is not a range type", rhsType)
}
@ -363,7 +377,7 @@ func typesAreAssignmentCompatible(targetType parser.ExprDataType, sourceType par
// returns true if the type can be used as a range subscript
func typeCanBeUsedInRange(testType parser.ExprDataType) bool {
switch testType.(type) {
case *parser.DataTypeID, *parser.DataTypeInt, *parser.DataTypeTimestamp:
case *parser.DataTypeID, *parser.DataTypeInt, *parser.DataTypeTimestamp, *parser.DataTypeDecimal:
return true
default:
return false
@ -371,43 +385,62 @@ func typeCanBeUsedInRange(testType parser.ExprDataType) bool {
}
// returns true if the types can be considered the same when used as a range bounds
func typesOfRangeBoundsAreTheSame(testTypeL parser.ExprDataType, testTypeR parser.ExprDataType) bool {
func typesOfRangeBoundsAreTheSame(testTypeL parser.ExprDataType, testTypeR parser.ExprDataType) (bool, parser.ExprDataType) {
switch testTypeL.(type) {
case *parser.DataTypeInt:
switch testTypeR.(type) {
case *parser.DataTypeInt:
return true
return true, testTypeL
case *parser.DataTypeID:
return true
return true, testTypeL
case *parser.DataTypeDecimal:
// 'widen' both to decimal
return true, testTypeR
default:
return false
return false, nil
}
case *parser.DataTypeID:
switch testTypeR.(type) {
case *parser.DataTypeID:
return true
return true, testTypeL
case *parser.DataTypeInt:
return true
return true, testTypeL
default:
return false
return false, nil
}
case *parser.DataTypeTimestamp:
switch testTypeR.(type) {
case *parser.DataTypeTimestamp:
return true
return true, testTypeL
default:
return false
return false, nil
}
case *parser.DataTypeDecimal:
switch testTypeR.(type) {
case *parser.DataTypeInt:
return true, testTypeL
case *parser.DataTypeID:
return true, testTypeL
case *parser.DataTypeDecimal:
return true, testTypeL
default:
return false, nil
}
default:
return false
return false, nil
}
}

View file

@ -62,11 +62,6 @@ func (p *PlanOpFeatureBaseColumns) Schema() types.Schema {
ColumnName: "type",
Type: parser.NewDataTypeString(),
},
&types.PlannerColumn{
RelationName: "fb_table_columns",
ColumnName: "internal_type",
Type: parser.NewDataTypeString(),
},
&types.PlannerColumn{
RelationName: "fb_table_columns",
ColumnName: "created_at",
@ -156,7 +151,6 @@ func (i *showColumnsRowIter) Next(ctx context.Context) (types.Row, error) {
fields[i.rowIndex].Name,
fields[i.rowIndex].Name,
fields[i.rowIndex].Type,
fields[i.rowIndex].Type,
tm.Format(time.RFC3339),
fields[i.rowIndex].StringKeys(),
fields[i.rowIndex].Options.CacheType,

View file

@ -109,7 +109,7 @@ var systemTables = map[string]*systemTable{
&types.PlannerColumn{
RelationName: fbClusterNodes,
ColumnName: "space_used",
Type: parser.NewDataTypeBool(),
Type: parser.NewDataTypeInt(),
},
},
requiresFanout: false,

View file

@ -636,6 +636,18 @@ func tryToReplaceGroupByWithPQLAggregate(ctx context.Context, a *ExecutionPlanne
return thisNode, true, nil
}
// make sure all the aggregates are bsi types
for _, agg := range thisNode.Aggregates {
aggregable, ok := agg.(types.Aggregable)
if !ok {
return n, false, sql3.NewErrInternalf("unexpected aggregate function arg type '%T'", agg)
}
switch aggregable.AggExpression().Type().(type) {
case *parser.DataTypeID, *parser.DataTypeString:
return thisNode, true, nil
}
}
ops := make([]*PlanOpPQLAggregate, 0)
for _, agg := range thisNode.Aggregates {

View file

@ -188,7 +188,7 @@ func TestPlanner_Show(t *testing.T) {
wireQueryFieldString("uri"),
wireQueryFieldString("grpc_uri"),
wireQueryFieldBool("is_primary"),
wireQueryFieldBool("space_used"),
wireQueryFieldInt("space_used"),
}, columns); diff != "" {
t.Fatal(diff)
}
@ -318,7 +318,6 @@ func TestPlanner_Show(t *testing.T) {
wireQueryFieldString("_id"),
wireQueryFieldString("name"),
wireQueryFieldString("type"),
wireQueryFieldString("internal_type"),
wireQueryFieldTimestamp("created_at"),
wireQueryFieldBool("keys"),
wireQueryFieldString("cache_type"),
@ -348,7 +347,6 @@ func TestPlanner_Show(t *testing.T) {
wireQueryFieldString("_id"),
wireQueryFieldString("name"),
wireQueryFieldString("type"),
wireQueryFieldString("internal_type"),
wireQueryFieldTimestamp("created_at"),
wireQueryFieldBool("keys"),
wireQueryFieldString("cache_type"),
@ -688,6 +686,20 @@ func TestPlanner_CreateTable(t *testing.T) {
}
})
t.Run("CreateTableMixedCaseColumn", func(t *testing.T) {
_, _, err := sql_test.MustQueryRows(t, server, `create table lowercase (_id id, name string, SomeColumn string, legalname string);`)
if err != nil {
t.Fatal(err)
}
})
t.Run("CreateTableMixedCaseColumn", func(t *testing.T) {
_, _, err := sql_test.MustQueryRows(t, server, `create table MixedCcase (_id id, name string, SomeColumn string, legalname string);`)
if err != nil {
t.Fatal(err)
}
})
t.Run("DropTable1", func(t *testing.T) {
_, _, err := sql_test.MustQueryRows(t, server, `drop table allcoltypes`)
if err != nil {
@ -730,7 +742,6 @@ func TestPlanner_CreateTable(t *testing.T) {
wireQueryFieldString("_id"),
wireQueryFieldString("name"),
wireQueryFieldString("type"),
wireQueryFieldString("internal_type"),
wireQueryFieldTimestamp("created_at"),
wireQueryFieldBool("keys"),
wireQueryFieldString("cache_type"),

View file

@ -16,6 +16,14 @@ var TableTests []TableTest = []TableTest{
selectTests,
selectKeyedTests,
selectHavingTests,
filterPredicates,
filterPredicatesIdKey,
filterPredicatesId,
filterPredicatesInt,
filterPredicatesBool,
filterPredicatesTimestamp,
filterPredicatesDecimal,
filterPredicatesString,
orderByTests,
distinctTests,

View file

@ -288,14 +288,15 @@ var avgTests = TableTest{
srcHdr("i1", fldTypeInt, "min 0", "max 1000"),
srcHdr("d1", fldTypeDecimal2),
srcHdr("s1", fldTypeString),
srcHdr("id1", fldTypeID),
),
srcRows(
srcRow(int64(1), int64(10), float64(10), string("foo")),
srcRow(int64(2), int64(10), float64(10), string("foo")),
srcRow(int64(3), int64(11), float64(11), string("foo")),
srcRow(int64(4), int64(12), float64(12), string("foo")),
srcRow(int64(5), int64(12), float64(12), string("foo")),
srcRow(int64(6), int64(13), float64(13), string("foo")),
srcRow(int64(1), int64(10), float64(10), string("foo"), int64(10)),
srcRow(int64(2), int64(10), float64(10), string("foo"), int64(11)),
srcRow(int64(3), int64(11), float64(11), string("foo"), int64(12)),
srcRow(int64(4), int64(12), float64(12), string("foo"), int64(13)),
srcRow(int64(5), int64(12), float64(12), string("foo"), int64(14)),
srcRow(int64(6), int64(13), float64(13), string("foo"), int64(15)),
),
),
SQLTests: []SQLTest{
@ -323,6 +324,22 @@ var avgTests = TableTest{
),
ExpErr: "integer or decimal expression expected",
},
{
SQLs: sqls(
"SELECT avg(id1) AS avg_rows FROM avg_test",
),
ExpHdrs: hdrs(
hdr("avg_rows", featurebase.WireQueryField{
Type: dax.BaseTypeDecimal + "(4)",
BaseType: dax.BaseTypeDecimal,
TypeInfo: map[string]interface{}{"scale": int64(4)},
}),
),
ExpRows: rows(
row(pql.NewDecimal(125000, 4)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"SELECT avg(i1) AS avg_rows FROM avg_test",
@ -430,12 +447,12 @@ var minmaxTests = TableTest{
srcHdr("ts1", fldTypeTimestamp),
),
srcRows(
srcRow(int64(1), int64(10), float64(10), string("foo"), timestampFromString("2013-07-15T01:18:46Z")),
srcRow(int64(2), int64(10), float64(10), string("foo"), timestampFromString("2014-07-15T01:18:46Z")),
srcRow(int64(3), int64(11), float64(11), string("foo"), timestampFromString("2015-07-15T01:18:46Z")),
srcRow(int64(4), int64(12), float64(12), string("foo"), timestampFromString("2016-07-15T01:18:46Z")),
srcRow(int64(5), int64(12), float64(12), string("foo"), timestampFromString("2017-07-15T01:18:46Z")),
srcRow(int64(6), int64(13), float64(13), string("foo"), timestampFromString("2018-07-15T01:18:46Z")),
srcRow(int64(1), int64(10), float64(10), string("afoo"), timestampFromString("2013-07-15T01:18:46Z")),
srcRow(int64(2), int64(10), float64(10), string("bfoo"), timestampFromString("2014-07-15T01:18:46Z")),
srcRow(int64(3), int64(11), float64(11), string("cfoo"), timestampFromString("2015-07-15T01:18:46Z")),
srcRow(int64(4), int64(12), float64(12), string("dfoo"), timestampFromString("2016-07-15T01:18:46Z")),
srcRow(int64(5), int64(12), float64(12), string("efoo"), timestampFromString("2017-07-15T01:18:46Z")),
srcRow(int64(6), int64(13), float64(13), string("ffoo"), timestampFromString("2018-07-15T01:18:46Z")),
),
),
SQLTests: []SQLTest{
@ -470,9 +487,26 @@ var minmaxTests = TableTest{
{
SQLs: sqls(
"SELECT min(s1) AS p_rows FROM minmax_test",
),
ExpHdrs: hdrs(
hdr("p_rows", fldTypeString),
),
ExpRows: rows(
row(string("afoo")),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"SELECT max(s1) AS p_rows FROM minmax_test",
),
ExpErr: "integer, decimal or timestamp expression expected",
ExpHdrs: hdrs(
hdr("p_rows", fldTypeString),
),
ExpRows: rows(
row(string("ffoo")),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(

View file

@ -44,6 +44,18 @@ var betweenTests = TableTest{
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select d1 between 10 and 15 from between_all_types",
),
ExpHdrs: hdrs(
hdr("", fldTypeBool),
),
ExpRows: rows(
row(bool(true)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select b1 between true and false from between_all_types",
@ -54,7 +66,13 @@ var betweenTests = TableTest{
SQLs: sqls(
"select d1 between 1.23 and 4.56 from between_all_types",
),
ExpErr: "type 'decimal(2)' cannot be used as a range subscript",
ExpHdrs: hdrs(
hdr("", fldTypeBool),
),
ExpRows: rows(
row(bool(false)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
@ -145,6 +163,18 @@ var notBetweenTests = TableTest{
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select d1 not between 10 and 15 from between_all_types",
),
ExpHdrs: hdrs(
hdr("", fldTypeBool),
),
ExpRows: rows(
row(bool(false)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select b1 not between true and false from not_between_all_types",
@ -155,7 +185,13 @@ var notBetweenTests = TableTest{
SQLs: sqls(
"select d1 not between 1.23 and 4.56 from not_between_all_types",
),
ExpErr: "type 'decimal(2)' cannot be used as a range subscript",
ExpHdrs: hdrs(
hdr("", fldTypeBool),
),
ExpRows: rows(
row(bool(true)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(

View file

@ -0,0 +1,507 @@
package defs
var filterPredicates = TableTest{
Table: tbl(
"filter_predicates",
srcHdrs(
srcHdr("_id", fldTypeID),
srcHdr("i1", fldTypeInt),
srcHdr("b1", fldTypeBool),
srcHdr("id1", fldTypeID),
srcHdr("ids1", fldTypeIDSet),
srcHdr("d1", fldTypeDecimal2),
srcHdr("s1", fldTypeString),
srcHdr("ss1", fldTypeStringSet),
srcHdr("ts1", fldTypeTimestamp),
),
srcRows(
srcRow(int64(1), int64(10), bool(false), int64(1), []int64{10, 20, 30}, float64(10.00), string("10"), []string{"10", "20", "30"}, string("2001-11-01T22:08:41+00:00")),
srcRow(int64(2), int64(20), bool(true), int64(2), []int64{11, 21, 31}, float64(20.00), string("20"), []string{"11", "21", "31"}, string("2002-11-01T22:08:41+00:00")),
srcRow(int64(3), int64(30), bool(false), int64(3), []int64{12, 22, 32}, float64(30.00), string("30"), []string{"12", "22", "32"}, string("2003-11-01T22:08:41+00:00")),
srcRow(int64(4), int64(40), bool(false), int64(4), []int64{10, 20, 30}, float64(40.00), string("40"), []string{"10", "20", "30"}, string("2004-11-01T22:08:41+00:00")),
srcRow(int64(5), int64(50), bool(true), int64(5), []int64{11, 21, 31}, float64(50.00), string("50"), []string{"11", "21", "31"}, string("2005-11-01T22:08:41+00:00")),
srcRow(int64(6), int64(60), bool(false), int64(6), []int64{12, 22, 32}, float64(60.00), string("60"), []string{"12", "22", "32"}, string("2006-11-01T22:08:41+00:00")),
),
),
}
var filterPredicatesIdKey = TableTest{
SQLTests: []SQLTest{
{
SQLs: sqls(
"select _id from filter_predicates where _id != 1",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(2)),
row(int64(3)),
row(int64(4)),
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where _id = 1",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where _id > 5",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where _id >= 5",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where _id < 2",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where _id <= 2",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
row(int64(2)),
),
Compare: CompareExactUnordered,
},
},
}
var filterPredicatesId = TableTest{
SQLTests: []SQLTest{
{
SQLs: sqls(
"select _id from filter_predicates where id1 != 1",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(2)),
row(int64(3)),
row(int64(4)),
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where id1 = 1",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where id1 > 5",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where id1 >= 5",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where id1 < 2",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where id1 <= 2",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
row(int64(2)),
),
Compare: CompareExactUnordered,
},
},
}
var filterPredicatesInt = TableTest{
SQLTests: []SQLTest{
{
SQLs: sqls(
"select _id from filter_predicates where i1 != 10",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(2)),
row(int64(3)),
row(int64(4)),
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where i1 = 10",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where i1 > 50",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where i1 >= 50",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where i1 < 20",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where i1 <= 20",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
row(int64(2)),
),
Compare: CompareExactUnordered,
},
},
}
var filterPredicatesBool = TableTest{
SQLTests: []SQLTest{
{
SQLs: sqls(
"select _id from filter_predicates where b1 != true",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
row(int64(3)),
row(int64(4)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where b1 = true",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(2)),
row(int64(5)),
),
Compare: CompareExactUnordered,
},
},
}
var filterPredicatesTimestamp = TableTest{
SQLTests: []SQLTest{
{
SQLs: sqls(
"select _id from filter_predicates where ts1 != '2001-11-01T22:08:41+00:00'",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(2)),
row(int64(3)),
row(int64(4)),
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where ts1 = '2001-11-01T22:08:41+00:00'",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where ts1 > '2005-11-01T22:08:41+00:00'",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where ts1 >= '2005-11-01T22:08:41+00:00'",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where ts1 < '2002-11-01T22:08:41+00:00'",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where ts1 <= '2002-11-01T22:08:41+00:00'",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
row(int64(2)),
),
Compare: CompareExactUnordered,
},
},
}
var filterPredicatesDecimal = TableTest{
SQLTests: []SQLTest{
{
SQLs: sqls(
"select _id from filter_predicates where d1 != 10.00",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(2)),
row(int64(3)),
row(int64(4)),
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where d1 = 10.00",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where d1 > 50.00",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where d1 >= 50.00",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where d1 < 20.00",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where d1 <= 20.00",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
row(int64(2)),
),
Compare: CompareExactUnordered,
},
},
}
var filterPredicatesString = TableTest{
SQLTests: []SQLTest{
{
SQLs: sqls(
"select _id from filter_predicates where s1 != '10'",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(2)),
row(int64(3)),
row(int64(4)),
row(int64(5)),
row(int64(6)),
),
Compare: CompareExactUnordered,
},
{
SQLs: sqls(
"select _id from filter_predicates where s1 = '10'",
),
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
),
Compare: CompareExactUnordered,
},
},
}