Full update of the SQL package

This commit is contained in:
Tobie Morgan Hitchcock 2017-11-16 20:53:13 +00:00
parent 2609d761be
commit 1d08949fff
55 changed files with 25318 additions and 3171 deletions

20013
sql/ast.gen.go Normal file

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,10 @@ package sql
import (
"strconv"
"strings"
"time"
"golang.org/x/text/language"
)
// --------------------------------------------------
@ -43,15 +46,11 @@ type AuthableStatement interface {
}
type KillableStatement interface {
Begin()
Cease()
Duration() time.Duration
Timedout() <-chan struct{}
}
type killable struct {
ticker *time.Timer
closer chan struct{}
type WriteableStatement interface {
Writeable() bool
}
// --------------------------------------------------
@ -83,6 +82,7 @@ type UseStatement struct {
// InfoStatement represents an SQL INFO statement.
type InfoStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -90,12 +90,25 @@ type InfoStatement struct {
What *Table `cork:"-" codec:"-"`
}
// --------------------------------------------------
// If
// --------------------------------------------------
// IfStatement represents an if else clause.
type IfStatement struct {
RW bool `cork:"-" codec:"-"`
Cond Exprs `cork:"cond" codec:"cond"`
Then Exprs `cork:"then" codec:"then"`
Else Expr `cork:"else" codec:"else"`
}
// --------------------------------------------------
// LET
// --------------------------------------------------
// LetStatement represents a SQL LET statement.
type LetStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -105,10 +118,11 @@ type LetStatement struct {
// ReturnStatement represents a SQL RETURN statement.
type ReturnStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
What Expr `cork:"-" codec:"-"`
What Exprs `cork:"-" codec:"-"`
}
// --------------------------------------------------
@ -117,84 +131,127 @@ type ReturnStatement struct {
// LiveStatement represents a SQL LIVE statement.
type LiveStatement struct {
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
RW bool `cork:"-" codec:"-"`
ID string `cork:"ID" codec:"ID"`
FB string `cork:"FB" codec:"FB"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
Diff bool `cork:"diff" codec:"diff"`
Expr Fields `cork:"expr" codec:"expr"`
What Exprs `cork:"what" codec:"what"`
What *Table `cork:"what" codec:"what"`
Cond Expr `cork:"cond" codec:"cond"`
Echo Token `cork:"echo" codec:"echo"`
}
// KillStatement represents a SQL KILL statement.
type KillStatement struct {
RW bool `cork:"-" codec:"-"`
FB string `cork:"FB" codec:"FB"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
Name *Value `cork:"name" codec:"name"`
}
// SelectStatement represents a SQL SELECT statement.
type SelectStatement struct {
killable
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Expr Fields `cork:"expr" codec:"expr"`
What Exprs `cork:"what" codec:"what"`
Cond Expr `cork:"cond" codec:"cond"`
Group Groups `cork:"group" codec:"group"`
Order Orders `cork:"order" codec:"order"`
Limit Expr `cork:"limit" codec:"limit"`
Start Expr `cork:"start" codec:"start"`
Version Expr `cork:"version" codec:"version"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
RW bool `cork:"-" codec:"-"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
Expr Fields `cork:"expr" codec:"expr"`
What Exprs `cork:"what" codec:"what"`
Cond Expr `cork:"cond" codec:"cond"`
Group Groups `cork:"group" codec:"group"`
Order Orders `cork:"order" codec:"order"`
Limit Expr `cork:"limit" codec:"limit"`
Start Expr `cork:"start" codec:"start"`
Version Expr `cork:"version" codec:"version"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
Parallel int `cork:"parallel" codec:"parallel"`
}
// CreateStatement represents a SQL CREATE statement.
type CreateStatement struct {
killable
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
What Exprs `cork:"what" codec:"what"`
Data Expr `cork:"data" codec:"data"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
RW bool `cork:"-" codec:"-"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
What Exprs `cork:"what" codec:"what"`
Data Expr `cork:"data" codec:"data"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
Parallel int `cork:"parallel" codec:"parallel"`
}
// UpdateStatement represents a SQL UPDATE statement.
type UpdateStatement struct {
killable
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Hard bool `cork:"hard" codec:"hard"`
What Exprs `cork:"what" codec:"what"`
Data Expr `cork:"data" codec:"data"`
Cond Expr `cork:"cond" codec:"cond"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
RW bool `cork:"-" codec:"-"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
What Exprs `cork:"what" codec:"what"`
Data Expr `cork:"data" codec:"data"`
Cond Expr `cork:"cond" codec:"cond"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
Parallel int `cork:"parallel" codec:"parallel"`
}
// DeleteStatement represents a SQL DELETE statement.
type DeleteStatement struct {
killable
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Hard bool `cork:"hard" codec:"hard"`
What Exprs `cork:"what" codec:"what"`
Cond Expr `cork:"cond" codec:"cond"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
RW bool `cork:"-" codec:"-"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
Hard bool `cork:"hard" codec:"hard"`
What Exprs `cork:"what" codec:"what"`
Cond Expr `cork:"cond" codec:"cond"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
Parallel int `cork:"parallel" codec:"parallel"`
}
// RelateStatement represents a SQL RELATE statement.
type RelateStatement struct {
killable
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Type Expr `cork:"type" codec:"type"`
From Exprs `cork:"from" codec:"from"`
With Exprs `cork:"with" codec:"with"`
Data Expr `cork:"data" codec:"data"`
Uniq bool `cork:"uniq" codec:"uniq"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
RW bool `cork:"-" codec:"-"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
Type Expr `cork:"type" codec:"type"`
From Exprs `cork:"from" codec:"from"`
With Exprs `cork:"with" codec:"with"`
Data Expr `cork:"data" codec:"data"`
Uniq bool `cork:"uniq" codec:"uniq"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
Parallel int `cork:"parallel" codec:"parallel"`
}
// InsertStatement represents a SQL INSERT statement.
type InsertStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
Data Expr `cork:"data" codec:"data"`
Into *Table `cork:"into" codec:"into"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
Parallel int `cork:"parallel" codec:"parallel"`
}
// UpsertStatement represents a SQL UPSERT statement.
type UpsertStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"KV" codec:"KV"`
NS string `cork:"NS" codec:"NS"`
DB string `cork:"DB" codec:"DB"`
Data Expr `cork:"data" codec:"data"`
Into *Table `cork:"into" codec:"into"`
Echo Token `cork:"echo" codec:"echo"`
Timeout time.Duration `cork:"timeout" codec:"timeout"`
Parallel int `cork:"parallel" codec:"parallel"`
}
// --------------------------------------------------
@ -202,6 +259,7 @@ type RelateStatement struct {
// --------------------------------------------------
type DefineNamespaceStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -209,6 +267,7 @@ type DefineNamespaceStatement struct {
}
type RemoveNamespaceStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -220,6 +279,7 @@ type RemoveNamespaceStatement struct {
// --------------------------------------------------
type DefineDatabaseStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -227,6 +287,7 @@ type DefineDatabaseStatement struct {
}
type RemoveDatabaseStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -239,6 +300,7 @@ type RemoveDatabaseStatement struct {
// DefineLoginStatement represents an SQL DEFINE LOGIN statement.
type DefineLoginStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -250,6 +312,7 @@ type DefineLoginStatement struct {
// RemoveLoginStatement represents an SQL REMOVE LOGIN statement.
type RemoveLoginStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -263,6 +326,7 @@ type RemoveLoginStatement struct {
// DefineTokenStatement represents an SQL DEFINE TOKEN statement.
type DefineTokenStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -274,6 +338,7 @@ type DefineTokenStatement struct {
// RemoveTokenStatement represents an SQL REMOVE TOKEN statement.
type RemoveTokenStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -287,18 +352,21 @@ type RemoveTokenStatement struct {
// DefineScopeStatement represents an SQL DEFINE SCOPE statement.
type DefineScopeStatement struct {
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"name" codec:"name"`
Time time.Duration `cork:"time" codec:"time"`
Code []byte `cork:"code" codec:"code"`
Signup Expr `cork:"signup" codec:"signup"`
Signin Expr `cork:"signin" codec:"signin"`
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"name" codec:"name"`
Time time.Duration `cork:"time" codec:"time"`
Code []byte `cork:"code" codec:"code"`
Signup Expr `cork:"signup" codec:"signup"`
Signin Expr `cork:"signin" codec:"signin"`
Connect Expr `cork:"connect" codec:"connect"`
}
// RemoveScopeStatement represents an SQL REMOVE SCOPE statement.
type RemoveScopeStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -311,49 +379,79 @@ type RemoveScopeStatement struct {
// DefineTableStatement represents an SQL DEFINE TABLE statement.
type DefineTableStatement struct {
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
What Tables `cork:"-" codec:"-"`
Full bool `cork:"full" codec:"full"`
Perm *PermExpression `cork:"perm" codec:"perm"`
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"name" codec:"name"`
What Tables `cork:"-" codec:"-"`
Full bool `cork:"full" codec:"full"`
Drop bool `cork:"drop" codec:"drop"`
Lock bool `cork:"lock" codec:"lock"`
Expr Fields `cork:"expr" codec:"expr"`
From Tables `cork:"from" codec:"from"`
Cond Expr `cork:"cond" codec:"cond"`
Group Groups `cork:"group" codec:"group"`
Perms Expr `cork:"perms" codec:"perms"`
}
// RemoveTableStatement represents an SQL REMOVE TABLE statement.
type RemoveTableStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
What Tables `cork:"-" codec:"-"`
}
// --------------------------------------------------
// Event
// --------------------------------------------------
// DefineEventStatement represents an SQL DEFINE EVENT statement.
type DefineEventStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"name" codec:"name"`
What Tables `cork:"-" codec:"-"`
When Expr `cork:"when" codec:"when"`
Then Expr `cork:"then" codec:"then"`
}
// RemoveEventStatement represents an SQL REMOVE EVENT statement.
type RemoveEventStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"-" codec:"-"`
What Tables `cork:"-" codec:"-"`
}
// --------------------------------------------------
// Field
// --------------------------------------------------
// DefineFieldStatement represents an SQL DEFINE FIELD statement.
type DefineFieldStatement struct {
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"name" codec:"name"`
What Tables `cork:"-" codec:"-"`
Type string `cork:"type" codec:"type"`
Perm *PermExpression `cork:"perm" codec:"perm"`
Enum Array `cork:"enum" codec:"enum"`
Code string `cork:"code" codec:"code"`
Min float64 `cork:"min" codec:"min"`
Max float64 `cork:"max" codec:"max"`
Match string `cork:"match" codec:"match"`
Default interface{} `cork:"default" codec:"default"`
Notnull bool `cork:"notnull" codec:"notnull"`
Readonly bool `cork:"readonly" codec:"readonly"`
Mandatory bool `cork:"mandatory" codec:"mandatory"`
Validate bool `cork:"validate" codec:"validate"`
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"name" codec:"name"`
What Tables `cork:"-" codec:"-"`
Perms Expr `cork:"perms" codec:"perms"`
Type string `cork:"type" codec:"type"`
Kind string `cork:"kind" codec:"kind"`
Value Expr `cork:"value" codec:"value"`
Assert Expr `cork:"assert" codec:"assert"`
}
// RemoveFieldStatement represents an SQL REMOVE FIELD statement.
type RemoveFieldStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -367,6 +465,7 @@ type RemoveFieldStatement struct {
// DefineIndexStatement represents an SQL DEFINE INDEX statement.
type DefineIndexStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -378,6 +477,7 @@ type DefineIndexStatement struct {
// RemoveIndexStatement represents an SQL REMOVE INDEX statement.
type RemoveIndexStatement struct {
RW bool `cork:"-" codec:"-"`
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
@ -385,30 +485,6 @@ type RemoveIndexStatement struct {
What Tables `cork:"-" codec:"-"`
}
// --------------------------------------------------
// View
// --------------------------------------------------
// DefineViewStatement represents an SQL DEFINE VIEW statement.
type DefineViewStatement struct {
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"name" codec:"name"`
Expr Fields `cork:"expr" codec:"expr"`
What Tables `cork:"what" codec:"what"`
Cond Expr `cork:"cond" codec:"cond"`
Group Groups `cork:"group" codec:"group"`
}
// RemoveViewStatement represents an SQL REMOVE VIEW statement.
type RemoveViewStatement struct {
KV string `cork:"-" codec:"-"`
NS string `cork:"-" codec:"-"`
DB string `cork:"-" codec:"-"`
Name *Ident `cork:"-" codec:"-"`
}
// --------------------------------------------------
// Literals
// --------------------------------------------------
@ -425,31 +501,17 @@ type All struct{}
// Any represents a ? expression.
type Any struct{}
// Asc represents the ASC expression.
type Asc struct{}
// Desc represents the DESC expression.
type Desc struct{}
// Null represents a null expression.
type Null struct{}
// Void represents an expression which is not set.
type Void struct{}
// Empty represents an expression which is null or "".
type Empty struct{}
// Array represents a parsed json array.
type Array []interface{}
// Object represents a parsed json object.
type Object map[string]interface{}
// Field represents a SELECT AS clause.
type Field struct {
Expr Expr
Alias *Ident
Field string
Alias string
}
// Fields represents multiple SELECT AS clauses.
@ -466,7 +528,8 @@ type Groups []*Group
// Order represents a ORDER BY clause.
type Order struct {
Expr Expr
Dir Expr
Dir bool
Tag language.Tag
}
// Orders represents multiple ORDER BY clauses.
@ -481,10 +544,18 @@ type SubExpression struct {
Expr Expr
}
// IfelExpression represents an if else clause.
type IfelExpression struct {
Cond Exprs
Then Exprs
Else Expr
}
// FuncExpression represents a function call.
type FuncExpression struct {
Name string
Args Exprs
Aggr bool
}
// ItemExpression represents a part of a SET expression.
@ -529,7 +600,6 @@ type PermExpression struct {
Create Expr
Update Expr
Delete Expr
Relate Expr
}
// DataExpression represents a SET expression.
@ -632,6 +702,27 @@ func NewTable(TB string) *Table {
return &Table{TB}
}
// --------------------------------------------------
// Batch
// --------------------------------------------------
// Batchs represents multiple Batch clauses.
type Batchs []*Batch
// Batch comment
type Batch struct {
TB string
BA []*Thing
}
func NewBatch(TB string, BA []interface{}) *Batch {
var b = &Batch{TB: TB}
for _, ID := range BA {
b.BA = append(b.BA, NewThing(TB, ID))
}
return b
}
// --------------------------------------------------
// Thing
// --------------------------------------------------
@ -646,23 +737,74 @@ type Thing struct {
}
func NewThing(TB string, ID interface{}) *Thing {
if str, ok := ID.(string); ok {
if cnv, err := strconv.ParseFloat(str, 64); err == nil {
if cnv == float64(int64(cnv)) {
return &Thing{TB: TB, ID: int64(cnv)}
}
switch val := ID.(type) {
default:
return &Thing{TB: TB, ID: ID}
case int:
return &Thing{TB: TB, ID: float64(val)}
case int64:
return &Thing{TB: TB, ID: float64(val)}
case string:
val = strings.Replace(val, TB+":", "", -1)
if cnv, err := strconv.ParseFloat(val, 64); err == nil {
return &Thing{TB: TB, ID: cnv}
} else if cnv, err := strconv.ParseBool(str); err == nil {
} else if cnv, err := strconv.ParseBool(val); err == nil {
return &Thing{TB: TB, ID: cnv}
} else if cnv, err := time.Parse(RFCDate, str); err == nil {
} else if cnv, err := time.Parse(RFCDate, val); err == nil {
return &Thing{TB: TB, ID: cnv.UTC()}
} else if cnv, err := time.Parse(RFCTime, str); err == nil {
return &Thing{TB: TB, ID: cnv.UTC()}
} else if cnv, err := time.Parse(RFCNorm, str); err == nil {
return &Thing{TB: TB, ID: cnv.UTC()}
} else if cnv, err := time.Parse(RFCText, str); err == nil {
} else if cnv, err := time.Parse(RFCTime, val); err == nil {
return &Thing{TB: TB, ID: cnv.UTC()}
}
return &Thing{TB: TB, ID: val}
}
return &Thing{TB: TB, ID: ID}
}
// --------------------------------------------------
// Point
// --------------------------------------------------
// Points represents multiple Point clauses.
type Points []*Point
// Point comment
type Point struct {
LA float64
LO float64
}
func NewPoint(LA, LO float64) *Point {
return &Point{LA: LA, LO: LO}
}
// --------------------------------------------------
// Circle
// --------------------------------------------------
// Circles represents multiple Circle clauses.
type Circles []*Circle
// Circle comment
type Circle struct {
CE *Point
RA float64
}
func NewCircle(CE *Point, RA float64) *Circle {
return &Circle{CE: CE, RA: RA}
}
// --------------------------------------------------
// Polygon
// --------------------------------------------------
// Polygons represents multiple Polygon clauses.
type Polygons []*Polygon
// Polygon comment
type Polygon struct {
PS Points
}
func NewPolygon(PS ...*Point) *Polygon {
return &Polygon{PS: PS}
}

View file

@ -38,6 +38,10 @@ func (s *LiveStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *KillStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *SelectStatement) Auth() (string, string) {
return s.NS, s.DB
}
@ -58,6 +62,14 @@ func (s *RelateStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *InsertStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *UpsertStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *DefineNamespaceStatement) Auth() (string, string) {
return s.NS, s.DB
}
@ -106,6 +118,14 @@ func (s *RemoveTableStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *DefineEventStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *RemoveEventStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *DefineFieldStatement) Auth() (string, string) {
return s.NS, s.DB
}
@ -121,11 +141,3 @@ func (s *DefineIndexStatement) Auth() (string, string) {
func (s *RemoveIndexStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *DefineViewStatement) Auth() (string, string) {
return s.NS, s.DB
}
func (s *RemoveViewStatement) Auth() (string, string) {
return s.NS, s.DB
}

View file

@ -6,11 +6,14 @@
{ "name": "Return" },
{ "name": "Live" },
{ "name": "Kill" },
{ "name": "Select" },
{ "name": "Create" },
{ "name": "Update" },
{ "name": "Delete" },
{ "name": "Relate" },
{ "name": "Insert" },
{ "name": "Upsert" },
{ "name": "DefineNamespace" },
{ "name": "RemoveNamespace" },
@ -24,10 +27,10 @@
{ "name": "RemoveScope" },
{ "name": "DefineTable" },
{ "name": "RemoveTable" },
{ "name": "DefineEvent" },
{ "name": "RemoveEvent" },
{ "name": "DefineField" },
{ "name": "RemoveField" },
{ "name": "DefineIndex" },
{ "name": "RemoveIndex" },
{ "name": "DefineView" },
{ "name": "RemoveView" }
{ "name": "RemoveIndex" }
]

246
sql/auth_test.go Normal file
View file

@ -0,0 +1,246 @@
// Code generated by https://github.com/abcum/tmpl
// Source file: auth.gen.go.tmpl
// DO NOT EDIT!
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestAuth(t *testing.T) {
Convey("UseStatement should auth correctly", t, func() {
s := &UseStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("InfoStatement should auth correctly", t, func() {
s := &InfoStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("LetStatement should auth correctly", t, func() {
s := &LetStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("ReturnStatement should auth correctly", t, func() {
s := &ReturnStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("LiveStatement should auth correctly", t, func() {
s := &LiveStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("KillStatement should auth correctly", t, func() {
s := &KillStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("SelectStatement should auth correctly", t, func() {
s := &SelectStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("CreateStatement should auth correctly", t, func() {
s := &CreateStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("UpdateStatement should auth correctly", t, func() {
s := &UpdateStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DeleteStatement should auth correctly", t, func() {
s := &DeleteStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RelateStatement should auth correctly", t, func() {
s := &RelateStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("InsertStatement should auth correctly", t, func() {
s := &InsertStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("UpsertStatement should auth correctly", t, func() {
s := &UpsertStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineNamespaceStatement should auth correctly", t, func() {
s := &DefineNamespaceStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveNamespaceStatement should auth correctly", t, func() {
s := &RemoveNamespaceStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineDatabaseStatement should auth correctly", t, func() {
s := &DefineDatabaseStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveDatabaseStatement should auth correctly", t, func() {
s := &RemoveDatabaseStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineLoginStatement should auth correctly", t, func() {
s := &DefineLoginStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveLoginStatement should auth correctly", t, func() {
s := &RemoveLoginStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineTokenStatement should auth correctly", t, func() {
s := &DefineTokenStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveTokenStatement should auth correctly", t, func() {
s := &RemoveTokenStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineScopeStatement should auth correctly", t, func() {
s := &DefineScopeStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveScopeStatement should auth correctly", t, func() {
s := &RemoveScopeStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineTableStatement should auth correctly", t, func() {
s := &DefineTableStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveTableStatement should auth correctly", t, func() {
s := &RemoveTableStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineEventStatement should auth correctly", t, func() {
s := &DefineEventStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveEventStatement should auth correctly", t, func() {
s := &RemoveEventStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineFieldStatement should auth correctly", t, func() {
s := &DefineFieldStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveFieldStatement should auth correctly", t, func() {
s := &RemoveFieldStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("DefineIndexStatement should auth correctly", t, func() {
s := &DefineIndexStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
Convey("RemoveIndexStatement should auth correctly", t, func() {
s := &RemoveIndexStatement{NS: "namespace", DB: "database"}
n, d := s.Auth()
So(n, ShouldEqual, "namespace")
So(d, ShouldEqual, "database")
})
}

62
sql/check.go Normal file
View file

@ -0,0 +1,62 @@
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
func checkExpression(allowed map[string]bool, expr Fields, grps Groups) error {
if len(grps) > 0 {
skip:
for _, e := range expr {
for _, g := range grps {
// If the expression in the SELECT
// clause is a field, then check to
// see if it exists in the GROUP BY.
if i, ok := g.Expr.(*Ident); ok {
if e.Field == i.ID {
continue skip
}
}
// Otherwise if the expression in
// the SELECT clause is a function
// then check to see if it is an
// aggregate function.
if f, ok := e.Expr.(*FuncExpression); ok {
if ok = allowed[f.Name]; ok {
continue skip
}
}
}
// If the expression in the SELECT
// clause isn't an aggregate function
// and isn't specified in the GROUP BY
// clause, then raise an error.
return &GroupError{found: e.Field}
}
}
return nil
}

File diff suppressed because it is too large Load diff

View file

@ -16,9 +16,9 @@ package sql
func (p *parser) parseCreateStatement() (stmt *CreateStatement, err error) {
stmt = &CreateStatement{}
stmt = &CreateStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthSC); err != nil {
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNO); err != nil {
return nil, err
}
@ -38,10 +38,6 @@ func (p *parser) parseCreateStatement() (stmt *CreateStatement, err error) {
return nil, err
}
if _, _, err = p.shouldBe(EOF, RPAREN, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -63,7 +63,7 @@ func (p *parser) parseSet() (mul Expr, err error) {
// always be an identifier, specifying a
// record field to set.
tok, lit, err = p.shouldBe(IDENT)
tok, lit, err = p.shouldBe(IDENT, EXPR)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
}

View file

@ -16,7 +16,7 @@ package sql
func (p *parser) parseDefineDatabaseStatement() (stmt *DefineDatabaseStatement, err error) {
stmt = &DefineDatabaseStatement{}
stmt = &DefineDatabaseStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNS); err != nil {
return nil, err
@ -32,7 +32,7 @@ func (p *parser) parseDefineDatabaseStatement() (stmt *DefineDatabaseStatement,
func (p *parser) parseRemoveDatabaseStatement() (stmt *RemoveDatabaseStatement, err error) {
stmt = &RemoveDatabaseStatement{}
stmt = &RemoveDatabaseStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNS); err != nil {
return nil, err

View file

@ -17,7 +17,7 @@ package sql
func (p *parser) parseDefineStatement() (Statement, error) {
// Inspect the next token.
tok, _, err := p.shouldBe(NAMESPACE, DATABASE, LOGIN, TOKEN, SCOPE, TABLE, FIELD, INDEX, VIEW)
tok, _, err := p.shouldBe(NAMESPACE, DATABASE, LOGIN, TOKEN, SCOPE, TABLE, EVENT, FIELD, INDEX)
switch tok {
case NAMESPACE:
@ -32,12 +32,12 @@ func (p *parser) parseDefineStatement() (Statement, error) {
return p.parseDefineScopeStatement()
case TABLE:
return p.parseDefineTableStatement()
case EVENT:
return p.parseDefineEventStatement()
case FIELD:
return p.parseDefineFieldStatement()
case INDEX:
return p.parseDefineIndexStatement()
case VIEW:
return p.parseDefineViewStatement()
default:
return nil, err
}

View file

@ -16,9 +16,9 @@ package sql
func (p *parser) parseDeleteStatement() (stmt *DeleteStatement, err error) {
stmt = &DeleteStatement{}
stmt = &DeleteStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthSC); err != nil {
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNO); err != nil {
return nil, err
}
@ -47,10 +47,6 @@ func (p *parser) parseDeleteStatement() (stmt *DeleteStatement, err error) {
return nil, err
}
if _, _, err = p.shouldBe(EOF, RPAREN, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -14,9 +14,9 @@
package sql
func (p *parser) parseEcho(empty Token) (exp Token, err error) {
func (p *parser) parseEcho(fallback Token) (exp Token, err error) {
exp = empty
exp = fallback
// The next token that we expect to see is a
// RETURN token, and if we don't find one then
@ -24,7 +24,7 @@ func (p *parser) parseEcho(empty Token) (exp Token, err error) {
if _, _, exi := p.mightBe(RETURN); exi {
exp, _, err = p.shouldBe(ID, NONE, INFO, BOTH, DIFF, BEFORE, AFTER)
exp, _, err = p.shouldBe(NONE, INFO, BOTH, DIFF, BEFORE, AFTER)
if err != nil {
return 0, err
}

View file

@ -43,6 +43,14 @@ func (e *BlankError) Error() string {
return fmt.Sprint("You need to specify a namespace and a database to use")
}
// TransError represents an error that occured when switching access.
type TransError struct{}
// Error returns the string representation of the error.
func (e *TransError) Error() string {
return fmt.Sprintf("You can't change NAMESPACE or DATABASE inside of a transaction")
}
// PermsError represents an error that occured when switching access.
type PermsError struct {
Resource string
@ -53,6 +61,16 @@ func (e *PermsError) Error() string {
return fmt.Sprintf("You don't have permission to access the '%s' resource", e.Resource)
}
// GroupError occurs when a 'group' expression is invalid.
type GroupError struct {
found interface{}
}
// Error returns the string representation of the error.
func (e *GroupError) Error() string {
return fmt.Sprintf("Found '%v' but field is not an aggregate function, and is not present in GROUP expression", e.found)
}
// ParseError represents an error that occurred during parsing.
type ParseError struct {
Found string

View file

@ -14,9 +14,9 @@
package sql
func (p *parser) parseDefineViewStatement() (stmt *DefineViewStatement, err error) {
func (p *parser) parseDefineEventStatement() (stmt *DefineEventStatement, err error) {
stmt = &DefineViewStatement{}
stmt = &DefineEventStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -26,38 +26,27 @@ func (p *parser) parseDefineViewStatement() (stmt *DefineViewStatement, err erro
return nil, err
}
_, _, err = p.shouldBe(AS)
if err != nil {
if _, _, err = p.shouldBe(ON); err != nil {
return nil, err
}
_, _, err = p.shouldBe(SELECT)
if err != nil {
if stmt.What, err = p.parseTables(); err != nil {
return nil, err
}
if stmt.Expr, err = p.parseFields(); err != nil {
if _, _, err = p.shouldBe(WHEN); err != nil {
return nil, err
}
_, _, err = p.shouldBe(FROM)
if err != nil {
if stmt.When, err = p.parseExpr(); err != nil {
return nil, err
}
if stmt.What, err = p.parseWhat(); err != nil {
if _, _, err = p.shouldBe(THEN); err != nil {
return nil, err
}
if stmt.Cond, err = p.parseCond(); err != nil {
return nil, err
}
if stmt.Group, err = p.parseGroup(); err != nil {
return nil, err
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
if stmt.Then, err = p.parseExpr(); err != nil {
return nil, err
}
@ -65,9 +54,9 @@ func (p *parser) parseDefineViewStatement() (stmt *DefineViewStatement, err erro
}
func (p *parser) parseRemoveViewStatement() (stmt *RemoveViewStatement, err error) {
func (p *parser) parseRemoveEventStatement() (stmt *RemoveEventStatement, err error) {
stmt = &RemoveViewStatement{}
stmt = &RemoveEventStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -77,7 +66,11 @@ func (p *parser) parseRemoveViewStatement() (stmt *RemoveViewStatement, err erro
return nil, err
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
if _, _, err = p.shouldBe(ON); err != nil {
return nil, err
}
if stmt.What, err = p.parseTables(); err != nil {
return nil, err
}

View file

@ -16,42 +16,22 @@ package sql
import (
"fmt"
"regexp"
"sort"
"time"
"golang.org/x/crypto/bcrypt"
"github.com/abcum/surreal/util/rand"
"golang.org/x/text/language"
)
func (p *parser) parseWhat() (mul []Expr, err error) {
for {
tok, lit, err := p.shouldBe(IDENT, THING, PARAM, MODEL)
exp, err := p.parsePart()
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"table, or thing"}}
return nil, err
}
if p.is(tok, IDENT) {
one, _ := p.declare(TABLE, lit)
mul = append(mul, one)
}
if p.is(tok, THING) {
one, _ := p.declare(THING, lit)
mul = append(mul, one)
}
if p.is(tok, PARAM) {
one, _ := p.declare(PARAM, lit)
mul = append(mul, one)
}
if p.is(tok, MODEL) {
one, _ := p.declare(MODEL, lit)
mul = append(mul, one)
}
mul = append(mul, exp)
// Check to see if the next token is a comma
// and if not, then break out of the loop,
@ -67,6 +47,19 @@ func (p *parser) parseWhat() (mul []Expr, err error) {
}
func (p *parser) parseValue() (*Value, error) {
tok, lit, err := p.shouldBe(STRING, REGION)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"string"}}
}
val, err := p.declare(tok, lit)
return val.(*Value), err
}
func (p *parser) parseIdent() (*Ident, error) {
_, lit, err := p.shouldBe(IDENT)
@ -135,40 +128,6 @@ func (p *parser) parseTables() (mul Tables, err error) {
}
func (p *parser) parseThing() (*Thing, error) {
_, lit, err := p.shouldBe(THING)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"thing"}}
}
val, err := p.declare(THING, lit)
return val.(*Thing), err
}
func (p *parser) parseThings() (mul Things, err error) {
for {
one, err := p.parseThing()
if err != nil {
return nil, err
}
mul = append(mul, one)
if _, _, exi := p.mightBe(COMMA); !exi {
break
}
}
return
}
func (p *parser) parseIdiom() (*Ident, error) {
_, lit, err := p.shouldBe(IDENT, EXPR)
@ -225,92 +184,6 @@ func (p *parser) parseCond() (exp Expr, err error) {
//
// --------------------------------------------------
func (p *parser) parseRand() (exp []byte, err error) {
exp = rand.New(128)
return
}
// --------------------------------------------------
//
// --------------------------------------------------
func (p *parser) parseArray() (Array, error) {
_, lit, err := p.shouldBe(ARRAY)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"array"}}
}
val, err := p.declare(ARRAY, lit)
return val.(Array), err
}
func (p *parser) parseObject() (Object, error) {
_, lit, err := p.shouldBe(JSON)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"object"}}
}
val, err := p.declare(JSON, lit)
return val.(Object), err
}
func (p *parser) parseNumber() (int64, error) {
_, lit, err := p.shouldBe(NUMBER)
if err != nil {
return int64(0), &ParseError{Found: lit, Expected: []string{"number"}}
}
val, err := p.declare(NUMBER, lit)
return val.(int64), err
}
func (p *parser) parseDouble() (float64, error) {
_, lit, err := p.shouldBe(NUMBER, DOUBLE)
if err != nil {
return float64(0), &ParseError{Found: lit, Expected: []string{"number"}}
}
val, err := p.declare(DOUBLE, lit)
return val.(float64), err
}
func (p *parser) parseString() (string, error) {
_, lit, err := p.shouldBe(STRING)
if err != nil {
return string(""), &ParseError{Found: lit, Expected: []string{"string"}}
}
return lit, err
}
func (p *parser) parseRegion() (string, error) {
_, lit, err := p.shouldBe(STRING, REGION)
if err != nil {
return string(""), &ParseError{Found: lit, Expected: []string{"string"}}
}
return lit, err
}
func (p *parser) parseBinary() ([]byte, error) {
_, lit, err := p.shouldBe(STRING, REGION)
@ -322,40 +195,13 @@ func (p *parser) parseBinary() ([]byte, error) {
}
func (p *parser) parseScript() (string, error) {
func (p *parser) parseTimeout() (time.Duration, error) {
_, lit, err := p.shouldBe(STRING, REGION)
if err != nil {
return string(""), &ParseError{Found: lit, Expected: []string{"script"}}
if _, _, exi := p.mightBe(TIMEOUT); !exi {
return 0, nil
}
return lit, err
}
func (p *parser) parseRegexp() (string, error) {
tok, lit, err := p.shouldBe(REGEX)
if err != nil {
return string(""), &ParseError{Found: lit, Expected: []string{"regular expression"}}
}
val, err := p.declare(tok, lit)
return val.(*regexp.Regexp).String(), err
}
func (p *parser) parseBoolean() (bool, error) {
tok, lit, err := p.shouldBe(TRUE, FALSE)
if err != nil {
return bool(false), &ParseError{Found: lit, Expected: []string{"boolean"}}
}
val, err := p.declare(tok, lit)
return val.(bool), err
return p.parseDuration()
}
@ -372,48 +218,69 @@ func (p *parser) parseDuration() (time.Duration, error) {
}
func (p *parser) parseTimeout() (time.Duration, error) {
func (p *parser) parseType() (t, k string, err error) {
if _, _, exi := p.mightBe(TIMEOUT); !exi {
return 0, nil
_, t, err = p.shouldBe(IDENT, STRING)
if err != nil {
err = &ParseError{Found: t, Expected: allowedTypes}
return
}
return p.parseDuration()
if !p.contains(t, allowedTypes) {
err = &ParseError{Found: t, Expected: allowedTypes}
return
}
if t == "record" {
if _, _, exi := p.mightBe(LPAREN); exi {
if _, k, err = p.shouldBe(IDENT); err != nil {
return
}
if _, _, err = p.shouldBe(RPAREN); err != nil {
return
}
}
}
return
}
func (p *parser) parseBcrypt() ([]byte, error) {
func (p *parser) parseLanguage() (language.Tag, error) {
_, lit, err := p.shouldBe(STRING)
_, lit, err := p.shouldBe(IDENT, STRING)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"string"}}
return language.English, &ParseError{Found: lit, Expected: []string{"string"}}
}
return bcrypt.GenerateFromPassword([]byte(lit), bcrypt.DefaultCost)
tag, err := language.Parse(lit)
if err != nil {
return language.English, &ParseError{Found: lit, Expected: []string{"BCP47 language"}}
}
if _, _, exi := p.mightBe(NUMERIC); exi {
tag, _ = tag.SetTypeForKey("kn", "true")
}
return tag, err
}
func (p *parser) parseAlgorithm() (string, error) {
expected := []string{
"ES256", "ES384", "ES512",
"HS256", "HS384", "HS512",
"PS256", "PS384", "PS512",
"RS256", "RS384", "RS512",
}
_, lit, err := p.shouldBe(IDENT, STRING)
if err != nil {
return string(""), &ParseError{Found: lit, Expected: expected}
return string(""), &ParseError{Found: lit, Expected: allowedAlgorithms}
}
switch lit {
case "ES256", "ES384", "ES512":
case "HS256", "HS384", "HS512":
case "PS256", "PS384", "PS512":
case "RS256", "RS384", "RS512":
case
"ES256", "ES384", "ES512",
"HS256", "HS384", "HS512",
"PS256", "PS384", "PS512",
"RS256", "RS384", "RS512":
default:
return string(""), &ParseError{Found: lit, Expected: expected}
return string(""), &ParseError{Found: lit, Expected: allowedAlgorithms}
}
return lit, err
@ -426,28 +293,40 @@ func (p *parser) parseExpr() (exp Expr, err error) {
root := &BinaryExpression{}
// If the subsequent token is an in, out, or
// multi way path expression, then parse all
// following expressions as a path.
// If the primary token is an in, out, or
// multi way path expression, then follow
// the path through to the end.
if tok, _, exi := p.mightBe(OEDGE, IEDGE, BEDGE); exi {
return p.parsePath(tok)
}
// Begin with parsing the first expression
// as the root of the tree to start with.
root.RHS, err = p.parsePath(tok)
if err != nil {
return nil, err
}
root.RHS, err = p.parsePart()
if err != nil {
return nil, err
}
} else {
// If the subsequent token is an in, out, or
// multi way path expression, then parse all
// following expressions as a path.
// Otherwise begin with parsing the first
// expression, as the root of the tree.
root.RHS, err = p.parsePart()
if err != nil {
return nil, err
}
// But if the subsequent token is an in, out,
// or multi way path expression, then follow
// the path through to the end.
if tok, _, exi := p.mightBe(DOT, OEDGE, IEDGE, BEDGE); exi {
root.RHS, err = p.parsePath(root.RHS, tok)
if err != nil {
return nil, err
}
}
if tok, _, exi := p.mightBe(OEDGE, IEDGE, BEDGE); exi {
return p.parsePath(root.RHS, tok)
}
// Loop over the operations and expressions
@ -548,7 +427,7 @@ func (p *parser) parseExpr() (exp Expr, err error) {
// of the expression and add it to the right.
if rhs == nil {
rhs, err = p.parsePart()
rhs, err = p.parseExpr()
if err != nil {
return nil, err
}
@ -562,6 +441,21 @@ func (p *parser) parseExpr() (exp Expr, err error) {
for node := root; ; {
r, ok := node.RHS.(*BinaryExpression)
// IMPORTANT fix binary OR/AND expressions
/*if !ok {
if node.Op.precedence() >= tok.precedence() {
node.RHS = &BinaryExpression{LHS: node.RHS, Op: tok, RHS: rhs}
break
} else {
r = &BinaryExpression{LHS: node, Op: tok, RHS: rhs}
break
}
} else {
if r.Op.precedence() >= tok.precedence() {
node.RHS = &BinaryExpression{LHS: node.RHS, Op: tok, RHS: rhs}
break
}
}*/
if !ok || r.Op.precedence() >= tok.precedence() {
node.RHS = &BinaryExpression{LHS: node.RHS, Op: tok, RHS: rhs}
break
@ -578,10 +472,10 @@ func (p *parser) parseExpr() (exp Expr, err error) {
func (p *parser) parsePart() (exp Expr, err error) {
toks := []Token{
MUL, ID, EXPR, IDENT, THING,
MUL, EXPR, IDENT, THING, MODEL,
NULL, VOID, EMPTY, MISSING,
TRUE, FALSE, STRING, REGION, NUMBER, DOUBLE,
NOW, DATE, TIME, DURATION, JSON, ARRAY, PARAM, LPAREN,
DATE, TIME, DURATION, JSON, ARRAY, PARAM, LPAREN, IF,
}
tok, lit, _ := p.scan()
@ -595,6 +489,14 @@ func (p *parser) parsePart() (exp Expr, err error) {
return nil, err
}
// If the current token is a IF word clause
// then we will parse anything from here on
// as an IF expression clause.
if p.is(tok, IF) {
return p.parseIfel()
}
// If the current token is a left parenthesis
// bracket, then we will parse this complete
// expression part as a subquery.
@ -628,33 +530,114 @@ func (p *parser) parseSubq() (sub *SubExpression, err error) {
var exp Expr
var tok Token
tok, _, _ = p.mightBe(SELECT, CREATE, UPDATE, DELETE, RELATE)
tok, _, _ = p.mightBe(SELECT, CREATE, UPDATE, DELETE, RELATE, INSERT, UPSERT)
switch tok {
default:
exp, err = p.parseExpr()
case SELECT:
exp, err = p.parseSelectStatement()
case CREATE:
p.buf.rw = true
exp, err = p.parseCreateStatement()
case UPDATE:
p.buf.rw = true
exp, err = p.parseUpdateStatement()
case DELETE:
p.buf.rw = true
exp, err = p.parseDeleteStatement()
case RELATE:
p.buf.rw = true
exp, err = p.parseRelateStatement()
case INSERT:
p.buf.rw = true
exp, err = p.parseInsertStatement()
case UPSERT:
p.buf.rw = true
exp, err = p.parseUpsertStatement()
default:
exp, err = p.parseExpr()
}
p.mightBe(RPAREN)
if err != nil {
return nil, err
}
_, _, err = p.shouldBe(RPAREN)
return &SubExpression{Expr: exp}, err
}
func (p *parser) parseIfel() (exp *IfelExpression, err error) {
exp = &IfelExpression{}
for {
var tok Token
if cond, err := p.parseExpr(); err != nil {
return nil, err
} else {
exp.Cond = append(exp.Cond, cond)
}
if _, _, err = p.shouldBe(THEN); err != nil {
return nil, err
}
if then, err := p.parseExpr(); err != nil {
return nil, err
} else {
exp.Then = append(exp.Then, then)
}
// Check to see if the next token is an
// ELSE keyword and if it is then check to
// see if there is another if statement.
if tok, _, err = p.shouldBe(ELSE, END); err != nil {
return nil, err
}
if tok == END {
return
}
if tok == ELSE {
if _, _, exi := p.mightBe(IF); !exi {
break
}
}
}
if then, err := p.parseExpr(); err != nil {
return nil, err
} else {
exp.Else = then
}
if _, _, err = p.shouldBe(END); err != nil {
return nil, err
}
return
}
func (p *parser) parseCall(name string) (fnc *FuncExpression, err error) {
fnc = &FuncExpression{Name: name}
// Check to see if this is an aggregate
// function, and if it is then mark it,
// so we can process it correcyly in the
// 'iterator' and 'document' layers.
if _, ok := aggrs[name]; ok {
fnc.Aggr = true
}
// Check to see if the immediate token
// is a right parenthesis bracket, and if
// it is then this function has no args.
@ -702,13 +685,27 @@ func (p *parser) parseCall(name string) (fnc *FuncExpression, err error) {
}
// Check to see if this function is allowed to
// have an undefined number of arguments, and
// if it is then skip argument checking.
if _, ok := funcs[fnc.Name][-1]; ok {
return
}
// Check to see if the number of arguments
// is correct for the specified function name,
// and if not, then return an error.
if _, ok := funcs[fnc.Name][len(fnc.Args)]; !ok {
s, t := "", len(funcs[fnc.Name])
s, a, t := "", []int{}, len(funcs[fnc.Name])
for i := range funcs[fnc.Name] {
a = append(a, i)
}
sort.Ints(a)
for i := 0; i < t; i++ {
switch {
@ -717,13 +714,13 @@ func (p *parser) parseCall(name string) (fnc *FuncExpression, err error) {
case i > 0:
s = s + ", "
}
s = s + fmt.Sprintf("%d", i)
s = s + fmt.Sprintf("%d", a[i])
}
switch {
case t == 1:
switch t {
case 1:
s = s + " argument"
case t >= 2:
default:
s = s + " arguments"
}
@ -740,6 +737,17 @@ func (p *parser) parseCall(name string) (fnc *FuncExpression, err error) {
func (p *parser) parsePath(expr ...Expr) (path *PathExpression, err error) {
defer func() {
if val, ok := path.Expr[len(path.Expr)-1].(*JoinExpression); ok {
if val.Join == DOT {
err = &ParseError{
Found: fmt.Sprintf("."),
Expected: []string{"field expression"},
}
}
}
}()
path = &PathExpression{}
// Take the previosuly scanned expression
@ -820,7 +828,7 @@ func (p *parser) parsePath(expr ...Expr) (path *PathExpression, err error) {
func (p *parser) parseJoin() (exp Expr, err error) {
toks := []Token{
OEDGE, IEDGE, BEDGE,
DOT, OEDGE, IEDGE, BEDGE,
}
tok, _, _ := p.scan()
@ -837,7 +845,7 @@ func (p *parser) parseJoin() (exp Expr, err error) {
func (p *parser) parseStep() (exp Expr, err error) {
toks := []Token{
QMARK, IDENT, THING, PARAM, LPAREN,
QMARK, IDENT, THING, PARAM, LPAREN, EXPR, MUL,
}
tok, lit, _ := p.scan()
@ -876,6 +884,8 @@ func (p *parser) parseSubp() (stmt *SubpExpression, err error) {
stmt = &SubpExpression{}
// IMPORTANT maybe we should not accept any expression here
if stmt.What, err = p.parseWhat(); err != nil {
return nil, err
}

View file

@ -16,13 +16,13 @@ package sql
func (p *parser) parseDefineFieldStatement() (stmt *DefineFieldStatement, err error) {
stmt = &DefineFieldStatement{}
stmt = &DefineFieldStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
}
if stmt.Name, err = p.parseIdent(); err != nil {
if stmt.Name, err = p.parseIdiom(); err != nil {
return nil, err
}
@ -36,108 +36,44 @@ func (p *parser) parseDefineFieldStatement() (stmt *DefineFieldStatement, err er
for {
tok, _, exi := p.mightBe(MIN, MAX, TYPE, ENUM, CODE, MATCH, DEFAULT, NOTNULL, READONLY, MANDATORY, VALIDATE, PERMISSIONS)
tok, _, exi := p.mightBe(TYPE, VALUE, ASSERT, PERMISSIONS)
if !exi {
break
}
if p.is(tok, MIN) {
if stmt.Min, err = p.parseDouble(); err != nil {
return nil, err
}
}
if p.is(tok, MAX) {
if stmt.Max, err = p.parseDouble(); err != nil {
return nil, err
}
}
if p.is(tok, TYPE) {
if stmt.Type, err = p.parseType(); err != nil {
if stmt.Type, stmt.Kind, err = p.parseType(); err != nil {
return nil, err
}
}
if p.is(tok, ENUM) {
if stmt.Enum, err = p.parseArray(); err != nil {
if p.is(tok, VALUE) {
if stmt.Value, err = p.parseExpr(); err != nil {
return nil, err
}
}
if p.is(tok, CODE) {
if stmt.Code, err = p.parseScript(); err != nil {
if p.is(tok, ASSERT) {
if stmt.Assert, err = p.parseExpr(); err != nil {
return nil, err
}
}
if p.is(tok, MATCH) {
if stmt.Match, err = p.parseRegexp(); err != nil {
return nil, err
}
}
if p.is(tok, DEFAULT) {
if stmt.Default, err = p.parseExpr(); err != nil {
return nil, err
}
}
if p.is(tok, NOTNULL) {
stmt.Notnull = true
if tok, _, exi := p.mightBe(TRUE, FALSE); exi {
if tok == FALSE {
stmt.Notnull = false
}
}
}
if p.is(tok, READONLY) {
stmt.Readonly = true
if tok, _, exi := p.mightBe(TRUE, FALSE); exi {
if tok == FALSE {
stmt.Readonly = false
}
}
}
if p.is(tok, MANDATORY) {
stmt.Mandatory = true
if tok, _, exi := p.mightBe(TRUE, FALSE); exi {
if tok == FALSE {
stmt.Mandatory = false
}
}
}
if p.is(tok, VALIDATE) {
stmt.Validate = true
if tok, _, exi := p.mightBe(TRUE, FALSE); exi {
if tok == FALSE {
stmt.Validate = false
}
}
}
if p.is(tok, PERMISSIONS) {
if stmt.Perm, err = p.parsePerms(); err != nil {
if stmt.Perms, err = p.parsePerms(); err != nil {
return nil, err
}
}
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}
func (p *parser) parseRemoveFieldStatement() (stmt *RemoveFieldStatement, err error) {
stmt = &RemoveFieldStatement{}
stmt = &RemoveFieldStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -155,10 +91,6 @@ func (p *parser) parseRemoveFieldStatement() (stmt *RemoveFieldStatement, err er
return nil, err
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -14,233 +14,272 @@
package sql
var funcs = map[string]map[int]bool{
var rolls = map[string]bool{
"abs": {
1: true,
},
"distinct": true,
"avg": {
1: true,
},
// Count implementation
"ceil": {
1: true,
},
"count": true,
"count.if": true,
"count.not": true,
"count": {
1: true,
},
// Math implementation
"date": {
0: true,
1: true,
},
"geometricmean": true,
"mean": true,
"percentile": true,
"stddev": true,
"sum": true,
"variance": true,
"day": {
0: true,
1: true,
},
// Math implementation
"derivative": {
1: true,
},
"math.geometricmean": true,
"math.mean": true,
"math.percentile": true,
"math.stddev": true,
"math.sum": true,
"math.variance": true,
}
"difference": {
1: true,
2: true,
3: true,
4: true,
5: true,
6: true,
7: true,
8: true,
9: true,
},
var aggrs = map[string]bool{
"distinct": {
1: true,
},
"distinct": true,
"floor": {
1: true,
},
// Count implementation
"hour": {
0: true,
1: true,
},
"count": true,
"count.if": true,
"count.not": true,
"intersect": {
1: true,
2: true,
3: true,
4: true,
5: true,
6: true,
7: true,
8: true,
9: true,
},
// Math implementation
"max": {
1: true,
},
"bottom": true,
"geometricmean": true,
"harmonicmean": true,
"interquartile": true,
"max": true,
"mean": true,
"median": true,
"midhinge": true,
"min": true,
"mode": true,
"percentile": true,
"sample": true,
"spread": true,
"stddev": true,
"sum": true,
"top": true,
"trimean": true,
"variance": true,
"md5": {
1: true,
},
// Math implementation
"mean": {
1: true,
},
"math.bottom": true,
"math.geometricmean": true,
"math.harmonicmean": true,
"math.interquartile": true,
"math.max": true,
"math.mean": true,
"math.median": true,
"math.midhinge": true,
"math.min": true,
"math.mode": true,
"math.percentile": true,
"math.sample": true,
"math.spread": true,
"math.stddev": true,
"math.sum": true,
"math.top": true,
"math.trimean": true,
"math.variance": true,
}
"median": {
1: true,
},
var funcs = map[string]map[int]interface{}{
"min": {
1: true,
},
"batch": {2: nil},
"binary": {1: nil},
"difference": {-1: nil},
"distinct": {1: nil},
"get": {2: nil},
"if": {3: nil},
"intersect": {-1: nil},
"model": {2: nil, 3: nil, 4: nil},
"table": {1: nil},
"thing": {2: nil},
"union": {-1: nil},
"mins": {
0: true,
1: true,
},
// Count implementation
"mode": {
1: true,
},
"count": {1: nil},
"count.if": {2: nil},
"count.not": {2: nil},
"month": {
0: true,
1: true,
},
// Json implementation
"json.decode": {1: nil},
"json.encode": {1: nil},
"now": {
0: true,
},
// Geo implementation
"geo.point": {1: nil, 2: nil},
"geo.circle": {2: nil},
"geo.polygon": {-1: nil},
"geo.distance": {2: nil},
"geo.inside": {2: nil},
"geo.intersects": {2: nil},
"geo.hash.decode": {1: nil},
"geo.hash.encode": {2: nil},
"percentile": {
1: true,
},
// Http implementation
"http.head": {1: nil, 2: nil},
"http.get": {1: nil, 2: nil},
"http.put": {1: nil, 2: nil, 3: nil},
"http.post": {1: nil, 2: nil, 3: nil},
"http.patch": {1: nil, 2: nil, 3: nil},
"http.delete": {1: nil, 2: nil},
"http.async.head": {1: nil, 2: nil},
"http.async.get": {1: nil, 2: nil},
"http.async.put": {1: nil, 2: nil, 3: nil},
"http.async.post": {1: nil, 2: nil, 3: nil},
"http.async.patch": {1: nil, 2: nil, 3: nil},
"http.async.delete": {1: nil, 2: nil},
"round": {
1: true,
},
// Math implementation
"abs": {1: nil},
"bottom": {2: nil},
"ceil": {1: nil},
"correlation": {2: nil},
"covariance": {2: nil},
"floor": {1: nil},
"geometricmean": {1: nil},
"harmonicmean": {1: nil},
"interquartile": {1: nil},
"max": {1: nil},
"mean": {1: nil},
"median": {1: nil},
"midhinge": {1: nil},
"min": {1: nil},
"mode": {1: nil},
"percentile": {2: nil},
"round": {2: nil},
"sample": {2: nil},
"spread": {1: nil},
"stddev": {1: nil},
"sum": {1: nil},
"top": {2: nil},
"trimean": {1: nil},
"variance": {1: nil},
"math.abs": {1: nil},
"math.bottom": {2: nil},
"math.ceil": {1: nil},
"math.correlation": {2: nil},
"math.covariance": {2: nil},
"math.floor": {1: nil},
"math.geometricmean": {1: nil},
"math.harmonicmean": {1: nil},
"math.interquartile": {1: nil},
"math.max": {1: nil},
"math.mean": {1: nil},
"math.median": {1: nil},
"math.midhinge": {1: nil},
"math.min": {1: nil},
"math.mode": {1: nil},
"math.percentile": {2: nil},
"math.round": {1: nil},
"math.sample": {2: nil},
"math.spread": {1: nil},
"math.stddev": {1: nil},
"math.sum": {1: nil},
"math.top": {2: nil},
"math.trimean": {1: nil},
"math.variance": {1: nil},
"stddev": {
1: true,
},
// String implementation
"string.concat": {-1: nil},
"string.contains": {2: nil},
"string.endsWith": {2: nil},
"string.format": {-1: nil},
"string.includes": {2: nil},
"string.join": {-1: nil},
"string.length": {1: nil},
"string.lowercase": {1: nil},
"string.repeat": {2: nil},
"string.replace": {3: nil},
"string.reverse": {3: nil},
"string.search": {2: nil},
"string.slice": {3: nil},
"string.split": {2: nil},
"string.startsWith": {2: nil},
"string.substr": {3: nil},
"string.trim": {1: nil},
"string.uppercase": {1: nil},
"string.words": {1: nil},
"sum": {
1: true,
},
// Hash implementation
"hash.md5": {1: nil},
"hash.sha1": {1: nil},
"hash.sha256": {1: nil},
"hash.sha512": {1: nil},
"table": {
1: true,
},
// Time implementation
"time.now": {0: nil},
"time.add": {2: nil},
"time.age": {2: nil},
"time.floor": {2: nil},
"time.round": {2: nil},
"time.day": {0: nil, 1: nil},
"time.hour": {0: nil, 1: nil},
"time.mins": {0: nil, 1: nil},
"time.month": {0: nil, 1: nil},
"time.nano": {0: nil, 1: nil},
"time.secs": {0: nil, 1: nil},
"time.unix": {0: nil, 1: nil},
"time.year": {0: nil, 1: nil},
"thing": {
2: true,
},
"union": {
1: true,
2: true,
3: true,
4: true,
5: true,
6: true,
7: true,
8: true,
9: true,
},
"unixtime": {
0: true,
1: true,
},
"uuid": {
0: true,
},
"variance": {
1: true,
},
"year": {
0: true,
1: true,
},
// HOTP implementation
"hotp.compare": {
2: true,
},
"hotp.generate": {
1: true,
},
// TOTP implementation
"totp.compare": {
2: true,
},
"totp.generate": {
1: true,
},
// Email implementation
"email.user": {1: nil},
"email.domain": {1: nil},
"email.valid": {1: nil},
// Bcrypt implementation
"bcrypt.compare": {
2: true,
},
"bcrypt.generate": {
1: true,
},
"bcrypt.compare": {2: nil},
"bcrypt.generate": {1: nil},
// Scrypt implementation
"scrypt.compare": {2: nil},
"scrypt.generate": {1: nil},
"scrypt.compare": {
2: true,
},
"scrypt.generate": {
1: true,
},
// Pbkdf2 implementation
"pbkdf2.compare": {
2: true,
},
"pbkdf2.generate": {
1: true,
},
// Yubikey implementation
"yubikey.id": {
1: true,
},
"yubikey.ctr": {
1: true,
},
"yubikey.use": {
1: true,
},
"yubikey.verify": {
2: true, // yubikey.verify(AUTHSERV, $otp)
3: true, // yubikey.verify(CLIENTID, SECRET, $otp)
},
// Random implementation
"rand": {0: nil},
"uuid": {0: nil},
"rand.bool": {0: nil},
"rand.uuid": {0: nil},
"rand.enum": {-1: nil},
"rand.time": {0: nil, 2: nil},
"rand.string": {0: nil, 1: nil, 2: nil},
"rand.integer": {0: nil, 2: nil},
"rand.decimal": {0: nil, 2: nil},
"rand.sentence": {0: nil, 2: nil},
"rand.paragraph": {0: nil, 2: nil},
"rand.person.email": {0: nil},
"rand.person.phone": {0: nil},
"rand.person.fullname": {0: nil},
"rand.person.firstname": {0: nil},
"rand.person.lastname": {0: nil},
"rand.person.username": {0: nil},
"rand.person.jobtitle": {0: nil},
"rand.company.name": {0: nil},
"rand.company.industry": {0: nil},
"rand.location.name": {0: nil},
"rand.location.address": {0: nil},
"rand.location.street": {0: nil},
"rand.location.city": {0: nil},
"rand.location.state": {0: nil},
"rand.location.county": {0: nil},
"rand.location.zipcode": {0: nil},
"rand.location.postcode": {0: nil},
"rand.location.country": {0: nil},
"rand.location.altitude": {0: nil},
"rand.location.latitude": {0: nil},
"rand.location.longitude": {0: nil},
}

View file

@ -17,3 +17,7 @@ package sql
//go:generate go get -u github.com/abcum/tmpl
//go:generate tmpl -file=auth.gen.json auth.gen.go.tmpl
//go:generate tmpl -file=kill.gen.json kill.gen.go.tmpl
//go:generate tmpl -file=rdwr.gen.json rdwr.gen.go.tmpl
//go:generate go get -u github.com/ugorji/go/codec/codecgen
//go:generate codecgen -o ast.gen.go ast.go

83
sql/if.go Normal file
View file

@ -0,0 +1,83 @@
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
func (p *parser) parseIfStatement() (stmt *IfStatement, err error) {
stmt = &IfStatement{}
for {
var tok Token
if cond, err := p.parseExpr(); err != nil {
return nil, err
} else {
stmt.Cond = append(stmt.Cond, cond)
}
if _, _, err = p.shouldBe(THEN); err != nil {
return nil, err
}
if then, err := p.parseExpr(); err != nil {
return nil, err
} else {
stmt.Then = append(stmt.Then, then)
}
// Check to see if the next token is an
// ELSE keyword and if it is then check to
// see if there is another if statement.
if tok, _, err = p.shouldBe(ELSE, END); err != nil {
return nil, err
}
if tok == END {
return
}
if tok == ELSE {
if _, _, exi := p.mightBe(IF); !exi {
break
}
}
}
// Check to see if the next token is an
// ELSE keyword and if it is then check to
// see if there is another if statement.
if then, err := p.parseExpr(); err != nil {
return nil, err
} else {
stmt.Else = then
}
if _, _, err = p.shouldBe(END); err != nil {
return nil, err
}
// If this query has any subqueries which
// need to alter the database then mark
// this query as a writeable statement.
stmt.RW = p.buf.rw
return
}

View file

@ -16,7 +16,7 @@ package sql
func (p *parser) parseDefineIndexStatement() (stmt *DefineIndexStatement, err error) {
stmt = &DefineIndexStatement{}
stmt = &DefineIndexStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -44,17 +44,13 @@ func (p *parser) parseDefineIndexStatement() (stmt *DefineIndexStatement, err er
_, _, stmt.Uniq = p.mightBe(UNIQUE)
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}
func (p *parser) parseRemoveIndexStatement() (stmt *RemoveIndexStatement, err error) {
stmt = &RemoveIndexStatement{}
stmt = &RemoveIndexStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -72,10 +68,6 @@ func (p *parser) parseRemoveIndexStatement() (stmt *RemoveIndexStatement, err er
return nil, err
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -16,7 +16,7 @@ package sql
func (p *parser) parseInfoStatement() (stmt *InfoStatement, err error) {
stmt = &InfoStatement{}
stmt = &InfoStatement{RW: false}
if _, _, err = p.shouldBe(FOR); err != nil {
return nil, err
@ -47,10 +47,6 @@ func (p *parser) parseInfoStatement() (stmt *InfoStatement, err error) {
}
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}

49
sql/insert.go Normal file
View file

@ -0,0 +1,49 @@
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
func (p *parser) parseInsertStatement() (stmt *InsertStatement, err error) {
stmt = &InsertStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNO); err != nil {
return nil, err
}
if stmt.Data, err = p.parseExpr(); err != nil {
return nil, err
}
if _, _, err = p.shouldBe(INTO); err != nil {
return nil, err
}
_, _, _ = p.mightBe(TABLE)
if stmt.Into, err = p.parseTable(); err != nil {
return nil, err
}
if stmt.Echo, err = p.parseEcho(AFTER); err != nil {
return nil, err
}
if stmt.Timeout, err = p.parseTimeout(); err != nil {
return nil, err
}
return
}

View file

@ -22,202 +22,30 @@ import (
"time"
)
func (s *SelectStatement) Begin() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
s.killable.ticker = time.AfterFunc(s.Timeout, func() {
s.killable.ticker.Stop()
s.killable.ticker = nil
close(s.killable.closer)
})
}
func (s *SelectStatement) Cease() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
if s.killable.ticker != nil {
s.killable.ticker.Stop()
}
}
func (s *SelectStatement) Duration() time.Duration {
return s.Timeout
}
func (s *SelectStatement) Timedout() <-chan struct{} {
if s.Timeout == 0 {
return nil
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
return s.killable.closer
}
func (s *CreateStatement) Begin() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
s.killable.ticker = time.AfterFunc(s.Timeout, func() {
s.killable.ticker.Stop()
s.killable.ticker = nil
close(s.killable.closer)
})
}
func (s *CreateStatement) Cease() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
if s.killable.ticker != nil {
s.killable.ticker.Stop()
}
}
func (s *CreateStatement) Duration() time.Duration {
return s.Timeout
}
func (s *CreateStatement) Timedout() <-chan struct{} {
if s.Timeout == 0 {
return nil
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
return s.killable.closer
}
func (s *UpdateStatement) Begin() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
s.killable.ticker = time.AfterFunc(s.Timeout, func() {
s.killable.ticker.Stop()
s.killable.ticker = nil
close(s.killable.closer)
})
}
func (s *UpdateStatement) Cease() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
if s.killable.ticker != nil {
s.killable.ticker.Stop()
}
}
func (s *UpdateStatement) Duration() time.Duration {
return s.Timeout
}
func (s *UpdateStatement) Timedout() <-chan struct{} {
if s.Timeout == 0 {
return nil
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
return s.killable.closer
}
func (s *DeleteStatement) Begin() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
s.killable.ticker = time.AfterFunc(s.Timeout, func() {
s.killable.ticker.Stop()
s.killable.ticker = nil
close(s.killable.closer)
})
}
func (s *DeleteStatement) Cease() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
if s.killable.ticker != nil {
s.killable.ticker.Stop()
}
}
func (s *DeleteStatement) Duration() time.Duration {
return s.Timeout
}
func (s *DeleteStatement) Timedout() <-chan struct{} {
if s.Timeout == 0 {
return nil
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
return s.killable.closer
}
func (s *RelateStatement) Begin() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
s.killable.ticker = time.AfterFunc(s.Timeout, func() {
s.killable.ticker.Stop()
s.killable.ticker = nil
close(s.killable.closer)
})
}
func (s *RelateStatement) Cease() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
if s.killable.ticker != nil {
s.killable.ticker.Stop()
}
}
func (s *RelateStatement) Duration() time.Duration {
return s.Timeout
}
func (s *RelateStatement) Timedout() <-chan struct{} {
if s.Timeout == 0 {
return nil
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
return s.killable.closer
func (s *InsertStatement) Duration() time.Duration {
return s.Timeout
}
func (s *UpsertStatement) Duration() time.Duration {
return s.Timeout
}

View file

@ -20,44 +20,8 @@ import (
{{with $types := .}}{{range $k := $types}}
func (s *{{$k.name}}Statement) Begin() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
s.killable.ticker = time.AfterFunc(s.Timeout, func() {
s.killable.ticker.Stop()
s.killable.ticker = nil
close(s.killable.closer)
})
}
func (s *{{$k.name}}Statement) Cease() {
if s.Timeout == 0 {
return
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
if s.killable.ticker != nil {
s.killable.ticker.Stop()
}
}
func (s *{{$k.name}}Statement) Duration() time.Duration {
return s.Timeout
}
func (s *{{$k.name}}Statement) Timedout() <-chan struct{} {
if s.Timeout == 0 {
return nil
}
if s.killable.closer == nil {
s.killable.closer = make(chan struct{})
}
return s.killable.closer
}
{{end}}{{end}}

View file

@ -3,5 +3,7 @@
{ "name": "Create" },
{ "name": "Update" },
{ "name": "Delete" },
{ "name": "Relate" }
{ "name": "Relate" },
{ "name": "Insert" },
{ "name": "Upsert" }
]

65
sql/kill_test.go Normal file
View file

@ -0,0 +1,65 @@
// Code generated by https://github.com/abcum/tmpl
// Source file: kill.gen.go.tmpl
// DO NOT EDIT!
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
import (
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func TestKill(t *testing.T) {
Convey("SelectStatement should have duration", t, func() {
So((&SelectStatement{}).Duration(), ShouldEqual, 0)
So((&SelectStatement{Timeout: 1 * time.Second}).Duration(), ShouldEqual, 1*time.Second)
})
Convey("CreateStatement should have duration", t, func() {
So((&CreateStatement{}).Duration(), ShouldEqual, 0)
So((&CreateStatement{Timeout: 1 * time.Second}).Duration(), ShouldEqual, 1*time.Second)
})
Convey("UpdateStatement should have duration", t, func() {
So((&UpdateStatement{}).Duration(), ShouldEqual, 0)
So((&UpdateStatement{Timeout: 1 * time.Second}).Duration(), ShouldEqual, 1*time.Second)
})
Convey("DeleteStatement should have duration", t, func() {
So((&DeleteStatement{}).Duration(), ShouldEqual, 0)
So((&DeleteStatement{Timeout: 1 * time.Second}).Duration(), ShouldEqual, 1*time.Second)
})
Convey("RelateStatement should have duration", t, func() {
So((&RelateStatement{}).Duration(), ShouldEqual, 0)
So((&RelateStatement{Timeout: 1 * time.Second}).Duration(), ShouldEqual, 1*time.Second)
})
Convey("InsertStatement should have duration", t, func() {
So((&InsertStatement{}).Duration(), ShouldEqual, 0)
So((&InsertStatement{Timeout: 1 * time.Second}).Duration(), ShouldEqual, 1*time.Second)
})
Convey("UpsertStatement should have duration", t, func() {
So((&UpsertStatement{}).Duration(), ShouldEqual, 0)
So((&UpsertStatement{Timeout: 1 * time.Second}).Duration(), ShouldEqual, 1*time.Second)
})
}

View file

@ -14,8 +14,6 @@
package sql
import "time"
func (p *parser) parseLetStatement() (stmt *LetStatement, err error) {
stmt = &LetStatement{}
@ -51,31 +49,11 @@ func (p *parser) parseLetStatement() (stmt *LetStatement, err error) {
return nil, err
}
// If the defined paramater is a basic type,
// then instead of defining it at a later
// stage, convert it to that type here.
// If this query has any subqueries which
// need to alter the database then mark
// this query as a writeable statement.
switch stmt.What.(type) {
case bool, int64, float64, string:
p.v[stmt.Name.ID] = stmt.What
case []interface{}, map[string]interface{}:
p.v[stmt.Name.ID] = stmt.What
case time.Time, time.Duration:
p.v[stmt.Name.ID] = stmt.What
case Array, Object:
p.v[stmt.Name.ID] = stmt.What
case *Null, *Void, *Empty:
p.v[stmt.Name.ID] = stmt.What
case *Table, *Thing, *Param, *Ident, *Value:
p.v[stmt.Name.ID] = stmt.What
}
// Check that we have reached the end of the
// statement with either a ';' or EOF.
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
stmt.RW = p.buf.rw
return

71
sql/live.go Normal file
View file

@ -0,0 +1,71 @@
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
func (p *parser) parseLiveStatement() (stmt *LiveStatement, err error) {
stmt = &LiveStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthSC); err != nil {
return nil, err
}
_, _, err = p.shouldBe(SELECT)
if err != nil {
return nil, err
}
_, _, stmt.Diff = p.mightBe(DIFF)
if stmt.Diff == false {
if stmt.Expr, err = p.parseFields(); err != nil {
return nil, err
}
}
_, _, err = p.shouldBe(FROM)
if err != nil {
return nil, err
}
if stmt.What, err = p.parseTable(); err != nil {
return nil, err
}
if stmt.Cond, err = p.parseCond(); err != nil {
return nil, err
}
return
}
func (p *parser) parseKillStatement() (stmt *KillStatement, err error) {
stmt = &KillStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthSC); err != nil {
return nil, err
}
if stmt.Name, err = p.parseValue(); err != nil {
return nil, err
}
return
}

View file

@ -16,7 +16,7 @@ package sql
func (p *parser) parseDefineLoginStatement() (stmt *DefineLoginStatement, err error) {
stmt = &DefineLoginStatement{}
stmt = &DefineLoginStatement{RW: true}
if stmt.User, err = p.parseIdent(); err != nil {
return nil, err
@ -50,17 +50,13 @@ func (p *parser) parseDefineLoginStatement() (stmt *DefineLoginStatement, err er
return nil, err
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}
func (p *parser) parseRemoveLoginStatement() (stmt *RemoveLoginStatement, err error) {
stmt = &RemoveLoginStatement{}
stmt = &RemoveLoginStatement{RW: true}
if stmt.User, err = p.parseIdent(); err != nil {
return nil, err
@ -86,10 +82,6 @@ func (p *parser) parseRemoveLoginStatement() (stmt *RemoveLoginStatement, err er
}
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -16,7 +16,7 @@ package sql
func (p *parser) parseDefineNamespaceStatement() (stmt *DefineNamespaceStatement, err error) {
stmt = &DefineNamespaceStatement{}
stmt = &DefineNamespaceStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthKV); err != nil {
return nil, err
@ -32,7 +32,7 @@ func (p *parser) parseDefineNamespaceStatement() (stmt *DefineNamespaceStatement
func (p *parser) parseRemoveNamespaceStatement() (stmt *RemoveNamespaceStatement, err error) {
stmt = &RemoveNamespaceStatement{}
stmt = &RemoveNamespaceStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthKV); err != nil {
return nil, err

View file

@ -73,9 +73,15 @@ func (o *options) ns(ns string) (err error) {
// Check to see that the current user has
// the necessary authentication privileges
// to be able to specify this namespace.
// This is only run if we are using the
// KV, NS, or DB authentication levels, as
// SC authentication levels make use of
// table and field permissions instead.
if o.auth.Possible.NS != "*" && o.auth.Possible.NS != ns {
return &PermsError{Resource: ns}
if o.auth.Kind < cnf.Kind(AuthSC) {
if o.auth.Possible.NS != "*" && o.auth.Possible.NS != ns {
return &PermsError{Resource: ns}
}
}
// Specify the NS on the context session, so
@ -93,9 +99,15 @@ func (o *options) db(db string) (err error) {
// Check to see that the current user has
// the necessary authentication privileges
// to be able to specify this namespace.
// This is only run if we are using the
// KV, NS, or DB authentication levels, as
// SC authentication levels make use of
// table and field permissions instead.
if o.auth.Possible.DB != "*" && o.auth.Possible.DB != db {
return &PermsError{Resource: db}
if o.auth.Kind < cnf.Kind(AuthSC) {
if o.auth.Possible.DB != "*" && o.auth.Possible.DB != db {
return &PermsError{Resource: db}
}
}
// Specify the DB on the context session, so

View file

@ -29,9 +29,9 @@ type parser struct {
s *scanner
o *options
c *fibre.Context
v map[string]interface{}
buf struct {
n int // buffer size
rw bool // writeable
txn bool // inside txn
tok Token // last read token
lit string // last read literal
@ -40,51 +40,47 @@ type parser struct {
}
// Parse parses sql from a []byte, string, or io.Reader.
func Parse(c *fibre.Context, i interface{}, v map[string]interface{}) (*Query, error) {
func Parse(c *fibre.Context, i interface{}) (*Query, error) {
defer trace.FromContext(c.Context()).NewChild("sql.Parse").Finish()
if v == nil {
v = make(map[string]interface{})
}
switch x := i.(type) {
default:
return nil, &EmptyError{}
case []byte:
return parseBytes(c, x, v)
return parseBytes(c, x)
case string:
return parseString(c, x, v)
return parseString(c, x)
case io.Reader:
return parseBuffer(c, x, v)
return parseBuffer(c, x)
}
}
// newParser returns a new instance of Parser.
func newParser(c *fibre.Context, v map[string]interface{}) *parser {
return &parser{c: c, v: v, o: newOptions(c)}
func newParser(c *fibre.Context) *parser {
return &parser{c: c, o: newOptions(c)}
}
// parseBytes parses a byte array.
func parseBytes(c *fibre.Context, i []byte, v map[string]interface{}) (*Query, error) {
func parseBytes(c *fibre.Context, i []byte) (*Query, error) {
p := newParser(c)
r := bytes.NewReader(i)
p := newParser(c, v)
p.s = newScanner(p, r)
return p.parse()
}
// parseString parses a string.
func parseString(c *fibre.Context, i string, v map[string]interface{}) (*Query, error) {
func parseString(c *fibre.Context, i string) (*Query, error) {
p := newParser(c)
r := strings.NewReader(i)
p := newParser(c, v)
p.s = newScanner(p, r)
return p.parse()
}
// parseBuffer parses a buffer.
func parseBuffer(c *fibre.Context, r io.Reader, v map[string]interface{}) (*Query, error) {
p := newParser(c, v)
func parseBuffer(c *fibre.Context, r io.Reader) (*Query, error) {
p := newParser(c)
p.s = newScanner(p, r)
return p.parse()
}
@ -97,40 +93,91 @@ func (p *parser) parse() (*Query, error) {
// parseMulti parses multiple SQL SELECT statements.
func (p *parser) parseMulti() (*Query, error) {
var statements Statements
var semi bool
var text bool
var stmts Statements
for {
if tok, _, _ := p.scan(); tok == EOF {
if !text {
return nil, &EmptyError{}
// If the next token is an EOF then
// check to see if the query is empty
// or return the parsed statements.
if _, _, exi := p.mightBe(EOF); exi {
if len(stmts) == 0 {
return nil, new(EmptyError)
}
return &Query{Statements: statements}, nil
} else if !semi && tok == SEMICOLON {
semi = true
} else {
text = true
p.unscan()
s, err := p.parseSingle()
if err != nil {
return nil, err
}
statements = append(statements, s)
semi = false
return &Query{Statements: stmts}, nil
}
// If this is a multi statement query
// and there is no semicolon separating
// the statements, then return an error.
if len(stmts) > 0 {
switch semi {
case true:
_, _, exi := p.mightBe(SEMICOLON)
if exi {
continue
}
case false:
_, _, err := p.shouldBe(SEMICOLON)
if err != nil {
return nil, err
}
semi = true
continue
}
}
// Parse the next token as a statement
// and append it to the statements
// array for the current sql query.
stmt, err := p.parseSingle()
if err != nil {
return nil, err
}
stmts = append(stmts, stmt)
}
}
// parseSingle parses a single SQL SELECT statement.
func (p *parser) parseSingle() (Statement, error) {
func (p *parser) parseSingle() (stmt Statement, err error) {
tok, _, err := p.shouldBe(USE, INFO, BEGIN, CANCEL, COMMIT, LET, RETURN, SELECT, CREATE, UPDATE, DELETE, RELATE, DEFINE, REMOVE)
p.buf.rw = false
tok, _, err := p.shouldBe(
USE,
INFO,
BEGIN,
CANCEL,
COMMIT,
IF,
LET,
RETURN,
LIVE,
KILL,
SELECT,
CREATE,
UPDATE,
DELETE,
RELATE,
INSERT,
UPSERT,
DEFINE,
REMOVE,
)
switch tok {
case IF:
return p.parseIfStatement()
case USE:
return p.parseUseStatement()
@ -140,12 +187,18 @@ func (p *parser) parseSingle() (Statement, error) {
case INFO:
return p.parseInfoStatement()
case LIVE:
return p.parseLiveStatement()
case KILL:
return p.parseKillStatement()
case BEGIN:
return p.parseBeginStatement()
case CANCEL:
return p.parseCancelStatement()
case COMMIT:
return p.parseCommitStatement()
case RETURN:
return p.parseReturnStatement()
@ -160,6 +213,11 @@ func (p *parser) parseSingle() (Statement, error) {
case RELATE:
return p.parseRelateStatement()
case INSERT:
return p.parseInsertStatement()
case UPSERT:
return p.parseUpsertStatement()
case DEFINE:
return p.parseDefineStatement()
case REMOVE:

View file

@ -21,7 +21,6 @@ func (p *parser) parsePerms() (exp *PermExpression, err error) {
Create: false,
Update: false,
Delete: false,
Relate: false,
}
tok, _, err := p.shouldBe(FOR, NONE, FULL, WHERE)
@ -48,7 +47,6 @@ func (p *parser) parsePerms() (exp *PermExpression, err error) {
exp.Create = expr
exp.Update = expr
exp.Delete = expr
exp.Relate = expr
return
@ -62,7 +60,7 @@ func (p *parser) parsePerms() (exp *PermExpression, err error) {
var when []Token
for {
tok, _, err := p.shouldBe(SELECT, CREATE, UPDATE, DELETE, RELATE)
tok, _, err := p.shouldBe(SELECT, CREATE, UPDATE, DELETE)
if err != nil {
return exp, err
}
@ -98,8 +96,6 @@ func (p *parser) parsePerms() (exp *PermExpression, err error) {
exp.Update = expr
case DELETE:
exp.Delete = expr
case RELATE:
exp.Relate = expr
}
}

143
sql/rdwr.gen.go Normal file
View file

@ -0,0 +1,143 @@
// Code generated by https://github.com/abcum/tmpl
// Source file: rdwr.gen.go.tmpl
// DO NOT EDIT!
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
func (s *InfoStatement) Writeable() bool {
return s.RW
}
func (s *IfStatement) Writeable() bool {
return s.RW
}
func (s *LetStatement) Writeable() bool {
return s.RW
}
func (s *ReturnStatement) Writeable() bool {
return s.RW
}
func (s *LiveStatement) Writeable() bool {
return s.RW
}
func (s *KillStatement) Writeable() bool {
return s.RW
}
func (s *SelectStatement) Writeable() bool {
return s.RW
}
func (s *CreateStatement) Writeable() bool {
return s.RW
}
func (s *UpdateStatement) Writeable() bool {
return s.RW
}
func (s *DeleteStatement) Writeable() bool {
return s.RW
}
func (s *RelateStatement) Writeable() bool {
return s.RW
}
func (s *InsertStatement) Writeable() bool {
return s.RW
}
func (s *UpsertStatement) Writeable() bool {
return s.RW
}
func (s *DefineNamespaceStatement) Writeable() bool {
return s.RW
}
func (s *RemoveNamespaceStatement) Writeable() bool {
return s.RW
}
func (s *DefineDatabaseStatement) Writeable() bool {
return s.RW
}
func (s *RemoveDatabaseStatement) Writeable() bool {
return s.RW
}
func (s *DefineLoginStatement) Writeable() bool {
return s.RW
}
func (s *RemoveLoginStatement) Writeable() bool {
return s.RW
}
func (s *DefineTokenStatement) Writeable() bool {
return s.RW
}
func (s *RemoveTokenStatement) Writeable() bool {
return s.RW
}
func (s *DefineScopeStatement) Writeable() bool {
return s.RW
}
func (s *RemoveScopeStatement) Writeable() bool {
return s.RW
}
func (s *DefineTableStatement) Writeable() bool {
return s.RW
}
func (s *RemoveTableStatement) Writeable() bool {
return s.RW
}
func (s *DefineEventStatement) Writeable() bool {
return s.RW
}
func (s *RemoveEventStatement) Writeable() bool {
return s.RW
}
func (s *DefineFieldStatement) Writeable() bool {
return s.RW
}
func (s *RemoveFieldStatement) Writeable() bool {
return s.RW
}
func (s *DefineIndexStatement) Writeable() bool {
return s.RW
}
func (s *RemoveIndexStatement) Writeable() bool {
return s.RW
}

View file

@ -14,19 +14,10 @@
package sql
func (p *parser) parseType() (exp string, err error) {
allowed := []string{"any", "url", "uuid", "color", "email", "phone", "array", "object", "domain", "record", "string", "number", "double", "custom", "boolean", "password", "datetime", "latitude", "longitude"}
_, lit, err := p.shouldBe(IDENT)
if err != nil {
return string(""), &ParseError{Found: lit, Expected: allowed}
}
if !p.contains(lit, allowed) {
return string(""), &ParseError{Found: lit, Expected: allowed}
}
return lit, err
{{with $types := .}}{{range $k := $types}}
func (s *{{$k.name}}Statement) Writeable() bool {
return s.RW
}
{{end}}{{end}}

36
sql/rdwr.gen.json Normal file
View file

@ -0,0 +1,36 @@
[
{ "name": "Info" },
{ "name": "If" },
{ "name": "Let" },
{ "name": "Return" },
{ "name": "Live" },
{ "name": "Kill" },
{ "name": "Select" },
{ "name": "Create" },
{ "name": "Update" },
{ "name": "Delete" },
{ "name": "Relate" },
{ "name": "Insert" },
{ "name": "Upsert" },
{ "name": "DefineNamespace" },
{ "name": "RemoveNamespace" },
{ "name": "DefineDatabase" },
{ "name": "RemoveDatabase" },
{ "name": "DefineLogin" },
{ "name": "RemoveLogin" },
{ "name": "DefineToken" },
{ "name": "RemoveToken" },
{ "name": "DefineScope" },
{ "name": "RemoveScope" },
{ "name": "DefineTable" },
{ "name": "RemoveTable" },
{ "name": "DefineEvent" },
{ "name": "RemoveEvent" },
{ "name": "DefineField" },
{ "name": "RemoveField" },
{ "name": "DefineIndex" },
{ "name": "RemoveIndex" }
]

215
sql/rdwr_test.go Normal file
View file

@ -0,0 +1,215 @@
// Code generated by https://github.com/abcum/tmpl
// Source file: auth.gen.go.tmpl
// DO NOT EDIT!
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestRdwr(t *testing.T) {
Convey("InfoStatement should auth correctly", t, func() {
s := &InfoStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("IfStatement should auth correctly", t, func() {
s := &IfStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("LetStatement should auth correctly", t, func() {
s := &LetStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("ReturnStatement should auth correctly", t, func() {
s := &ReturnStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("LiveStatement should auth correctly", t, func() {
s := &LiveStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("KillStatement should auth correctly", t, func() {
s := &KillStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("SelectStatement should auth correctly", t, func() {
s := &SelectStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("CreateStatement should auth correctly", t, func() {
s := &CreateStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("UpdateStatement should auth correctly", t, func() {
s := &UpdateStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DeleteStatement should auth correctly", t, func() {
s := &DeleteStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RelateStatement should auth correctly", t, func() {
s := &RelateStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("InsertStatement should auth correctly", t, func() {
s := &InsertStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("UpsertStatement should auth correctly", t, func() {
s := &UpsertStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineNamespaceStatement should auth correctly", t, func() {
s := &DefineNamespaceStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveNamespaceStatement should auth correctly", t, func() {
s := &RemoveNamespaceStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineDatabaseStatement should auth correctly", t, func() {
s := &DefineDatabaseStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveDatabaseStatement should auth correctly", t, func() {
s := &RemoveDatabaseStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineLoginStatement should auth correctly", t, func() {
s := &DefineLoginStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveLoginStatement should auth correctly", t, func() {
s := &RemoveLoginStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineTokenStatement should auth correctly", t, func() {
s := &DefineTokenStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveTokenStatement should auth correctly", t, func() {
s := &RemoveTokenStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineScopeStatement should auth correctly", t, func() {
s := &DefineScopeStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveScopeStatement should auth correctly", t, func() {
s := &RemoveScopeStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineTableStatement should auth correctly", t, func() {
s := &DefineTableStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveTableStatement should auth correctly", t, func() {
s := &RemoveTableStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineEventStatement should auth correctly", t, func() {
s := &DefineEventStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveEventStatement should auth correctly", t, func() {
s := &RemoveEventStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineFieldStatement should auth correctly", t, func() {
s := &DefineFieldStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveFieldStatement should auth correctly", t, func() {
s := &RemoveFieldStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("DefineIndexStatement should auth correctly", t, func() {
s := &DefineIndexStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
Convey("RemoveIndexStatement should auth correctly", t, func() {
s := &RemoveIndexStatement{RW: true}
w := s.Writeable()
So(w, ShouldEqual, true)
})
}

View file

@ -16,9 +16,9 @@ package sql
func (p *parser) parseRelateStatement() (stmt *RelateStatement, err error) {
stmt = &RelateStatement{}
stmt = &RelateStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthSC); err != nil {
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNO); err != nil {
return nil, err
}
@ -56,10 +56,6 @@ func (p *parser) parseRelateStatement() (stmt *RelateStatement, err error) {
return nil, err
}
if _, _, err = p.shouldBe(EOF, RPAREN, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -17,7 +17,7 @@ package sql
func (p *parser) parseRemoveStatement() (Statement, error) {
// Inspect the next token.
tok, _, err := p.shouldBe(NAMESPACE, DATABASE, LOGIN, TOKEN, SCOPE, TABLE, FIELD, INDEX, VIEW)
tok, _, err := p.shouldBe(NAMESPACE, DATABASE, LOGIN, TOKEN, SCOPE, TABLE, EVENT, FIELD, INDEX)
switch tok {
case NAMESPACE:
@ -32,12 +32,12 @@ func (p *parser) parseRemoveStatement() (Statement, error) {
return p.parseRemoveScopeStatement()
case TABLE:
return p.parseRemoveTableStatement()
case EVENT:
return p.parseRemoveEventStatement()
case FIELD:
return p.parseRemoveFieldStatement()
case INDEX:
return p.parseRemoveIndexStatement()
case VIEW:
return p.parseRemoveViewStatement()
default:
return nil, err
}

View file

@ -26,17 +26,16 @@ func (p *parser) parseReturnStatement() (stmt *ReturnStatement, err error) {
// including a parenthesised expression or a
// binary expression so handle accordingly.
stmt.What, err = p.parseExpr()
stmt.What, err = p.parseWhat()
if err != nil {
return nil, err
}
// Check that we have reached the end of the
// statement with either a ';' or EOF.
// If this query has any subqueries which
// need to alter the database then mark
// this query as a writeable statement.
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
stmt.RW = p.buf.rw
return

View file

@ -17,7 +17,6 @@ package sql
import (
"bufio"
"bytes"
"fmt"
"io"
"regexp"
"strconv"
@ -51,7 +50,7 @@ func (s *scanner) scan() (tok Token, lit string, val interface{}) {
// If we see a letter then consume as a string.
if isLetter(ch) {
return s.scanIdent(ch)
return s.scanIdiom(ch)
}
// If we see a number then consume as a number.
@ -351,7 +350,11 @@ func (s *scanner) scanCommentMultiple(chp ...rune) (tok Token, lit string, val i
func (s *scanner) scanParams(chp ...rune) (tok Token, lit string, val interface{}) {
tok, lit, _ = s.scanIdent()
tok, lit, _ = s.scanIdiom()
if s.p.is(tok, THING) {
return ILLEGAL, lit, val
}
if s.p.is(tok, REGION) {
return ILLEGAL, lit, val
@ -367,6 +370,49 @@ func (s *scanner) scanParams(chp ...rune) (tok Token, lit string, val interface{
func (s *scanner) scanQuoted(chp ...rune) (tok Token, lit string, val interface{}) {
var tbv string
var idv interface{}
// Create a buffer
var buf bytes.Buffer
tok, lit, _ = s.scanString(chp...)
if s.p.is(tok, REGION) {
return ILLEGAL, lit, val
}
if s.p.is(tok, ILLEGAL) {
return ILLEGAL, lit, val
}
if ch := s.next(); ch == ':' {
tbv = lit
buf.WriteString(lit)
buf.WriteRune(ch)
if tok, lit, idv = s.part(); tok == ILLEGAL {
buf.WriteString(lit)
return ILLEGAL, buf.String(), val
} else {
buf.WriteString(lit)
}
return THING, buf.String(), NewThing(tbv, idv)
} else if ch != eof {
s.undo()
}
return IDENT, lit, val
}
func (s *scanner) scanSection(chp ...rune) (tok Token, lit string, val interface{}) {
tok, lit, _ = s.scanString(chp...)
if s.p.is(tok, REGION) {
@ -394,6 +440,48 @@ func (s *scanner) scanIdent(chp ...rune) (tok Token, lit string, val interface{}
buf.WriteRune(ch)
}
// Read subsequent characters
for {
if ch := s.next(); ch == eof {
break
} else if isIdentChar(ch) {
buf.WriteRune(ch)
} else {
s.undo()
break
}
}
// If the string matches a keyword then return that keyword.
if tok := keywords[strings.ToUpper(buf.String())]; tok > 0 {
return tok, buf.String(), val
}
if val, err := time.ParseDuration(buf.String()); err == nil {
return DURATION, buf.String(), val
}
// Otherwise return as a regular identifier.
return tok, buf.String(), val
}
// scanIdiom consumes the current rune and all contiguous ident runes.
func (s *scanner) scanIdiom(chp ...rune) (tok Token, lit string, val interface{}) {
tok = IDENT
var tbv string
var idv interface{}
// Create a buffer
var buf bytes.Buffer
// Read passed in runes
for _, ch := range chp {
buf.WriteRune(ch)
}
// Read subsequent characters
for {
if ch := s.next(); ch == eof {
@ -403,6 +491,26 @@ func (s *scanner) scanIdent(chp ...rune) (tok Token, lit string, val interface{}
} else if isExprsChar(ch) {
tok = EXPR
buf.WriteRune(ch)
} else if ch == ':' {
if tok == EXPR {
s.undo()
break
}
tbv = buf.String()
buf.WriteRune(ch)
if tok, lit, idv = s.part(); tok == ILLEGAL {
buf.WriteString(lit)
return ILLEGAL, buf.String(), val
} else {
buf.WriteString(lit)
}
return THING, buf.String(), NewThing(tbv, idv)
} else {
s.undo()
break
@ -640,6 +748,16 @@ func (s *scanner) scanNumber(chp ...rune) (tok Token, lit string, val interface{
tok = IDENT
buf.WriteRune(ch)
switch ch {
case 'e', 'E':
if chn := s.next(); chn == '+' {
tok = DOUBLE
buf.WriteRune(chn)
} else if ch == '-' {
tok = DOUBLE
buf.WriteRune(chn)
} else {
s.undo()
}
case 's', 'h', 'd', 'w':
tok = DURATION
case 'n', 'u', 'µ', 'm':
@ -692,10 +810,6 @@ func (s *scanner) scanString(chp ...rune) (tok Token, lit string, val interface{
end = '⟩'
}
if beg == '{' {
end = '}'
}
tok = STRING
// Create a buffer
@ -744,14 +858,6 @@ func (s *scanner) scanString(chp ...rune) (tok Token, lit string, val interface{
return TIME, buf.String(), val.UTC()
}
if val, err := time.Parse(RFCNorm, buf.String()); err == nil {
return TIME, buf.String(), val.UTC()
}
if val, err := time.Parse(RFCText, buf.String()); err == nil {
return TIME, buf.String(), val.UTC()
}
return tok, buf.String(), val
}
@ -850,6 +956,10 @@ func (s *scanner) scanObject(chp ...rune) (tok Token, lit string, val interface{
}
func (s *scanner) scanPart() {
}
func (s *scanner) part() (tok Token, lit string, val interface{}) {
if ch := s.next(); isLetter(ch) {
@ -857,33 +967,15 @@ func (s *scanner) part() (tok Token, lit string, val interface{}) {
} else if isNumber(ch) {
tok, lit, _ = s.scanNumber(ch)
} else if ch == '`' {
tok, lit, _ = s.scanQuoted(ch)
} else if ch == '{' {
tok, lit, _ = s.scanQuoted(ch)
tok, lit, _ = s.scanSection(ch)
} else if ch == '⟨' {
tok, lit, _ = s.scanQuoted(ch)
} else if ch == '$' {
tok, lit, _ = s.scanParams(ch)
if p, ok := s.p.v[lit]; ok {
switch v := p.(type) {
case bool, int64, float64, string:
val, lit = v, fmt.Sprint(v)
case *Ident:
lit = v.ID
case *Value:
lit = v.ID
default:
tok = ILLEGAL
}
} else {
tok = ILLEGAL
}
tok, lit, _ = s.scanSection(ch)
} else {
s.undo()
tok = ILLEGAL
}
if tok != IDENT && tok != PARAM && tok != NUMBER && tok != DOUBLE {
if tok != IDENT && tok != NUMBER && tok != DOUBLE {
tok = ILLEGAL
}

View file

@ -16,7 +16,7 @@ package sql
func (p *parser) parseDefineScopeStatement() (stmt *DefineScopeStatement, err error) {
stmt = &DefineScopeStatement{}
stmt = &DefineScopeStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -28,7 +28,7 @@ func (p *parser) parseDefineScopeStatement() (stmt *DefineScopeStatement, err er
for {
tok, _, exi := p.mightBe(SESSION, SIGNUP, SIGNIN)
tok, _, exi := p.mightBe(SESSION, SIGNUP, SIGNIN, CONNECT)
if !exi {
break
}
@ -53,10 +53,13 @@ func (p *parser) parseDefineScopeStatement() (stmt *DefineScopeStatement, err er
}
}
}
if p.is(tok, CONNECT) {
_, _, _ = p.mightBe(AS)
if stmt.Connect, err = p.parseExpr(); err != nil {
return nil, err
}
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
@ -65,7 +68,7 @@ func (p *parser) parseDefineScopeStatement() (stmt *DefineScopeStatement, err er
func (p *parser) parseRemoveScopeStatement() (stmt *RemoveScopeStatement, err error) {
stmt = &RemoveScopeStatement{}
stmt = &RemoveScopeStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -75,10 +78,6 @@ func (p *parser) parseRemoveScopeStatement() (stmt *RemoveScopeStatement, err er
return nil, err
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -18,7 +18,7 @@ func (p *parser) parseSelectStatement() (stmt *SelectStatement, err error) {
stmt = &SelectStatement{}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthSC); err != nil {
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNO); err != nil {
return nil, err
}
@ -63,19 +63,22 @@ func (p *parser) parseSelectStatement() (stmt *SelectStatement, err error) {
return nil, err
}
if _, _, err = p.shouldBe(EOF, RPAREN, SEMICOLON); err != nil {
if err = checkExpression(aggrs, stmt.Expr, stmt.Group); err != nil {
return nil, err
}
return stmt, nil
// If this query has any subqueries which
// need to alter the database then mark
// this query as a writeable statement.
stmt.RW = p.buf.rw
return
}
func (p *parser) parseFields() (mul Fields, err error) {
var lit string
var exi bool
for {
one := &Field{}
@ -89,10 +92,25 @@ func (p *parser) parseFields() (mul Fields, err error) {
// clause, and if it is read the defined
// field alias name from the scanner.
if _, _, exi = p.mightBe(AS); exi {
if _, _, exi := p.mightBe(AS); exi {
if one.Alias, err = p.parseIdent(); err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"alias name"}}
if _, one.Alias, err = p.shouldBe(IDENT); err != nil {
return nil, &ParseError{Found: one.Alias, Expected: []string{"alias name"}}
}
one.Field = one.Alias
} else {
switch v := one.Expr.(type) {
case *Param:
one.Field = v.ID
case *Value:
one.Field = v.ID
case *Ident:
one.Field = v.ID
default:
one.Field = one.String()
}
}
@ -106,7 +124,7 @@ func (p *parser) parseFields() (mul Fields, err error) {
// and if not, then break out of the loop,
// otherwise repeat until we find no comma.
if _, _, exi = p.mightBe(COMMA); !exi {
if _, _, exi := p.mightBe(COMMA); !exi {
break
}
@ -116,20 +134,6 @@ func (p *parser) parseFields() (mul Fields, err error) {
}
func (p *parser) parseWhere() (exp Expr, err error) {
// The next token that we expect to see is a
// WHERE token, and if we don't find one then
// return nil, with no error.
if _, _, exi := p.mightBe(WHERE); !exi {
return nil, nil
}
return p.parseExpr()
}
func (p *parser) parseGroup() (mul Groups, err error) {
// The next token that we expect to see is a
@ -153,7 +157,7 @@ func (p *parser) parseGroup() (mul Groups, err error) {
one := &Group{}
tok, lit, err = p.shouldBe(IDENT, ID)
tok, lit, err = p.shouldBe(IDENT, EXPR)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
}
@ -206,23 +210,38 @@ func (p *parser) parseOrder() (mul Orders, err error) {
one := &Order{}
tok, lit, err = p.shouldBe(IDENT, ID)
tok, lit, err = p.shouldBe(IDENT, EXPR, RAND)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"field name"}}
}
one.Expr, err = p.declare(tok, lit)
if err != nil {
return nil, err
switch tok {
default:
one.Expr, err = p.declare(tok, lit)
if err != nil {
return nil, err
}
case RAND:
one.Expr = &FuncExpression{Name: "rand"}
if _, _, exi = p.mightBe(LPAREN); exi {
_, _, err = p.shouldBe(RPAREN)
if err != nil {
return nil, err
}
}
}
if tok, lit, exi = p.mightBe(ASC, DESC); !exi {
tok = ASC
if _, _, exi = p.mightBe(COLLATE); exi {
one.Tag, err = p.parseLanguage()
if err != nil {
return nil, err
}
}
one.Dir, err = p.declare(tok, lit)
if err != nil {
return nil, err
if tok, _, exi = p.mightBe(ASC, DESC); exi {
one.Dir = (tok == ASC)
} else {
one.Dir = true
}
// Append the single expression to the array
@ -302,7 +321,7 @@ func (p *parser) parseVersion() (Expr, error) {
tok, lit, err := p.shouldBe(DATE, TIME, PARAM)
if err != nil {
return nil, &ParseError{Found: lit, Expected: []string{"timestamp"}}
return nil, &ParseError{Found: lit, Expected: []string{"version date or time"}}
}
return p.declare(tok, lit)

View file

@ -30,3 +30,22 @@ const (
// Parsing format for readable text format date times
RFCText = "2006-01-02 15:04:05.999999999 -0700 MST"
)
var (
allowedTypes = []string{
"array", "boolean", "circle",
"color", "datetime", "domain",
"double", "email", "latitude",
"longitude", "number", "object",
"password", "phone", "point",
"polygon", "record", "string",
"url", "uuid",
}
allowedAlgorithms = []string{
"ES256", "ES384", "ES512",
"HS256", "HS384", "HS512",
"PS256", "PS384", "PS512",
"RS256", "RS384", "RS512",
}
)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@ package sql
func (p *parser) parseDefineTableStatement() (stmt *DefineTableStatement, err error) {
stmt = &DefineTableStatement{}
stmt = &DefineTableStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -28,11 +28,15 @@ func (p *parser) parseDefineTableStatement() (stmt *DefineTableStatement, err er
for {
tok, _, exi := p.mightBe(SCHEMAFULL, SCHEMALESS, PERMISSIONS)
tok, _, exi := p.mightBe(DROP, SCHEMAFULL, SCHEMALESS, PERMISSIONS, AS)
if !exi {
break
}
if p.is(tok, DROP) {
stmt.Drop = true
}
if p.is(tok, SCHEMAFULL) {
stmt.Full = true
}
@ -42,15 +46,51 @@ func (p *parser) parseDefineTableStatement() (stmt *DefineTableStatement, err er
}
if p.is(tok, PERMISSIONS) {
if stmt.Perm, err = p.parsePerms(); err != nil {
if stmt.Perms, err = p.parsePerms(); err != nil {
return nil, err
}
}
}
if p.is(tok, AS) {
stmt.Lock = true
_, _, _ = p.mightBe(LPAREN)
_, _, err = p.shouldBe(SELECT)
if err != nil {
return nil, err
}
if stmt.Expr, err = p.parseFields(); err != nil {
return nil, err
}
_, _, err = p.shouldBe(FROM)
if err != nil {
return nil, err
}
if stmt.From, err = p.parseTables(); err != nil {
return nil, err
}
if stmt.Cond, err = p.parseCond(); err != nil {
return nil, err
}
if stmt.Group, err = p.parseGroup(); err != nil {
return nil, err
}
_, _, _ = p.mightBe(RPAREN)
if err = checkExpression(rolls, stmt.Expr, stmt.Group); err != nil {
return nil, err
}
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
@ -59,7 +99,7 @@ func (p *parser) parseDefineTableStatement() (stmt *DefineTableStatement, err er
func (p *parser) parseRemoveTableStatement() (stmt *RemoveTableStatement, err error) {
stmt = &RemoveTableStatement{}
stmt = &RemoveTableStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthDB); err != nil {
return nil, err
@ -69,10 +109,6 @@ func (p *parser) parseRemoveTableStatement() (stmt *RemoveTableStatement, err er
return nil, err
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -16,7 +16,7 @@ package sql
func (p *parser) parseDefineTokenStatement() (stmt *DefineTokenStatement, err error) {
stmt = &DefineTokenStatement{}
stmt = &DefineTokenStatement{RW: true}
if stmt.Name, err = p.parseIdent(); err != nil {
return nil, err
@ -70,15 +70,11 @@ func (p *parser) parseDefineTokenStatement() (stmt *DefineTokenStatement, err er
}
if stmt.Type == "" {
return nil, &ParseError{Found: ";", Expected: []string{"TYPE"}}
return nil, &ParseError{Found: "", Expected: []string{"TYPE"}}
}
if stmt.Code == nil {
return nil, &ParseError{Found: ";", Expected: []string{"VALUE"}}
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
if len(stmt.Code) == 0 {
return nil, &ParseError{Found: "", Expected: []string{"VALUE"}}
}
return
@ -87,7 +83,7 @@ func (p *parser) parseDefineTokenStatement() (stmt *DefineTokenStatement, err er
func (p *parser) parseRemoveTokenStatement() (stmt *RemoveTokenStatement, err error) {
stmt = &RemoveTokenStatement{}
stmt = &RemoveTokenStatement{RW: true}
if stmt.Name, err = p.parseIdent(); err != nil {
return nil, err
@ -119,10 +115,6 @@ func (p *parser) parseRemoveTokenStatement() (stmt *RemoveTokenStatement, err er
}
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -91,26 +91,26 @@ const (
operatorEnd
// literals
// keywords
keywordsBeg
ACCEPT
AFTER
ALL
ALLCONTAINEDIN
AND
AS
ASC
ASSERT
AT
BEFORE
BEGIN
BOTH
BY
CANCEL
CODE
COLLATE
COLUMNS
COMMIT
CONNECT
CONTAINS
CONTAINSALL
CONTAINSNONE
@ -119,15 +119,15 @@ const (
CREATE
DATABASE
DB
DEFAULT
DEFINE
DELETE
DESC
DIFF
DISTINCT
DROP
ELSE
EMPTY
ENUM
EXISTS
END
EVENT
EXPUNGE
FALSE
FIELD
@ -135,38 +135,34 @@ const (
FROM
FULL
GROUP
ID
IF
IN
INDEX
INFO
INSERT
INTO
IS
KILL
LET
LIMIT
LIVE
LOGIN
MANDATORY
MATCH
MAX
MERGE
MIN
MISSING
NAMESPACE
NONE
NONECONTAINEDIN
NOT
NOTNULL
NOW
NS
NULL
OFFSET
NUMERIC
ON
OR
ORDER
PASSWORD
PERMISSIONS
READONLY
REJECT
RAND
RELATE
REMOVE
RETURN
@ -181,6 +177,7 @@ const (
SOMECONTAINEDIN
START
TABLE
THEN
TIMEOUT
TO
TOKEN
@ -191,11 +188,10 @@ const (
UPDATE
UPSERT
USE
VALIDATE
VALUE
VERSION
VIEW
VOID
WHEN
WHERE
WITH
@ -266,22 +262,22 @@ var tokens = [...]string{
// keywords
ACCEPT: "ACCEPT",
AFTER: "AFTER",
ALL: "ALL",
ALLCONTAINEDIN: "ALLCONTAINEDIN",
AND: "AND",
AS: "AS",
ASC: "ASC",
ASSERT: "ASSERT",
AT: "AT",
BEFORE: "BEFORE",
BEGIN: "BEGIN",
BOTH: "BOTH",
BY: "BY",
CANCEL: "CANCEL",
CODE: "CODE",
COLLATE: "COLLATE",
COLUMNS: "COLUMNS",
COMMIT: "COMMIT",
CONNECT: "CONNECT",
CONTAINS: "CONTAINS",
CONTAINSALL: "CONTAINSALL",
CONTAINSNONE: "CONTAINSNONE",
@ -290,15 +286,15 @@ var tokens = [...]string{
CREATE: "CREATE",
DATABASE: "DATABASE",
DB: "DB",
DEFAULT: "DEFAULT",
DEFINE: "DEFINE",
DELETE: "DELETE",
DESC: "DESC",
DIFF: "DIFF",
DISTINCT: "DISTINCT",
DROP: "DROP",
ELSE: "ELSE",
EMPTY: "EMPTY",
ENUM: "ENUM",
EXISTS: "EXISTS",
END: "END",
EVENT: "EVENT",
EXPUNGE: "EXPUNGE",
FALSE: "FALSE",
FIELD: "FIELD",
@ -306,37 +302,34 @@ var tokens = [...]string{
FROM: "FROM",
FULL: "FULL",
GROUP: "GROUP",
ID: "ID",
IF: "IF",
IN: "IN",
INDEX: "INDEX",
INFO: "INFO",
INSERT: "INSERT",
INTO: "INTO",
IS: "IS",
KILL: "KILL",
LET: "LET",
LIMIT: "LIMIT",
LIVE: "LIVE",
LOGIN: "LOGIN",
MANDATORY: "MANDATORY",
MATCH: "MATCH",
MAX: "MAX",
MERGE: "MERGE",
MIN: "MIN",
MISSING: "MISSING",
NAMESPACE: "NAMESPACE",
NONE: "NONE",
NONECONTAINEDIN: "NONECONTAINEDIN",
NOT: "NOT",
NOTNULL: "NOTNULL",
NOW: "NOW",
NS: "NS",
NULL: "NULL",
NUMERIC: "NUMERIC",
ON: "ON",
OR: "OR",
ORDER: "ORDER",
PASSWORD: "PASSWORD",
PERMISSIONS: "PERMISSIONS",
READONLY: "READONLY",
REJECT: "REJECT",
RAND: "RAND",
RELATE: "RELATE",
REMOVE: "REMOVE",
RETURN: "RETURN",
@ -351,6 +344,7 @@ var tokens = [...]string{
SOMECONTAINEDIN: "SOMECONTAINEDIN",
START: "START",
TABLE: "TABLE",
THEN: "THEN",
TIMEOUT: "TIMEOUT",
TO: "TO",
TOKEN: "TOKEN",
@ -361,11 +355,10 @@ var tokens = [...]string{
UPDATE: "UPDATE",
UPSERT: "UPSERT",
USE: "USE",
VALIDATE: "VALIDATE",
VALUE: "VALUE",
VERSION: "VERSION",
VIEW: "VIEW",
VOID: "VOID",
WHEN: "WHEN",
WHERE: "WHERE",
WITH: "WITH",
}
@ -430,10 +423,12 @@ func (tok Token) String() string {
return ""
}
func newToken(str string) Token {
func newToken(s string) Token {
for k, v := range tokens {
if v == strings.ToUpper(str) {
return Token(k)
if len(v) == len(s) {
if strings.EqualFold(v, s) {
return Token(k)
}
}
}
return ILLEGAL

View file

@ -20,10 +20,6 @@ func (p *parser) parseBeginStatement() (stmt *BeginStatement, err error) {
_, _, _ = p.mightBe(TRANSACTION)
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
p.buf.txn = true
return
@ -36,10 +32,6 @@ func (p *parser) parseCancelStatement() (stmt *CancelStatement, err error) {
_, _, _ = p.mightBe(TRANSACTION)
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
p.buf.txn = false
return
@ -52,10 +44,6 @@ func (p *parser) parseCommitStatement() (stmt *CommitStatement, err error) {
_, _, _ = p.mightBe(TRANSACTION)
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
p.buf.txn = false
return

View file

@ -16,19 +16,12 @@ package sql
func (p *parser) parseUpdateStatement() (stmt *UpdateStatement, err error) {
stmt = &UpdateStatement{}
stmt = &UpdateStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthSC); err != nil {
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNO); err != nil {
return nil, err
}
if _, _, exi := p.mightBe(AND); exi {
if _, _, err = p.shouldBe(UPSERT); err != nil {
return nil, err
}
stmt.Hard = true
}
if stmt.What, err = p.parseWhat(); err != nil {
return nil, err
}
@ -49,10 +42,6 @@ func (p *parser) parseUpdateStatement() (stmt *UpdateStatement, err error) {
return nil, err
}
if _, _, err = p.shouldBe(EOF, RPAREN, SEMICOLON); err != nil {
return nil, err
}
return
}

49
sql/upsert.go Normal file
View file

@ -0,0 +1,49 @@
// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
func (p *parser) parseUpsertStatement() (stmt *UpsertStatement, err error) {
stmt = &UpsertStatement{RW: true}
if stmt.KV, stmt.NS, stmt.DB, err = p.o.get(AuthNO); err != nil {
return nil, err
}
if stmt.Data, err = p.parseExpr(); err != nil {
return nil, err
}
if _, _, err = p.shouldBe(INTO); err != nil {
return nil, err
}
_, _, _ = p.mightBe(TABLE)
if stmt.Into, err = p.parseTable(); err != nil {
return nil, err
}
if stmt.Echo, err = p.parseEcho(AFTER); err != nil {
return nil, err
}
if stmt.Timeout, err = p.parseTimeout(); err != nil {
return nil, err
}
return
}

View file

@ -21,22 +21,27 @@ func (p *parser) parseUseStatement() (stmt *UseStatement, err error) {
var tok Token
var exi bool
tok, _, err = p.shouldBe(NAMESPACE, NS, DATABASE, DB)
if p.buf.txn {
return nil, &TransError{}
}
tok, _, err = p.shouldBe(NAMESPACE, DATABASE, NS, DB)
if err != nil {
return nil, err
}
for {
var val *Ident
if p.is(tok, NAMESPACE, NS) {
if val, err = p.parseIdent(); err != nil {
return nil, err
_, stmt.NS, err = p.shouldBe(IDENT, STRING, NUMBER, DOUBLE, DATE, TIME)
if err != nil {
return
}
stmt.NS = val.ID
if len(stmt.NS) == 0 {
return nil, &ParseError{Expected: []string{"namespace name"}, Found: stmt.NS}
}
if err = p.o.ns(stmt.NS); err != nil {
return nil, err
@ -46,11 +51,14 @@ func (p *parser) parseUseStatement() (stmt *UseStatement, err error) {
if p.is(tok, DATABASE, DB) {
if val, err = p.parseIdent(); err != nil {
return nil, err
_, stmt.DB, err = p.shouldBe(IDENT, STRING, NUMBER, DOUBLE, DATE, TIME)
if err != nil {
return
}
stmt.DB = val.ID
if len(stmt.DB) == 0 {
return nil, &ParseError{Expected: []string{"database name"}, Found: stmt.DB}
}
if err = p.o.db(stmt.DB); err != nil {
return nil, err
@ -65,10 +73,6 @@ func (p *parser) parseUseStatement() (stmt *UseStatement, err error) {
}
if _, _, err = p.shouldBe(EOF, SEMICOLON); err != nil {
return nil, err
}
return
}

View file

@ -18,6 +18,7 @@ import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
json "github.com/hjson/hjson-go"
@ -67,15 +68,15 @@ func (p *parser) declare(tok Token, lit string) (interface{}, error) {
switch tok {
case NULL:
return nil, nil
case TRUE:
return true, nil
case FALSE:
return false, nil
case NULL:
return &Null{}, nil
case VOID:
return &Void{}, nil
@ -91,19 +92,13 @@ func (p *parser) declare(tok Token, lit string) (interface{}, error) {
case QMARK:
return &Any{}, nil
case ASC:
return &Asc{}, nil
case DESC:
return &Desc{}, nil
case STRING:
return &Value{lit}, nil
case REGION:
return &Value{lit}, nil
case ID:
case EXPR:
return &Ident{lit}, nil
case IDENT:
@ -112,35 +107,50 @@ func (p *parser) declare(tok Token, lit string) (interface{}, error) {
case TABLE:
return &Table{lit}, nil
case NOW:
return time.Now().UTC(), nil
case PARAM:
return &Param{lit}, nil
case DATE:
return time.Parse("2006-01-02", lit)
return time.Parse(RFCDate, lit)
case TIME:
return time.Parse(time.RFC3339, lit)
return time.Parse(RFCTime, lit)
case REGEX:
return regexp.Compile(lit)
case NUMBER:
return strconv.ParseInt(lit, 10, 64)
val, err := strconv.ParseFloat(lit, 64)
if err != nil {
return val, fmt.Errorf("Invalid number: %s", lit)
}
return val, nil
case DOUBLE:
return strconv.ParseFloat(lit, 64)
val, err := strconv.ParseFloat(lit, 64)
if err != nil {
return val, fmt.Errorf("Invalid number: %s", lit)
}
return val, nil
case DURATION:
return time.ParseDuration(lit)
case PARAM:
if p, ok := p.v[lit]; ok {
return p, nil
var mul time.Duration
switch {
default:
mul = 1
case strings.HasSuffix(lit, "d"):
mul, lit = 24, strings.Replace(lit, "d", "h", -1)
case strings.HasSuffix(lit, "w"):
mul, lit = 168, strings.Replace(lit, "w", "h", -1)
}
return &Param{lit}, nil
val, err := time.ParseDuration(lit)
if err != nil {
return val, fmt.Errorf("Invalid duration: %s", lit)
}
return val * mul, nil
case ARRAY:
var j Array
var j []interface{}
json.Unmarshal([]byte(lit), &j)
if j == nil {
return j, fmt.Errorf("Invalid JSON: %s", lit)
@ -148,7 +158,7 @@ func (p *parser) declare(tok Token, lit string) (interface{}, error) {
return j, nil
case JSON:
var j Object
var j map[string]interface{}
json.Unmarshal([]byte(lit), &j)
if j == nil {
return j, fmt.Errorf("Invalid JSON: %s", lit)