mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
non-sql aggregation, top, decimal and sundries (#2328)
* fixed a bunch of issues with non-pql aggregation; moved some decimal related functionality; made top actually top (for the non-pql case); experimental create function
* drive up test coverage
(cherry picked from commit 0be0c42b66)
This commit is contained in:
parent
dca0dd84e3
commit
d6d5ddb501
15 changed files with 954 additions and 803 deletions
|
|
@ -3,6 +3,7 @@
|
|||
package pql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
|
@ -344,6 +345,33 @@ const (
|
|||
stateMantissa = "mantissa"
|
||||
)
|
||||
|
||||
// FromInt64 converts an int64 into a Decimal.
|
||||
func FromInt64(i int64, scale int64) Decimal {
|
||||
us := i * Pow10(scale)
|
||||
return NewDecimal(us, scale)
|
||||
}
|
||||
|
||||
// FromFloat64 converts a float into a Decimal.
|
||||
func FromFloat64(f float64) Decimal {
|
||||
scale := decimalPlaces(fmt.Sprintf("%v", f))
|
||||
us := int64(f * math.Pow(10, float64(scale)))
|
||||
return NewDecimal(us, int64(scale))
|
||||
}
|
||||
|
||||
func decimalPlaces(v string) int {
|
||||
i := strings.IndexByte(v, '.')
|
||||
if i > -1 {
|
||||
return len(v) - i - 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// FromFloat64WithScale converts a float into a Decimal.
|
||||
func FromFloat64WithScale(f float64, scale int) (Decimal, error) {
|
||||
us := int64(f * math.Pow(10, float64(scale)))
|
||||
return NewDecimal(us, int64(scale)), nil
|
||||
}
|
||||
|
||||
// ParseDecimal parses a string into a Decimal.
|
||||
func ParseDecimal(s string) (Decimal, error) {
|
||||
var sign bool
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@ func (*ColumnDefinition) node() {}
|
|||
func (*CommitStatement) node() {}
|
||||
func (*CreateIndexStatement) node() {}
|
||||
func (*CreateTableStatement) node() {}
|
||||
func (*CreateTriggerStatement) node() {}
|
||||
func (*CreateFunctionStatement) node() {}
|
||||
func (*CreateViewStatement) node() {}
|
||||
func (*DateLit) node() {}
|
||||
func (*DefaultConstraint) node() {}
|
||||
func (*DeleteStatement) node() {}
|
||||
func (*DropIndexStatement) node() {}
|
||||
func (*DropTableStatement) node() {}
|
||||
func (*DropTriggerStatement) node() {}
|
||||
func (*DropFunctionStatement) node() {}
|
||||
func (*DropViewStatement) node() {}
|
||||
func (*Exists) node() {}
|
||||
func (*ExplainStatement) node() {}
|
||||
|
|
@ -51,7 +51,7 @@ func (*ForeignKeyArg) node() {}
|
|||
func (*ForeignKeyConstraint) node() {}
|
||||
func (*FrameSpec) node() {}
|
||||
func (*Ident) node() {}
|
||||
func (*VariableRef) node() {}
|
||||
func (*Variable) node() {}
|
||||
func (*IndexedColumn) node() {}
|
||||
func (*InsertStatement) node() {}
|
||||
func (*JoinClause) node() {}
|
||||
|
|
@ -98,29 +98,29 @@ type Statement interface {
|
|||
stmt()
|
||||
}
|
||||
|
||||
func (*AlterTableStatement) stmt() {}
|
||||
func (*AnalyzeStatement) stmt() {}
|
||||
func (*BeginStatement) stmt() {}
|
||||
func (*BulkInsertStatement) stmt() {}
|
||||
func (*ShowTablesStatement) stmt() {}
|
||||
func (*ShowColumnsStatement) stmt() {}
|
||||
func (*CommitStatement) stmt() {}
|
||||
func (*CreateIndexStatement) stmt() {}
|
||||
func (*CreateTableStatement) stmt() {}
|
||||
func (*CreateTriggerStatement) stmt() {}
|
||||
func (*CreateViewStatement) stmt() {}
|
||||
func (*DeleteStatement) stmt() {}
|
||||
func (*DropIndexStatement) stmt() {}
|
||||
func (*DropTableStatement) stmt() {}
|
||||
func (*DropTriggerStatement) stmt() {}
|
||||
func (*DropViewStatement) stmt() {}
|
||||
func (*ExplainStatement) stmt() {}
|
||||
func (*InsertStatement) stmt() {}
|
||||
func (*ReleaseStatement) stmt() {}
|
||||
func (*RollbackStatement) stmt() {}
|
||||
func (*SavepointStatement) stmt() {}
|
||||
func (*SelectStatement) stmt() {}
|
||||
func (*UpdateStatement) stmt() {}
|
||||
func (*AlterTableStatement) stmt() {}
|
||||
func (*AnalyzeStatement) stmt() {}
|
||||
func (*BeginStatement) stmt() {}
|
||||
func (*BulkInsertStatement) stmt() {}
|
||||
func (*ShowTablesStatement) stmt() {}
|
||||
func (*ShowColumnsStatement) stmt() {}
|
||||
func (*CommitStatement) stmt() {}
|
||||
func (*CreateIndexStatement) stmt() {}
|
||||
func (*CreateTableStatement) stmt() {}
|
||||
func (*CreateFunctionStatement) stmt() {}
|
||||
func (*CreateViewStatement) stmt() {}
|
||||
func (*DeleteStatement) stmt() {}
|
||||
func (*DropIndexStatement) stmt() {}
|
||||
func (*DropTableStatement) stmt() {}
|
||||
func (*DropFunctionStatement) stmt() {}
|
||||
func (*DropViewStatement) stmt() {}
|
||||
func (*ExplainStatement) stmt() {}
|
||||
func (*InsertStatement) stmt() {}
|
||||
func (*ReleaseStatement) stmt() {}
|
||||
func (*RollbackStatement) stmt() {}
|
||||
func (*SavepointStatement) stmt() {}
|
||||
func (*SelectStatement) stmt() {}
|
||||
func (*UpdateStatement) stmt() {}
|
||||
|
||||
// CloneStatement returns a deep copy stmt.
|
||||
func CloneStatement(stmt Statement) Statement {
|
||||
|
|
@ -141,7 +141,7 @@ func CloneStatement(stmt Statement) Statement {
|
|||
return stmt.Clone()
|
||||
case *CreateTableStatement:
|
||||
return stmt.Clone()
|
||||
case *CreateTriggerStatement:
|
||||
case *CreateFunctionStatement:
|
||||
return stmt.Clone()
|
||||
case *CreateViewStatement:
|
||||
return stmt.Clone()
|
||||
|
|
@ -151,7 +151,7 @@ func CloneStatement(stmt Statement) Statement {
|
|||
return stmt.Clone()
|
||||
case *DropTableStatement:
|
||||
return stmt.Clone()
|
||||
case *DropTriggerStatement:
|
||||
case *DropFunctionStatement:
|
||||
return stmt.Clone()
|
||||
case *DropViewStatement:
|
||||
return stmt.Clone()
|
||||
|
|
@ -218,7 +218,7 @@ func (*DateLit) expr() {}
|
|||
func (*Exists) expr() {}
|
||||
func (*ExprList) expr() {}
|
||||
func (*Ident) expr() {}
|
||||
func (*VariableRef) expr() {}
|
||||
func (*Variable) expr() {}
|
||||
func (*NullLit) expr() {}
|
||||
func (*IntegerLit) expr() {}
|
||||
func (*FloatLit) expr() {}
|
||||
|
|
@ -268,7 +268,7 @@ func CloneExpr(expr Expr) Expr {
|
|||
return expr.Clone()
|
||||
case *UnaryExpr:
|
||||
return expr.Clone()
|
||||
case *VariableRef:
|
||||
case *Variable:
|
||||
return expr.Clone()
|
||||
|
||||
default:
|
||||
|
|
@ -1436,7 +1436,10 @@ func cloneIdents(a []*Ident) []*Ident {
|
|||
|
||||
// String returns the string representation of the expression.
|
||||
func (i *Ident) String() string {
|
||||
return `"` + strings.Replace(i.Name, `"`, `""`, -1) + `"`
|
||||
if i.Quoted {
|
||||
return `"` + strings.Replace(i.Name, `"`, `""`, -1) + `"`
|
||||
}
|
||||
return i.Name
|
||||
}
|
||||
|
||||
// IdentName returns the name of ident. Returns a blank string if ident is nil.
|
||||
|
|
@ -1447,25 +1450,25 @@ func IdentName(ident *Ident) string {
|
|||
return ident.Name
|
||||
}
|
||||
|
||||
type VariableRef struct {
|
||||
type Variable struct {
|
||||
NamePos Pos // variable position
|
||||
Name string // variable name
|
||||
VariableIndex int
|
||||
VarDataType ExprDataType
|
||||
}
|
||||
|
||||
func (expr *VariableRef) IsLiteral() bool { return false }
|
||||
func (expr *Variable) IsLiteral() bool { return false }
|
||||
|
||||
func (expr *VariableRef) DataType() ExprDataType {
|
||||
func (expr *Variable) DataType() ExprDataType {
|
||||
return expr.VarDataType
|
||||
}
|
||||
|
||||
func (expr *VariableRef) Pos() Pos {
|
||||
func (expr *Variable) Pos() Pos {
|
||||
return expr.NamePos
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of i.
|
||||
func (i *VariableRef) Clone() *VariableRef {
|
||||
func (i *Variable) Clone() *Variable {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1474,11 +1477,11 @@ func (i *VariableRef) Clone() *VariableRef {
|
|||
}
|
||||
|
||||
// String returns the string representation of the expression.
|
||||
func (i *VariableRef) String() string {
|
||||
return `"` + i.Name + `"`
|
||||
func (i *Variable) String() string {
|
||||
return i.Name
|
||||
}
|
||||
|
||||
func (i *VariableRef) VarName() string {
|
||||
func (i *Variable) VarName() string {
|
||||
return i.Name[1:]
|
||||
}
|
||||
|
||||
|
|
@ -2632,98 +2635,68 @@ func (s *DropIndexStatement) String() string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
type CreateTriggerStatement struct {
|
||||
type ParameterDefinition struct {
|
||||
Name *Variable // parameter name
|
||||
Type *Type // data type
|
||||
}
|
||||
|
||||
type CreateFunctionStatement struct {
|
||||
Create Pos // position of CREATE keyword
|
||||
Trigger Pos // position of TRIGGER keyword
|
||||
Function Pos // position of FUNCTION keyword
|
||||
If Pos // position of IF keyword
|
||||
IfNot Pos // position of NOT keyword after IF
|
||||
IfNotExists Pos // position of EXISTS keyword after IF NOT
|
||||
Name *Ident // index name
|
||||
|
||||
Before Pos // position of BEFORE keyword
|
||||
After Pos // position of AFTER keyword
|
||||
Instead Pos // position of INSTEAD keyword
|
||||
InsteadOf Pos // position of OF keyword after INSTEAD
|
||||
Lparen Pos // position of parameter LParen
|
||||
Parameters []*ParameterDefinition // parameters
|
||||
Rparen Pos // position of parameter RParen
|
||||
|
||||
Delete Pos // position of DELETE keyword
|
||||
Insert Pos // position of INSERT keyword
|
||||
Update Pos // position of UPDATE keyword
|
||||
UpdateOf Pos // position of OF keyword after UPDATE
|
||||
UpdateOfColumns []*Ident // columns list for UPDATE OF
|
||||
On Pos // position of ON keyword
|
||||
Table *Ident // table name
|
||||
Returns Pos // position of RETURNS keyword
|
||||
ReturnDef *ParameterDefinition // return def
|
||||
|
||||
For Pos // position of FOR keyword
|
||||
ForEach Pos // position of EACH keyword after FOR
|
||||
ForEachRow Pos // position of ROW keyword after FOR EACH
|
||||
|
||||
When Pos // position of WHEN keyword
|
||||
WhenExpr Expr // conditional expression
|
||||
As Pos // position of AS keyword
|
||||
|
||||
Begin Pos // position of BEGIN keyword
|
||||
Body []Statement // trigger body
|
||||
Body []Statement // function body
|
||||
End Pos // position of END keyword
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of s.
|
||||
func (s *CreateTriggerStatement) Clone() *CreateTriggerStatement {
|
||||
func (s *CreateFunctionStatement) Clone() *CreateFunctionStatement {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
other := *s
|
||||
other.Name = s.Name.Clone()
|
||||
other.UpdateOfColumns = cloneIdents(s.UpdateOfColumns)
|
||||
other.Table = s.Table.Clone()
|
||||
other.WhenExpr = CloneExpr(s.WhenExpr)
|
||||
other.Body = cloneStatements(s.Body)
|
||||
return &other
|
||||
}
|
||||
|
||||
// String returns the string representation of the statement.
|
||||
func (s *CreateTriggerStatement) String() string {
|
||||
func (s *CreateFunctionStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("CREATE TRIGGER")
|
||||
buf.WriteString("CREATE FUNCTION")
|
||||
if s.IfNotExists.IsValid() {
|
||||
buf.WriteString(" IF NOT EXISTS")
|
||||
}
|
||||
fmt.Fprintf(&buf, " %s", s.Name.String())
|
||||
|
||||
if s.Before.IsValid() {
|
||||
buf.WriteString(" BEFORE")
|
||||
} else if s.After.IsValid() {
|
||||
buf.WriteString(" AFTER")
|
||||
} else if s.InsteadOf.IsValid() {
|
||||
buf.WriteString(" INSTEAD OF")
|
||||
}
|
||||
|
||||
if s.Delete.IsValid() {
|
||||
buf.WriteString(" DELETE")
|
||||
} else if s.Insert.IsValid() {
|
||||
buf.WriteString(" INSERT")
|
||||
} else if s.Update.IsValid() {
|
||||
buf.WriteString(" UPDATE")
|
||||
if s.UpdateOf.IsValid() {
|
||||
buf.WriteString(" OF ")
|
||||
for i, col := range s.UpdateOfColumns {
|
||||
if i != 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(col.String())
|
||||
if len(s.Parameters) > 0 {
|
||||
buf.WriteString(" (")
|
||||
for idx, p := range s.Parameters {
|
||||
if idx > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s %s", p.Name.Name, p.Type.Name)
|
||||
}
|
||||
buf.WriteString(")")
|
||||
}
|
||||
|
||||
fmt.Fprintf(&buf, " ON %s", s.Table.String())
|
||||
buf.WriteString(" RETURNS ")
|
||||
fmt.Fprintf(&buf, "%s %s", s.ReturnDef.Name, s.ReturnDef.Type.Name)
|
||||
|
||||
if s.ForEachRow.IsValid() {
|
||||
buf.WriteString(" FOR EACH ROW")
|
||||
}
|
||||
|
||||
if s.WhenExpr != nil {
|
||||
fmt.Fprintf(&buf, " WHEN %s", s.WhenExpr.String())
|
||||
}
|
||||
|
||||
buf.WriteString(" BEGIN")
|
||||
buf.WriteString(" AS BEGIN")
|
||||
for i := range s.Body {
|
||||
fmt.Fprintf(&buf, " %s;", s.Body[i].String())
|
||||
}
|
||||
|
|
@ -2732,7 +2705,7 @@ func (s *CreateTriggerStatement) String() string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
type DropTriggerStatement struct {
|
||||
type DropFunctionStatement struct {
|
||||
Drop Pos // position of DROP keyword
|
||||
Trigger Pos // position of TRIGGER keyword
|
||||
If Pos // position of IF keyword
|
||||
|
|
@ -2741,7 +2714,7 @@ type DropTriggerStatement struct {
|
|||
}
|
||||
|
||||
// Clone returns a deep copy of s.
|
||||
func (s *DropTriggerStatement) Clone() *DropTriggerStatement {
|
||||
func (s *DropFunctionStatement) Clone() *DropFunctionStatement {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -2750,7 +2723,7 @@ func (s *DropTriggerStatement) Clone() *DropTriggerStatement {
|
|||
return &other
|
||||
}
|
||||
|
||||
func (s *DropTriggerStatement) String() string {
|
||||
func (s *DropFunctionStatement) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("DROP TRIGGER")
|
||||
if s.IfExists.IsValid() {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ func TestAlterTableStatement_String(t *testing.T) {
|
|||
Name: &parser.Ident{Name: "foo"},
|
||||
OldColumnName: &parser.Ident{Name: "col1"},
|
||||
NewColumnName: &parser.Ident{Name: "col2"},
|
||||
}, `ALTER TABLE "foo" RENAME COLUMN "col1" TO "col2"`)
|
||||
}, `ALTER TABLE foo RENAME COLUMN col1 TO col2`)
|
||||
|
||||
AssertStatementStringer(t, &parser.AlterTableStatement{
|
||||
Name: &parser.Ident{Name: "foo"},
|
||||
|
|
@ -68,11 +68,11 @@ func TestAlterTableStatement_String(t *testing.T) {
|
|||
Name: &parser.Ident{Name: "bar"},
|
||||
Type: &parser.Type{Name: &parser.Ident{Name: "INTEGER"}},
|
||||
},
|
||||
}, `ALTER TABLE "foo" ADD COLUMN "bar" INTEGER`)
|
||||
}, `ALTER TABLE foo ADD COLUMN bar INTEGER`)
|
||||
AssertStatementStringer(t, &parser.AlterTableStatement{
|
||||
Name: &parser.Ident{Name: "foo"},
|
||||
DropColumnName: &parser.Ident{Name: "bar"},
|
||||
}, `ALTER TABLE "foo" DROP COLUMN "bar"`)
|
||||
}, `ALTER TABLE foo DROP COLUMN bar`)
|
||||
}
|
||||
|
||||
/*func TestAnalyzeStatement_String(t *testing.T) {
|
||||
|
|
@ -130,7 +130,7 @@ func TestCreateTableStatement_String(t *testing.T) {
|
|||
Type: &parser.Type{Name: &parser.Ident{Name: "STRING"}},
|
||||
},
|
||||
},
|
||||
}, `CREATE TABLE IF NOT EXISTS "foo" ("bar" INTEGER, "baz" STRING)`)
|
||||
}, `CREATE TABLE IF NOT EXISTS foo (bar INTEGER, baz STRING)`)
|
||||
|
||||
AssertStatementStringer(t, &parser.CreateTableStatement{
|
||||
Name: &parser.Ident{Name: "foo"},
|
||||
|
|
@ -212,15 +212,15 @@ func TestCreateTableStatement_String(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, `CREATE TABLE IF NOT EXISTS "foo" (`+
|
||||
`"boolcol" BOOL, `+
|
||||
`"decimalcol" DECIMAL MIN 100.25 MAX 1000.75, `+
|
||||
`"idcol" ID CACHETYPE RANKED SIZE 10000, `+
|
||||
`"idsetcol" IDSET CACHETYPE RANKED SIZE 10000, `+
|
||||
`"intcol" INTEGER MIN 100 MAX 1000, `+
|
||||
`"stringcol" STRING CACHETYPE RANKED SIZE 10000, `+
|
||||
`"stringsetcol" STRINGSET CACHETYPE RANKED SIZE 10000, `+
|
||||
`"timestampcol" TIMESTAMP TIMEUNIT 's' EPOCH '2021-01-01T00:00:00Z'`+
|
||||
}, `CREATE TABLE IF NOT EXISTS foo (`+
|
||||
`boolcol BOOL, `+
|
||||
`decimalcol DECIMAL MIN 100.25 MAX 1000.75, `+
|
||||
`idcol ID CACHETYPE RANKED SIZE 10000, `+
|
||||
`idsetcol IDSET CACHETYPE RANKED SIZE 10000, `+
|
||||
`intcol INTEGER MIN 100 MAX 1000, `+
|
||||
`stringcol STRING CACHETYPE RANKED SIZE 10000, `+
|
||||
`stringsetcol STRINGSET CACHETYPE RANKED SIZE 10000, `+
|
||||
`timestampcol TIMESTAMP TIMEUNIT 's' EPOCH '2021-01-01T00:00:00Z'`+
|
||||
`)`)
|
||||
}
|
||||
|
||||
|
|
@ -361,51 +361,35 @@ func OLDTestCreateTableStatement_String(t *testing.T) {
|
|||
}, `CREATE TABLE "foo" AS SELECT *`)
|
||||
}
|
||||
|
||||
func TestCreateTriggerStatement_String(t *testing.T) {
|
||||
t.Skip("CREATE TRIGGER is currently disabled in the parser")
|
||||
AssertStatementStringer(t, &parser.CreateTriggerStatement{
|
||||
Name: &parser.Ident{Name: "trig"},
|
||||
Insert: pos(0),
|
||||
Table: &parser.Ident{Name: "tbl"},
|
||||
Body: []parser.Statement{
|
||||
&parser.DeleteStatement{Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "tbl2"}}},
|
||||
func TestCreateFunctionStatement_String(t *testing.T) {
|
||||
AssertStatementStringer(t, &parser.CreateFunctionStatement{
|
||||
Name: &parser.Ident{Name: "func"},
|
||||
Parameters: []*parser.ParameterDefinition{
|
||||
{
|
||||
Name: &parser.Variable{Name: "@param1"},
|
||||
Type: &parser.Type{Name: &parser.Ident{Name: "int"}},
|
||||
},
|
||||
},
|
||||
}, `CREATE TRIGGER "trig" INSERT ON "tbl" BEGIN DELETE FROM "tbl2"; END`)
|
||||
|
||||
AssertStatementStringer(t, &parser.CreateTriggerStatement{
|
||||
Name: &parser.Ident{Name: "trig"},
|
||||
Before: pos(0),
|
||||
Delete: pos(0),
|
||||
ForEachRow: pos(0),
|
||||
Table: &parser.Ident{Name: "tbl"},
|
||||
Body: []parser.Statement{
|
||||
&parser.DeleteStatement{Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "x"}}},
|
||||
ReturnDef: &parser.ParameterDefinition{
|
||||
Name: &parser.Variable{Name: "@scalar"},
|
||||
Type: &parser.Type{Name: &parser.Ident{Name: "int"}},
|
||||
},
|
||||
}, `CREATE TRIGGER "trig" BEFORE DELETE ON "tbl" FOR EACH ROW BEGIN DELETE FROM "x"; END`)
|
||||
}, `CREATE FUNCTION func (@param1 int) RETURNS @scalar int AS BEGIN END`)
|
||||
|
||||
AssertStatementStringer(t, &parser.CreateTriggerStatement{
|
||||
AssertStatementStringer(t, &parser.CreateFunctionStatement{
|
||||
IfNotExists: pos(0),
|
||||
Name: &parser.Ident{Name: "trig"},
|
||||
After: pos(0),
|
||||
Update: pos(0),
|
||||
Table: &parser.Ident{Name: "tbl"},
|
||||
WhenExpr: &parser.BoolLit{Value: true},
|
||||
Body: []parser.Statement{
|
||||
&parser.DeleteStatement{Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "x"}}},
|
||||
Name: &parser.Ident{Name: "func"},
|
||||
Parameters: []*parser.ParameterDefinition{
|
||||
{
|
||||
Name: &parser.Variable{Name: "@param1"},
|
||||
Type: &parser.Type{Name: &parser.Ident{Name: "int"}},
|
||||
},
|
||||
},
|
||||
}, `CREATE TRIGGER IF NOT EXISTS "trig" AFTER UPDATE ON "tbl" WHEN TRUE BEGIN DELETE FROM "x"; END`)
|
||||
|
||||
AssertStatementStringer(t, &parser.CreateTriggerStatement{
|
||||
Name: &parser.Ident{Name: "trig"},
|
||||
InsteadOf: pos(0),
|
||||
Update: pos(0),
|
||||
UpdateOf: pos(0),
|
||||
UpdateOfColumns: []*parser.Ident{{Name: "x"}, {Name: "y"}},
|
||||
Table: &parser.Ident{Name: "tbl"},
|
||||
Body: []parser.Statement{
|
||||
&parser.DeleteStatement{Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "x"}}},
|
||||
ReturnDef: &parser.ParameterDefinition{
|
||||
Name: &parser.Variable{Name: "@scalar"},
|
||||
Type: &parser.Type{Name: &parser.Ident{Name: "int"}},
|
||||
},
|
||||
}, `CREATE TRIGGER "trig" INSTEAD OF UPDATE OF "x", "y" ON "tbl" BEGIN DELETE FROM "x"; END`)
|
||||
}, `CREATE FUNCTION IF NOT EXISTS func (@param1 int) RETURNS @scalar int AS BEGIN END`)
|
||||
}
|
||||
|
||||
func TestCreateViewStatement_String(t *testing.T) {
|
||||
|
|
@ -433,7 +417,7 @@ func TestCreateViewStatement_String(t *testing.T) {
|
|||
func TestDeleteStatement_String(t *testing.T) {
|
||||
AssertStatementStringer(t, &parser.DeleteStatement{
|
||||
Table: &parser.QualifiedTableName{Name: &parser.Ident{Name: "tbl"}, Alias: &parser.Ident{Name: "tbl2"}},
|
||||
}, `DELETE FROM "tbl" AS "tbl2"`)
|
||||
}, `DELETE FROM tbl AS tbl2`)
|
||||
|
||||
// AssertStatementStringer(t, &sql.DeleteStatement{
|
||||
// Table: &sql.QualifiedTableName{Name: &sql.Ident{Name: "tbl"}, Index: &sql.Ident{Name: "idx"}},
|
||||
|
|
@ -483,21 +467,21 @@ func TestDropIndexStatement_String(t *testing.T) {
|
|||
func TestDropTableStatement_String(t *testing.T) {
|
||||
AssertStatementStringer(t, &parser.DropTableStatement{
|
||||
Name: &parser.Ident{Name: "tbl"},
|
||||
}, `DROP TABLE "tbl"`)
|
||||
}, `DROP TABLE tbl`)
|
||||
|
||||
AssertStatementStringer(t, &parser.DropTableStatement{
|
||||
IfExists: pos(0),
|
||||
Name: &parser.Ident{Name: "tbl"},
|
||||
}, `DROP TABLE IF EXISTS "tbl"`)
|
||||
}, `DROP TABLE IF EXISTS tbl`)
|
||||
}
|
||||
|
||||
func TestDropTriggerStatement_String(t *testing.T) {
|
||||
t.Skip("DROP TRIGGER is currently disabled in the parser")
|
||||
AssertStatementStringer(t, &parser.DropTriggerStatement{
|
||||
AssertStatementStringer(t, &parser.DropFunctionStatement{
|
||||
Name: &parser.Ident{Name: "trig"},
|
||||
}, `DROP TRIGGER "trig"`)
|
||||
|
||||
AssertStatementStringer(t, &parser.DropTriggerStatement{
|
||||
AssertStatementStringer(t, &parser.DropFunctionStatement{
|
||||
IfExists: pos(0),
|
||||
Name: &parser.Ident{Name: "trig"},
|
||||
}, `DROP TRIGGER IF EXISTS "trig"`)
|
||||
|
|
@ -597,7 +581,7 @@ func TestInsertStatement_String(t *testing.T) {
|
|||
Exprs: []parser.Expr{&parser.NullLit{}, &parser.NullLit{}},
|
||||
},
|
||||
},
|
||||
}, `INSERT INTO "tbl" ("x", "y") VALUES (NULL, NULL)`)
|
||||
}, `INSERT INTO tbl (x, y) VALUES (NULL, NULL)`)
|
||||
|
||||
AssertStatementStringer(t, &parser.InsertStatement{
|
||||
Table: &parser.Ident{Name: "tbl"},
|
||||
|
|
@ -613,7 +597,7 @@ func TestInsertStatement_String(t *testing.T) {
|
|||
Exprs: []parser.Expr{&parser.IntegerLit{Value: "3"}, &parser.IntegerLit{Value: "4"}},
|
||||
},
|
||||
},
|
||||
}, `INSERT INTO "tbl" ("x", "y") VALUES (1, 2), (3, 4)`)
|
||||
}, `INSERT INTO tbl (x, y) VALUES (1, 2), (3, 4)`)
|
||||
|
||||
// AssertStatementStringer(t, &sql.InsertStatement{
|
||||
// WithClause: &sql.WithClause{
|
||||
|
|
@ -687,14 +671,14 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
{Expr: &parser.Ident{Name: "x"}, Alias: &parser.Ident{Name: "y"}},
|
||||
{Expr: &parser.Ident{Name: "z"}},
|
||||
},
|
||||
}, `SELECT "x" AS "y", "z"`)
|
||||
}, `SELECT x AS y, z`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Distinct: pos(0),
|
||||
Columns: []*parser.ResultColumn{
|
||||
{Expr: &parser.Ident{Name: "x"}},
|
||||
},
|
||||
}, `SELECT DISTINCT "x"`)
|
||||
}, `SELECT DISTINCT x`)
|
||||
|
||||
// AssertStatementStringer(t, &sql.SelectStatement{
|
||||
// All: pos(0),
|
||||
|
|
@ -709,7 +693,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
WhereExpr: &parser.BoolLit{Value: true},
|
||||
GroupByExprs: []parser.Expr{&parser.Ident{Name: "x"}, &parser.Ident{Name: "y"}},
|
||||
HavingExpr: &parser.Ident{Name: "z"},
|
||||
}, `SELECT * FROM "tbl" WHERE TRUE GROUP BY "x", "y" HAVING "z"`)
|
||||
}, `SELECT * FROM tbl WHERE TRUE GROUP BY x, y HAVING z`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Columns: []*parser.ResultColumn{{Star: pos(0)}},
|
||||
|
|
@ -717,7 +701,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
X: &parser.SelectStatement{Columns: []*parser.ResultColumn{{Star: pos(0)}}},
|
||||
Alias: &parser.Ident{Name: "tbl"},
|
||||
},
|
||||
}, `SELECT * FROM (SELECT *) AS "tbl"`)
|
||||
}, `SELECT * FROM (SELECT *) AS tbl`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Columns: []*parser.ResultColumn{{Star: pos(0)}},
|
||||
|
|
@ -753,7 +737,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, `SELECT * FROM "tbl" WINDOW "win1" AS ("base" PARTITION BY "x", "y" ORDER BY "x" ASC NULLS FIRST, "y" DESC NULLS LAST RANGE UNBOUNDED PRECEDING), "win2" AS ("base2")`)
|
||||
}, `SELECT * FROM tbl WINDOW win1 AS (base PARTITION BY x, y ORDER BY x ASC NULLS FIRST, y DESC NULLS LAST RANGE UNBOUNDED PRECEDING), win2 AS (base2)`)
|
||||
|
||||
// AssertStatementStringer(t, &sql.SelectStatement{
|
||||
// WithClause: &sql.WithClause{
|
||||
|
|
@ -813,7 +797,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
{X: &parser.Ident{Name: "x"}},
|
||||
{X: &parser.Ident{Name: "y"}},
|
||||
},
|
||||
}, `SELECT * ORDER BY "x", "y"`)
|
||||
}, `SELECT * ORDER BY x, y`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Columns: []*parser.ResultColumn{{Star: pos(0)}},
|
||||
|
|
@ -822,7 +806,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
Operator: &parser.JoinOperator{Comma: pos(0)},
|
||||
Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}},
|
||||
},
|
||||
}, `SELECT * FROM "x", "y"`)
|
||||
}, `SELECT * FROM x, y`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Columns: []*parser.ResultColumn{{Star: pos(0)}},
|
||||
|
|
@ -832,7 +816,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}},
|
||||
Constraint: &parser.OnConstraint{X: &parser.BoolLit{Value: true}},
|
||||
},
|
||||
}, `SELECT * FROM "x" JOIN "y" ON TRUE`)
|
||||
}, `SELECT * FROM x JOIN y ON TRUE`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Columns: []*parser.ResultColumn{{Star: pos(0)}},
|
||||
|
|
@ -844,7 +828,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
Columns: []*parser.Ident{{Name: "a"}, {Name: "b"}},
|
||||
},
|
||||
},
|
||||
}, `SELECT * FROM "x" NATURAL INNER JOIN "y" USING ("a", "b")`)
|
||||
}, `SELECT * FROM x NATURAL INNER JOIN y USING (a, b)`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Columns: []*parser.ResultColumn{{Star: pos(0)}},
|
||||
|
|
@ -853,7 +837,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
Operator: &parser.JoinOperator{Left: pos(0)},
|
||||
Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}},
|
||||
},
|
||||
}, `SELECT * FROM "x" LEFT JOIN "y"`)
|
||||
}, `SELECT * FROM x LEFT JOIN y`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Columns: []*parser.ResultColumn{{Star: pos(0)}},
|
||||
|
|
@ -862,7 +846,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
Operator: &parser.JoinOperator{Left: pos(0), Outer: pos(0)},
|
||||
Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}},
|
||||
},
|
||||
}, `SELECT * FROM "x" LEFT OUTER JOIN "y"`)
|
||||
}, `SELECT * FROM x LEFT OUTER JOIN y`)
|
||||
|
||||
AssertStatementStringer(t, &parser.SelectStatement{
|
||||
Columns: []*parser.ResultColumn{{Star: pos(0)}},
|
||||
|
|
@ -871,7 +855,7 @@ func TestSelectStatement_String(t *testing.T) {
|
|||
Operator: &parser.JoinOperator{Cross: pos(0)},
|
||||
Y: &parser.QualifiedTableName{Name: &parser.Ident{Name: "y"}},
|
||||
},
|
||||
}, `SELECT * FROM "x" CROSS JOIN "y"`)
|
||||
}, `SELECT * FROM x CROSS JOIN y`)
|
||||
}
|
||||
|
||||
func TestUpdateStatement_String(t *testing.T) {
|
||||
|
|
@ -882,7 +866,7 @@ func TestUpdateStatement_String(t *testing.T) {
|
|||
{Columns: []*parser.Ident{{Name: "y"}}, Expr: &parser.IntegerLit{Value: "200"}},
|
||||
},
|
||||
WhereExpr: &parser.BoolLit{Value: true},
|
||||
}, `UPDATE "tbl" SET "x" = 100, "y" = 200 WHERE TRUE`)
|
||||
}, `UPDATE tbl SET x = 100, y = 200 WHERE TRUE`)
|
||||
|
||||
AssertStatementStringer(t, &parser.UpdateStatement{
|
||||
UpdateOrRollback: pos(0),
|
||||
|
|
@ -890,7 +874,7 @@ func TestUpdateStatement_String(t *testing.T) {
|
|||
Assignments: []*parser.Assignment{
|
||||
{Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}},
|
||||
},
|
||||
}, `UPDATE OR ROLLBACK "tbl" SET "x" = 100`)
|
||||
}, `UPDATE OR ROLLBACK tbl SET x = 100`)
|
||||
|
||||
AssertStatementStringer(t, &parser.UpdateStatement{
|
||||
UpdateOrAbort: pos(0),
|
||||
|
|
@ -898,7 +882,7 @@ func TestUpdateStatement_String(t *testing.T) {
|
|||
Assignments: []*parser.Assignment{
|
||||
{Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}},
|
||||
},
|
||||
}, `UPDATE OR ABORT "tbl" SET "x" = 100`)
|
||||
}, `UPDATE OR ABORT tbl SET x = 100`)
|
||||
|
||||
AssertStatementStringer(t, &parser.UpdateStatement{
|
||||
UpdateOrReplace: pos(0),
|
||||
|
|
@ -906,7 +890,7 @@ func TestUpdateStatement_String(t *testing.T) {
|
|||
Assignments: []*parser.Assignment{
|
||||
{Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}},
|
||||
},
|
||||
}, `UPDATE OR REPLACE "tbl" SET "x" = 100`)
|
||||
}, `UPDATE OR REPLACE tbl SET x = 100`)
|
||||
|
||||
AssertStatementStringer(t, &parser.UpdateStatement{
|
||||
UpdateOrFail: pos(0),
|
||||
|
|
@ -914,7 +898,7 @@ func TestUpdateStatement_String(t *testing.T) {
|
|||
Assignments: []*parser.Assignment{
|
||||
{Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}},
|
||||
},
|
||||
}, `UPDATE OR FAIL "tbl" SET "x" = 100`)
|
||||
}, `UPDATE OR FAIL tbl SET x = 100`)
|
||||
|
||||
AssertStatementStringer(t, &parser.UpdateStatement{
|
||||
UpdateOrIgnore: pos(0),
|
||||
|
|
@ -922,7 +906,7 @@ func TestUpdateStatement_String(t *testing.T) {
|
|||
Assignments: []*parser.Assignment{
|
||||
{Columns: []*parser.Ident{{Name: "x"}}, Expr: &parser.IntegerLit{Value: "100"}},
|
||||
},
|
||||
}, `UPDATE OR IGNORE "tbl" SET "x" = 100`)
|
||||
}, `UPDATE OR IGNORE tbl SET x = 100`)
|
||||
|
||||
// AssertStatementStringer(t, &sql.UpdateStatement{
|
||||
// WithClause: &sql.WithClause{
|
||||
|
|
@ -941,8 +925,8 @@ func TestUpdateStatement_String(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIdent_String(t *testing.T) {
|
||||
AssertExprStringer(t, &parser.Ident{Name: "foo"}, `"foo"`)
|
||||
AssertExprStringer(t, &parser.Ident{Name: "foo \" bar"}, `"foo "" bar"`)
|
||||
AssertExprStringer(t, &parser.Ident{Name: "foo", Quoted: true}, `"foo"`)
|
||||
AssertExprStringer(t, &parser.Ident{Name: "foo \" bar", Quoted: true}, `"foo "" bar"`)
|
||||
}
|
||||
|
||||
func TestStringLit_String(t *testing.T) {
|
||||
|
|
@ -1021,7 +1005,7 @@ func TestCaseExpr_String(t *testing.T) {
|
|||
{Condition: &parser.IntegerLit{Value: "2"}, Body: &parser.BoolLit{Value: false}},
|
||||
},
|
||||
ElseExpr: &parser.NullLit{},
|
||||
}, `CASE "foo" WHEN 1 THEN TRUE WHEN 2 THEN FALSE ELSE NULL END`)
|
||||
}, `CASE foo WHEN 1 THEN TRUE WHEN 2 THEN FALSE ELSE NULL END`)
|
||||
|
||||
AssertExprStringer(t, &parser.CaseExpr{
|
||||
Blocks: []*parser.CaseBlock{
|
||||
|
|
@ -1036,8 +1020,8 @@ func TestExprList_String(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestQualifiedRef_String(t *testing.T) {
|
||||
AssertExprStringer(t, &parser.QualifiedRef{Table: &parser.Ident{Name: "tbl"}, Column: &parser.Ident{Name: "col"}}, `"tbl"."col"`)
|
||||
AssertExprStringer(t, &parser.QualifiedRef{Table: &parser.Ident{Name: "tbl"}, Star: pos(0)}, `"tbl".*`)
|
||||
AssertExprStringer(t, &parser.QualifiedRef{Table: &parser.Ident{Name: "tbl"}, Column: &parser.Ident{Name: "col"}}, `tbl.col`)
|
||||
AssertExprStringer(t, &parser.QualifiedRef{Table: &parser.Ident{Name: "tbl"}, Star: pos(0)}, `tbl.*`)
|
||||
}
|
||||
|
||||
func TestCall_String(t *testing.T) {
|
||||
|
|
@ -1065,7 +1049,7 @@ func TestCall_String(t *testing.T) {
|
|||
Over: &parser.OverClause{
|
||||
Name: &parser.Ident{Name: "win"},
|
||||
},
|
||||
}, `foo() OVER "win"`)
|
||||
}, `foo() OVER win`)
|
||||
|
||||
t.Run("FrameSpec", func(t *testing.T) {
|
||||
AssertExprStringer(t, &parser.Call{
|
||||
|
|
|
|||
|
|
@ -2,11 +2,7 @@ package parser
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/featurebasedb/featurebase/v3/pql"
|
||||
)
|
||||
|
||||
// TODO(pok) make all these lower case
|
||||
|
|
@ -360,25 +356,6 @@ func (*DataTypeTimestamp) TypeInfo() map[string]interface{} {
|
|||
return nil
|
||||
}
|
||||
|
||||
func StringToDecimal(v string) (pql.Decimal, error) {
|
||||
fvalue, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return pql.NewDecimal(0, 0), err
|
||||
}
|
||||
return FloatToDecimal(fvalue), nil
|
||||
}
|
||||
|
||||
func FloatToDecimal(v float64) pql.Decimal {
|
||||
scale := NumDecimalPlaces(fmt.Sprintf("%v", v))
|
||||
unscaledValue := int64(v * math.Pow(10, float64(scale)))
|
||||
return pql.NewDecimal(unscaledValue, int64(scale))
|
||||
}
|
||||
|
||||
func FloatToDecimalWithScale(v float64, s int64) pql.Decimal {
|
||||
unscaledValue := int64(v * math.Pow(10, float64(s)))
|
||||
return pql.NewDecimal(unscaledValue, int64(s))
|
||||
}
|
||||
|
||||
func NumDecimalPlaces(v string) int {
|
||||
i := strings.IndexByte(v, '.')
|
||||
if i > -1 {
|
||||
|
|
|
|||
|
|
@ -300,9 +300,9 @@ func (p *Parser) parseCreateStatement() (Statement, error) {
|
|||
/* case VIEW:
|
||||
return p.parseCreateViewStatement(pos)
|
||||
case INDEX, UNIQUE:
|
||||
return p.parseCreateIndexStatement(pos)
|
||||
case TRIGGER:
|
||||
return p.parseCreateTriggerStatement(pos)*/
|
||||
return p.parseCreateIndexStatement(pos)*/
|
||||
case FUNCTION:
|
||||
return p.parseCreateFunctionStatement(pos)
|
||||
default:
|
||||
return nil, p.errorExpected(pos, tok, "TABLE")
|
||||
}
|
||||
|
|
@ -318,9 +318,9 @@ func (p *Parser) parseDropStatement() (Statement, error) {
|
|||
/* case VIEW:
|
||||
return p.parseDropViewStatement(pos)
|
||||
case INDEX:
|
||||
return p.parseDropIndexStatement(pos)
|
||||
case TRIGGER:
|
||||
return p.parseDropTriggerStatement(pos)*/
|
||||
return p.parseDropIndexStatement(pos)*/
|
||||
case FUNCTION:
|
||||
return p.parseDropFunctionStatement(pos)
|
||||
default:
|
||||
return nil, p.errorExpected(pos, tok, "TABLE")
|
||||
}
|
||||
|
|
@ -1173,12 +1173,43 @@ func (p *Parser) parseDropTableStatement(dropPos Pos) (_ *DropTableStatement, er
|
|||
return &stmt, nil
|
||||
}*/
|
||||
|
||||
/*func (p *Parser) parseCreateTriggerStatement(createPos Pos) (_ *CreateTriggerStatement, err error) {
|
||||
assert(p.peek() == TRIGGER)
|
||||
func (p *Parser) parseParameterDefinitions() (_ []*ParameterDefinition, err error) {
|
||||
var params []*ParameterDefinition
|
||||
for {
|
||||
switch {
|
||||
case p.peek() == VARIABLE:
|
||||
col, err := p.parseParameterDefinition()
|
||||
params = append(params, col)
|
||||
if err != nil {
|
||||
return params, err
|
||||
}
|
||||
if p.peek() == COMMA {
|
||||
p.scan()
|
||||
}
|
||||
case p.peek() == RP:
|
||||
return params, nil
|
||||
default:
|
||||
return params, p.errorExpected(p.pos, p.tok, "parameter name, or right paren")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stmt CreateTriggerStatement
|
||||
func (p *Parser) parseParameterDefinition() (_ *ParameterDefinition, err error) {
|
||||
var param ParameterDefinition
|
||||
if param.Name, err = p.parseVariable("parameter name"); err != nil {
|
||||
return ¶m, err
|
||||
} else if param.Type, err = p.parseType(); err != nil {
|
||||
return ¶m, err
|
||||
}
|
||||
return ¶m, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseCreateFunctionStatement(createPos Pos) (_ *CreateFunctionStatement, err error) {
|
||||
assert(p.peek() == FUNCTION)
|
||||
|
||||
var stmt CreateFunctionStatement
|
||||
stmt.Create = createPos
|
||||
stmt.Trigger, _, _ = p.scan()
|
||||
stmt.Function, _, _ = p.scan()
|
||||
|
||||
// Parse optional "IF NOT EXISTS".
|
||||
if p.peek() == IF {
|
||||
|
|
@ -1195,135 +1226,83 @@ func (p *Parser) parseDropTableStatement(dropPos Pos) (_ *DropTableStatement, er
|
|||
stmt.IfNotExists, _, _ = p.scan()
|
||||
}
|
||||
|
||||
if stmt.Name, err = p.parseIdent("index name"); err != nil {
|
||||
if stmt.Name, err = p.parseIdent("function name"); err != nil {
|
||||
return &stmt, err
|
||||
}
|
||||
|
||||
// Parse BEFORE, AFTER, or INSTEAD OF
|
||||
switch p.peek() {
|
||||
case BEFORE:
|
||||
stmt.Before, _, _ = p.scan()
|
||||
case AFTER:
|
||||
stmt.After, _, _ = p.scan()
|
||||
case INSTEAD:
|
||||
stmt.Instead, _, _ = p.scan()
|
||||
if p.peek() != OF {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "OF")
|
||||
}
|
||||
stmt.InsteadOf, _, _ = p.scan()
|
||||
}
|
||||
// parameters
|
||||
if p.peek() == LP {
|
||||
stmt.Lparen, _, _ = p.scan()
|
||||
|
||||
// Parse DELETE, INSERT, UPDATE, or UPDATE OF [columns]
|
||||
switch p.peek() {
|
||||
case DELETE:
|
||||
stmt.Delete, _, _ = p.scan()
|
||||
case INSERT:
|
||||
stmt.Insert, _, _ = p.scan()
|
||||
case UPDATE:
|
||||
stmt.Update, _, _ = p.scan()
|
||||
if p.peek() == OF {
|
||||
stmt.UpdateOf, _, _ = p.scan()
|
||||
for {
|
||||
col, err := p.parseIdent("column name")
|
||||
if err != nil {
|
||||
return &stmt, err
|
||||
}
|
||||
stmt.UpdateOfColumns = append(stmt.UpdateOfColumns, col)
|
||||
|
||||
if p.peek() != COMMA {
|
||||
break
|
||||
}
|
||||
p.scan()
|
||||
}
|
||||
}
|
||||
default:
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "DELETE, INSERT, or UPDATE")
|
||||
}
|
||||
|
||||
// Parse "ON table-name".
|
||||
if p.peek() != ON {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "ON")
|
||||
}
|
||||
stmt.On, _, _ = p.scan()
|
||||
if stmt.Table, err = p.parseIdent("table name"); err != nil {
|
||||
return &stmt, err
|
||||
}
|
||||
|
||||
// Parse optional "FOR EACH ROW".
|
||||
if p.peek() == FOR {
|
||||
stmt.For, _, _ = p.scan()
|
||||
if p.peek() != EACH {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "EACH")
|
||||
}
|
||||
stmt.ForEach, _, _ = p.scan()
|
||||
if p.peek() != ROW {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "ROW")
|
||||
}
|
||||
stmt.ForEachRow, _, _ = p.scan()
|
||||
}
|
||||
|
||||
// Parse optional "WHEN expr".
|
||||
if p.peek() == WHEN {
|
||||
stmt.When, _, _ = p.scan()
|
||||
if stmt.WhenExpr, err = p.ParseExpr(); err != nil {
|
||||
stmt.Parameters, err = p.parseParameterDefinitions()
|
||||
if err != nil {
|
||||
return &stmt, err
|
||||
}
|
||||
|
||||
if p.peek() != RP {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, ")")
|
||||
}
|
||||
stmt.Rparen, _, _ = p.scan()
|
||||
}
|
||||
|
||||
// Parse trigger body.
|
||||
if p.peek() != RETURNS {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "RETURNS")
|
||||
}
|
||||
stmt.Returns, _, _ = p.scan()
|
||||
stmt.ReturnDef, err = p.parseParameterDefinition()
|
||||
if err != nil {
|
||||
return &stmt, err
|
||||
}
|
||||
|
||||
if p.peek() != AS {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "AS")
|
||||
}
|
||||
stmt.As, _, _ = p.scan()
|
||||
|
||||
if p.peek() != BEGIN {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "BEGIN")
|
||||
}
|
||||
stmt.Begin, _, _ = p.scan()
|
||||
|
||||
for {
|
||||
s, err := p.parseTriggerBodyStatement()
|
||||
s, err := p.parseFunctionBodyStatement()
|
||||
if err != nil {
|
||||
return &stmt, err
|
||||
}
|
||||
stmt.Body = append(stmt.Body, s)
|
||||
if s != nil {
|
||||
stmt.Body = append(stmt.Body, s)
|
||||
}
|
||||
|
||||
if p.peek() == END {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if p.peek() != END {
|
||||
return &stmt, p.errorExpected(p.pos, p.tok, "END")
|
||||
}
|
||||
stmt.End, _, _ = p.scan()
|
||||
|
||||
return &stmt, nil
|
||||
}*/
|
||||
}
|
||||
|
||||
/*func (p *Parser) parseTriggerBodyStatement() (stmt Statement, err error) {
|
||||
func (p *Parser) parseFunctionBodyStatement() (stmt Statement, err error) {
|
||||
switch p.peek() {
|
||||
case SELECT, VALUES:
|
||||
stmt, err = p.parseSelectStatement(false, nil)
|
||||
case INSERT, REPLACE:
|
||||
stmt, err = p.parseInsertStatement(nil)
|
||||
case UPDATE:
|
||||
stmt, err = p.parseUpdateStatement(nil)
|
||||
case DELETE:
|
||||
stmt, err = p.parseDeleteStatement(nil)
|
||||
//case WITH:
|
||||
// stmt, err = p.parseWithStatement()
|
||||
case END:
|
||||
break
|
||||
default:
|
||||
return nil, p.errorExpected(p.pos, p.tok, "statement")
|
||||
}
|
||||
if err != nil {
|
||||
return stmt, err
|
||||
}
|
||||
|
||||
// Ensure trailing semicolon exists.
|
||||
if p.peek() != SEMI {
|
||||
return stmt, p.errorExpected(p.pos, p.tok, "semicolon")
|
||||
}
|
||||
p.scan()
|
||||
|
||||
return stmt, nil
|
||||
}*/
|
||||
}
|
||||
|
||||
/*func (p *Parser) parseDropTriggerStatement(dropPos Pos) (_ *DropTriggerStatement, err error) {
|
||||
assert(p.peek() == TRIGGER)
|
||||
func (p *Parser) parseDropFunctionStatement(dropPos Pos) (_ *DropFunctionStatement, err error) {
|
||||
assert(p.peek() == FUNCTION)
|
||||
|
||||
var stmt DropTriggerStatement
|
||||
var stmt DropFunctionStatement
|
||||
stmt.Drop = dropPos
|
||||
stmt.Trigger, _, _ = p.scan()
|
||||
|
||||
|
|
@ -1336,12 +1315,12 @@ func (p *Parser) parseDropTableStatement(dropPos Pos) (_ *DropTableStatement, er
|
|||
stmt.IfExists, _, _ = p.scan()
|
||||
}
|
||||
|
||||
if stmt.Name, err = p.parseIdent("trigger name"); err != nil {
|
||||
if stmt.Name, err = p.parseIdent("function name"); err != nil {
|
||||
return &stmt, err
|
||||
}
|
||||
|
||||
return &stmt, nil
|
||||
}*/
|
||||
}
|
||||
|
||||
func (p *Parser) parseIdent(desc string) (*Ident, error) {
|
||||
pos, tok, lit := p.scan()
|
||||
|
|
@ -1353,6 +1332,19 @@ func (p *Parser) parseIdent(desc string) (*Ident, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseVariable(desc string) (*Variable, error) {
|
||||
pos, tok, lit := p.scan()
|
||||
switch tok {
|
||||
case VARIABLE:
|
||||
return &Variable{
|
||||
Name: lit,
|
||||
NamePos: pos,
|
||||
}, nil
|
||||
default:
|
||||
return nil, p.errorExpected(pos, tok, desc)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseType() (_ *Type, err error) {
|
||||
var typ Type
|
||||
if typ.Name, err = p.parseIdent("type name"); err != nil {
|
||||
|
|
@ -2567,7 +2559,7 @@ func (p *Parser) parseOperand() (expr Expr, err error) {
|
|||
}
|
||||
return ident, nil
|
||||
case VARIABLE:
|
||||
return &VariableRef{Name: lit, NamePos: pos}, nil
|
||||
return &Variable{Name: lit, NamePos: pos}, nil
|
||||
case MIN, MAX:
|
||||
ident := &Ident{Name: lit, NamePos: pos, Quoted: tok == QIDENT}
|
||||
return p.parseCall(ident)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -205,6 +205,8 @@ const (
|
|||
RENAME
|
||||
REPLACE
|
||||
RESTRICT
|
||||
RETURNS
|
||||
RETURN
|
||||
ROLLBACK
|
||||
ROW
|
||||
ROWS
|
||||
|
|
@ -426,6 +428,8 @@ var tokens = [...]string{
|
|||
RENAME: "RENAME",
|
||||
REPLACE: "REPLACE",
|
||||
RESTRICT: "RESTRICT",
|
||||
RETURNS: "RETURNS",
|
||||
RETURN: "RETURN",
|
||||
ROLLBACK: "ROLLBACK",
|
||||
ROW: "ROW",
|
||||
ROWS: "ROWS",
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ func walk(v Visitor, node Node) (_ Node, err error) {
|
|||
return node, err
|
||||
}
|
||||
|
||||
case *DropTriggerStatement:
|
||||
case *DropFunctionStatement:
|
||||
if err := walkIdent(v, &n.Name); err != nil {
|
||||
return node, err
|
||||
}
|
||||
|
|
@ -161,19 +161,13 @@ func walk(v Visitor, node Node) (_ Node, err error) {
|
|||
return node, err
|
||||
}
|
||||
|
||||
case *CreateTriggerStatement:
|
||||
case *CreateFunctionStatement:
|
||||
if err := walkIdent(v, &n.Name); err != nil {
|
||||
return node, err
|
||||
}
|
||||
if err := walkIdentList(v, n.UpdateOfColumns); err != nil {
|
||||
return node, err
|
||||
}
|
||||
if err := walkIdent(v, &n.Table); err != nil {
|
||||
return node, err
|
||||
}
|
||||
if err := walkExpr(v, &n.WhenExpr); err != nil {
|
||||
return node, err
|
||||
}
|
||||
// if err := walkIdentList(v, n.UpdateOfColumns); err != nil {
|
||||
// return node, err
|
||||
// }
|
||||
for i := range n.Body {
|
||||
if body, err := walk(v, n.Body[i]); err != nil {
|
||||
return node, err
|
||||
|
|
|
|||
|
|
@ -1808,13 +1808,7 @@ func newFloatLiteralPlanExpression(value string) *floatLiteralPlanExpression {
|
|||
}
|
||||
|
||||
func (n *floatLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
||||
scale := parser.NumDecimalPlaces(n.value)
|
||||
fvalue, err := strconv.ParseFloat(n.value, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unscaledValue := int64(fvalue * math.Pow(10, float64(scale)))
|
||||
return pql.NewDecimal(unscaledValue, int64(scale)), nil
|
||||
return pql.ParseDecimal(n.value)
|
||||
}
|
||||
|
||||
func (n *floatLiteralPlanExpression) Type() parser.ExprDataType {
|
||||
|
|
@ -2081,14 +2075,11 @@ func (n *castPlanExpression) Evaluate(currentRow []interface{}) (interface{}, er
|
|||
return i, nil
|
||||
|
||||
case *parser.DataTypeDecimal:
|
||||
fvalue, err := strconv.ParseFloat(nl, 64)
|
||||
castValue, err := pql.ParseDecimal(nl)
|
||||
if err != nil {
|
||||
//TODO(pok) need to push location into here
|
||||
return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription())
|
||||
}
|
||||
scale := parser.NumDecimalPlaces(nl)
|
||||
unscaledValue := int64(fvalue * math.Pow(10, float64(scale)))
|
||||
castValue := pql.NewDecimal(unscaledValue, int64(scale))
|
||||
if tt.Scale < castValue.Scale {
|
||||
return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription())
|
||||
}
|
||||
|
|
@ -2500,7 +2491,7 @@ func (p *ExecutionPlanner) compileExpr(expr parser.Expr) (_ types.PlanExpression
|
|||
case *parser.ParenExpr:
|
||||
return p.compileExpr(expr.X)
|
||||
|
||||
case *parser.VariableRef:
|
||||
case *parser.Variable:
|
||||
ref := newVariableRefPlanExpression(expr.Name, expr.VariableIndex, expr.DataType())
|
||||
return ref, nil
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ package planner
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/featurebasedb/featurebase/v3/pql"
|
||||
"github.com/featurebasedb/featurebase/v3/sql3"
|
||||
|
|
@ -346,19 +345,17 @@ func (n *sumPlanExpression) WithChildren(children ...types.PlanExpression) (type
|
|||
return newSumPlanExpression(children[0], n.returnDataType), nil
|
||||
}
|
||||
|
||||
// aggregator for AVG
|
||||
// aggregator for AVG()
|
||||
type aggregateAvg struct {
|
||||
sum float64
|
||||
sum interface{}
|
||||
rows int64
|
||||
expr types.PlanExpression
|
||||
}
|
||||
|
||||
func NewAggAvgBuffer(child types.PlanExpression) *aggregateAvg {
|
||||
const (
|
||||
sum = float64(0)
|
||||
rows = int64(0)
|
||||
)
|
||||
return &aggregateAvg{sum, rows, child}
|
||||
return &aggregateAvg{
|
||||
expr: child,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregateAvg) Update(ctx context.Context, row types.Row) error {
|
||||
|
|
@ -370,24 +367,87 @@ func (a *aggregateAvg) Update(ctx context.Context, row types.Row) error {
|
|||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
a.sum += v.(float64)
|
||||
|
||||
aggExpr, ok := a.expr.(*avgPlanExpression)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected aggregate expression type '%T'", a.expr)
|
||||
}
|
||||
|
||||
// we're going to do the sum in the return type
|
||||
switch returnType := aggExpr.returnDataType.(type) {
|
||||
case *parser.DataTypeDecimal:
|
||||
|
||||
// get the current agg value
|
||||
var ok bool
|
||||
var aggVal pql.Decimal
|
||||
if a.sum == nil {
|
||||
aggVal = pql.NewDecimal(0, returnType.Scale)
|
||||
} else {
|
||||
aggVal, ok = a.sum.(pql.Decimal)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
}
|
||||
|
||||
switch dataType := aggExpr.arg.Type().(type) {
|
||||
case *parser.DataTypeDecimal:
|
||||
thisVal, ok := v.(pql.Decimal)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
|
||||
a.sum = pql.AddDecimal(thisVal, aggVal)
|
||||
|
||||
case *parser.DataTypeInt:
|
||||
thisIVal, ok := v.(int64)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
|
||||
thisVal := pql.FromInt64(thisIVal, returnType.Scale)
|
||||
a.sum = pql.AddDecimal(thisVal, aggVal)
|
||||
|
||||
default:
|
||||
return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", dataType)
|
||||
}
|
||||
default:
|
||||
return sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", returnType)
|
||||
}
|
||||
a.rows += 1
|
||||
|
||||
//return nil
|
||||
return sql3.NewErrInternalf("implement me")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aggregateAvg) Eval(ctx context.Context) (interface{}, error) {
|
||||
// This case is triggered when no rows exist.
|
||||
if a.sum == 0 && a.rows == 0 {
|
||||
// bail if we have no aggregate at all
|
||||
if a.sum == nil && a.rows == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if a.rows == 0 {
|
||||
return float64(0), nil
|
||||
aggExpr, ok := a.expr.(*avgPlanExpression)
|
||||
if !ok {
|
||||
return nil, sql3.NewErrInternalf("unexpected aggregate expression type '%T'", a.expr)
|
||||
}
|
||||
|
||||
switch returnType := aggExpr.returnDataType.(type) {
|
||||
case *parser.DataTypeDecimal:
|
||||
|
||||
// if no rows, average is 0
|
||||
if a.rows == 0 {
|
||||
return pql.NewDecimal(0, returnType.Scale), nil
|
||||
}
|
||||
count := pql.FromInt64(a.rows, returnType.Scale)
|
||||
|
||||
sum, ok := a.sum.(pql.Decimal)
|
||||
if !ok {
|
||||
return nil, sql3.NewErrInternalf("unexpected type conversion '%T'", a.sum)
|
||||
}
|
||||
return pql.DivideDecimal(sum, count), nil
|
||||
|
||||
default:
|
||||
return nil, sql3.NewErrInternalf("unhandled aggregate expression datatype '%T'", returnType)
|
||||
}
|
||||
|
||||
return a.sum / float64(a.rows), nil
|
||||
}
|
||||
|
||||
// avgPlanExpression handles AVG()
|
||||
|
|
@ -458,37 +518,76 @@ func (n *avgPlanExpression) WithChildren(children ...types.PlanExpression) (type
|
|||
return newAvgPlanExpression(children[0], n.returnDataType), nil
|
||||
}
|
||||
|
||||
// aggregator for MIN
|
||||
type aggreagateMin struct {
|
||||
// aggregator for MIN()
|
||||
type aggregateMin struct {
|
||||
val interface{}
|
||||
expr types.PlanExpression
|
||||
}
|
||||
|
||||
func NewAggMinBuffer(child types.PlanExpression) *aggreagateMin {
|
||||
return &aggreagateMin{nil, child}
|
||||
func NewAggMinBuffer(child types.PlanExpression) *aggregateMin {
|
||||
return &aggregateMin{nil, child}
|
||||
}
|
||||
|
||||
func (m *aggreagateMin) Update(ctx context.Context, row types.Row) error {
|
||||
func (m *aggregateMin) Update(ctx context.Context, row types.Row) error {
|
||||
v, err := m.expr.Evaluate(row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reflect.TypeOf(v) == nil {
|
||||
// skip if nil
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if we have no min, then set the value
|
||||
if m.val == nil {
|
||||
m.val = v
|
||||
return nil
|
||||
}
|
||||
|
||||
//return nil
|
||||
return sql3.NewErrInternalf("implement me")
|
||||
aggExpr, ok := m.expr.(*minPlanExpression)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected aggregate expression type '%T'", m.expr)
|
||||
}
|
||||
|
||||
switch dataType := aggExpr.arg.Type().(type) {
|
||||
case *parser.DataTypeDecimal:
|
||||
thisVal, ok := v.(pql.Decimal)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
|
||||
aggVal, ok := m.val.(pql.Decimal)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
|
||||
if thisVal.LessThan(aggVal) {
|
||||
m.val = thisVal
|
||||
}
|
||||
|
||||
case *parser.DataTypeInt:
|
||||
thisVal, ok := v.(int64)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
|
||||
aggVal, ok := m.val.(int64)
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *aggreagateMin) Eval(ctx context.Context) (interface{}, error) {
|
||||
func (m *aggregateMin) Eval(ctx context.Context) (interface{}, error) {
|
||||
return m.val, nil
|
||||
}
|
||||
|
||||
|
|
@ -560,7 +659,7 @@ func (n *minPlanExpression) WithChildren(children ...types.PlanExpression) (type
|
|||
return newMinPlanExpression(children[0], n.returnDataType), nil
|
||||
}
|
||||
|
||||
// aggregator for MAX
|
||||
// aggregator for MAX()
|
||||
type aggregateMax struct {
|
||||
val interface{}
|
||||
expr types.PlanExpression
|
||||
|
|
@ -576,18 +675,58 @@ func (m *aggregateMax) Update(ctx context.Context, row types.Row) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if reflect.TypeOf(v) == nil {
|
||||
// skip if nil
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if we have no min, then set the value
|
||||
if m.val == nil {
|
||||
m.val = v
|
||||
return nil
|
||||
}
|
||||
|
||||
//return nil
|
||||
return sql3.NewErrInternalf("implement me")
|
||||
aggExpr, ok := m.expr.(*maxPlanExpression)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected aggregate expression type '%T'", m.expr)
|
||||
}
|
||||
|
||||
switch dataType := aggExpr.arg.Type().(type) {
|
||||
case *parser.DataTypeDecimal:
|
||||
thisVal, ok := v.(pql.Decimal)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
|
||||
aggVal, ok := m.val.(pql.Decimal)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
|
||||
if thisVal.GreaterThan(aggVal) {
|
||||
m.val = thisVal
|
||||
}
|
||||
|
||||
case *parser.DataTypeInt:
|
||||
thisVal, ok := v.(int64)
|
||||
if !ok {
|
||||
return sql3.NewErrInternalf("unexpected type conversion '%T'", v)
|
||||
}
|
||||
|
||||
aggVal, ok := m.val.(int64)
|
||||
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)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *aggregateMax) Eval(ctx context.Context) (interface{}, error) {
|
||||
|
|
@ -736,7 +875,7 @@ func (n *percentilePlanExpression) WithChildren(children ...types.PlanExpression
|
|||
return newPercentilePlanExpression(children[0], children[1], n.returnDataType), nil
|
||||
}
|
||||
|
||||
// aggregator for last
|
||||
// aggregator for LAST()
|
||||
type aggregateLast struct {
|
||||
val interface{}
|
||||
expr types.PlanExpression
|
||||
|
|
@ -764,7 +903,7 @@ func (l *aggregateLast) Eval(ctx context.Context) (interface{}, error) {
|
|||
return l.val, nil
|
||||
}
|
||||
|
||||
// aggregator for first
|
||||
// aggregator for FIRST()
|
||||
type aggregateFirst struct {
|
||||
val interface{}
|
||||
expr types.PlanExpression
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func (p *ExecutionPlanner) analyzeExpression(expr parser.Expr, scope parser.Stat
|
|||
return nil, sql3.NewErrInternalf("unhandled scope type '%T'", sc)
|
||||
}
|
||||
|
||||
case *parser.VariableRef:
|
||||
case *parser.Variable:
|
||||
switch sc := scope.(type) {
|
||||
case *parser.BulkInsertStatement:
|
||||
// get the name of the variable without the @
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ func (i *bulkInsertSourceCSVRowIter) Next(ctx context.Context) (types.Row, error
|
|||
result[idx] = bval
|
||||
|
||||
case *parser.DataTypeDecimal:
|
||||
dval, err := parser.StringToDecimal(evalValue)
|
||||
dval, err := pql.ParseDecimal(evalValue)
|
||||
if err != nil {
|
||||
return nil, sql3.NewErrTypeConversionOnMap(0, 0, evalValue, mapColumn.colType.TypeName())
|
||||
}
|
||||
|
|
@ -658,7 +658,7 @@ func (i *bulkInsertSourceNDJsonRowIter) Next(ctx context.Context) (types.Row, er
|
|||
case *parser.DataTypeDecimal:
|
||||
switch v := evalValue.(type) {
|
||||
case float64:
|
||||
result[idx] = parser.FloatToDecimal(v)
|
||||
result[idx] = pql.FromFloat64(v)
|
||||
|
||||
case []interface{}:
|
||||
return nil, sql3.NewErrTypeConversionOnMap(0, 0, v, mapColumn.colType.TypeName())
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ func newQueryIterator(requests pilosa.ExecutionRequestsAPI, query *PlanOpQuery,
|
|||
|
||||
func (i *queryIterator) Next(ctx context.Context) (types.Row, error) {
|
||||
if i.hasStarted == nil {
|
||||
i.hasStarted = &struct{}{}
|
||||
|
||||
requestId, ok := fbcontext.RequestID(ctx)
|
||||
if !ok {
|
||||
|
|
@ -134,6 +133,7 @@ func (i *queryIterator) Next(ctx context.Context) (types.Row, error) {
|
|||
userId, _ = fbcontext.UserID(ctx)
|
||||
|
||||
i.requests.AddRequest(requestId, userId, time.Now(), i.query.sql)
|
||||
i.hasStarted = &struct{}{}
|
||||
}
|
||||
|
||||
row, err := i.child.Next(ctx)
|
||||
|
|
|
|||
|
|
@ -30,8 +30,11 @@ func (p *PlanOpTop) Schema() types.Schema {
|
|||
}
|
||||
|
||||
func (p *PlanOpTop) Iterator(ctx context.Context, row types.Row) (types.RowIterator, error) {
|
||||
// TODO (pok) actually implement top
|
||||
return p.ChildOp.Iterator(ctx, row)
|
||||
iter, err := p.ChildOp.Iterator(ctx, row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newTopIter(p.expr, iter), nil
|
||||
}
|
||||
|
||||
func (p *PlanOpTop) Children() []types.PlanOperator {
|
||||
|
|
@ -75,3 +78,47 @@ func (p *PlanOpTop) Warnings() []string {
|
|||
w = append(w, p.ChildOp.Warnings()...)
|
||||
return w
|
||||
}
|
||||
|
||||
type topIter struct {
|
||||
child types.RowIterator
|
||||
expr types.PlanExpression
|
||||
|
||||
rowCount int64
|
||||
topValue int64
|
||||
|
||||
hasStarted *struct{}
|
||||
}
|
||||
|
||||
func newTopIter(expr types.PlanExpression, child types.RowIterator) *topIter {
|
||||
return &topIter{
|
||||
child: child,
|
||||
expr: expr,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *topIter) Next(ctx context.Context) (types.Row, error) {
|
||||
if i.hasStarted == nil {
|
||||
topEval, err := i.expr.Evaluate(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ok bool
|
||||
i.topValue, ok = topEval.(int64)
|
||||
if !ok {
|
||||
return nil, sql3.NewErrInternalf("unexpected top expression result type %T", topEval)
|
||||
}
|
||||
i.hasStarted = &struct{}{}
|
||||
|
||||
}
|
||||
|
||||
if i.rowCount >= i.topValue {
|
||||
return nil, types.ErrNoMoreRows
|
||||
}
|
||||
|
||||
row, err := i.child.Next(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i.rowCount++
|
||||
return row, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import (
|
|||
pilosa "github.com/molecula/featurebase/v3"
|
||||
"github.com/molecula/featurebase/v3/dax"
|
||||
"github.com/molecula/featurebase/v3/pql"
|
||||
"github.com/molecula/featurebase/v3/sql3/parser"
|
||||
sql_test "github.com/molecula/featurebase/v3/sql3/test"
|
||||
"github.com/molecula/featurebase/v3/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -27,14 +26,14 @@ import (
|
|||
|
||||
func TestPlanner_Misc(t *testing.T) {
|
||||
|
||||
d, err := parser.StringToDecimal("12.345678")
|
||||
d, err := pql.ParseDecimal("12.345678")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, d.EqualTo(pql.NewDecimal(12345678, 6)))
|
||||
|
||||
d = parser.FloatToDecimalWithScale(12.345678, 6)
|
||||
d, err = pql.FromFloat64WithScale(12.345678, 6)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -136,6 +135,30 @@ func TestPlanner_Show(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("SystemTablesExecRequestsAgg", func(t *testing.T) {
|
||||
_, columns, err := sql_test.MustQueryRows(t, c.GetNode(0).Server, `select
|
||||
count(request_id) as request_count,
|
||||
min(elapsed_time) as min_duration,
|
||||
max(elapsed_time) as max_duration,
|
||||
avg(elapsed_time) as avg_duration
|
||||
from
|
||||
fb_exec_requests
|
||||
where
|
||||
status = 'complete';`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff([]*pilosa.WireQueryField{
|
||||
wireQueryFieldInt("request_count"),
|
||||
wireQueryFieldInt("min_duration"),
|
||||
wireQueryFieldInt("max_duration"),
|
||||
wireQueryFieldDecimal("avg_duration", 4),
|
||||
}, columns); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ShowTables", func(t *testing.T) {
|
||||
results, columns, err := sql_test.MustQueryRows(t, c.GetNode(0).Server, `SHOW TABLES`)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue