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
This commit is contained in:
pokeeffe-molecula 2022-12-03 20:39:57 -06:00 committed by GitHub
parent 47d8be26f5
commit 0be0c42b66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 954 additions and 803 deletions

View file

@ -2,6 +2,7 @@
package pql
import (
"fmt"
"math"
"math/big"
"strconv"
@ -343,6 +344,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

View file

@ -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() {

View file

@ -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{

View file

@ -2,11 +2,7 @@ package parser
import (
"fmt"
"math"
"strconv"
"strings"
"github.com/molecula/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 {

View file

@ -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 &param, err
} else if param.Type, err = p.parseType(); err != nil {
return &param, err
}
return &param, 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

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -5,7 +5,6 @@ package planner
import (
"context"
"fmt"
"reflect"
"github.com/molecula/featurebase/v3/pql"
"github.com/molecula/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

View file

@ -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 @

View file

@ -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())

View file

@ -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)

View file

@ -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
}

View file

@ -13,7 +13,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"
@ -21,14 +20,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)
}
@ -130,6 +129,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 {