Files
upstream-corteza/server/pkg/ql/parser_nodes.go
upstream-mirror 7e385fc126
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
2026-09-18 01:22:23 +05:00

222 lines
4.4 KiB
Go

package ql
import (
"fmt"
"strings"
)
// SelectStatement represents a SQL SELECT statement.
type (
parserNode interface {
fmt.Stringer
Validate() error
ToAST() *ASTNode
}
parserNodeSet []parserNode // Stream of comma delimited nodes
parserNodes []parserNode // Stream of space delimited nodes
lNull struct{}
lBoolean struct{ value bool }
lString struct {
value string
args []interface{}
}
lNumber struct {
value string
}
operator struct {
kind string
}
Ident struct {
Value string
args []interface{}
}
keyword struct {
keyword string
}
interval struct {
value string
unit string
}
function struct {
name string
arguments parserNodeSet
}
opDef struct {
name string
weight int
}
)
var (
ops = map[string]opDef{
// generic comparison
`=`: {name: `eq`, weight: 40},
`==`: {name: `eq`, weight: 40},
`===`: {name: `eq`, weight: 40},
`!=`: {name: `ne`, weight: 40},
`!==`: {name: `ne`, weight: 40},
`<>`: {name: `ne`, weight: 40},
`<`: {name: `lt`, weight: 30},
`<=`: {name: `le`, weight: 30},
`>`: {name: `gt`, weight: 30},
`>=`: {name: `ge`, weight: 30},
`IS`: {name: `is`, weight: 40},
`IS NOT`: {name: `nis`, weight: 40},
`IN`: {name: `in`, weight: 40},
`NOT IN`: {name: `nin`, weight: 40},
// conjunction
`AND`: {name: `and`, weight: 50},
`&&`: {name: `and`, weight: 50},
`OR`: {name: `or`, weight: 60},
`||`: {name: `or`, weight: 60},
`XOR`: {name: `xor`, weight: 60},
// math
`+`: {name: `add`, weight: 20},
`-`: {name: `sub`, weight: 20},
`*`: {name: `mult`, weight: 10},
`/`: {name: `div`, weight: 10},
// modifiers
`!`: {name: `not`, weight: 0},
// str comp.
`LIKE`: {name: `like`, weight: 40},
`NOT LIKE`: {name: `nlike`, weight: 40},
// range comp.
`BETWEEN`: {name: `between`, weight: 40},
`NOT BETWEEN`: {name: `nbetween`, weight: 40},
}
)
func isUnary(s string) bool {
return s == "!" || s == "not"
}
func isOperator(s string) bool {
return s == "and" || s == "or" || s == "xor"
}
func getOp(op string) *opDef {
o, ok := ops[strings.ToUpper(op)]
if !ok {
return nil
}
return &o
}
func (n lNull) Validate() (err error) { return }
func (n lNull) String() string { return "NULL" }
func (n lBoolean) Validate() (err error) { return }
func (n lBoolean) String() string {
if n.value {
return "TRUE"
} else {
return "FALSE"
}
}
func (n lString) Validate() (err error) { return }
func (n lString) String() string { return fmt.Sprintf("%q", n.value) }
func (n lNumber) Validate() (err error) { return }
func (n lNumber) String() string { return n.value }
func (n operator) Validate() (err error) {
if getOp(n.kind) == nil {
return fmt.Errorf("unknown operator '%s'", n.kind)
}
return
}
func (n operator) String() string { return n.kind }
func (n keyword) Validate() (err error) { return }
func (n keyword) String() string { return n.keyword }
func (n interval) Validate() (err error) { return }
func (n interval) String() string { return fmt.Sprintf("INTERVAL %s %s", n.value, n.unit) }
func (n function) Validate() (err error) { return }
func (n function) String() string { return fmt.Sprintf("%s(%s)", n.name, n.arguments) }
func (n Ident) Validate() (err error) { return }
func (n Ident) String() string { return n.Value }
func (nn parserNodes) Validate() (err error) {
if err = validate(nn); err != nil {
return
}
l := len(nn)
if l == 0 {
return fmt.Errorf("empty set")
}
if op, ok := nn[0].(operator); ok && !isUnary(op.kind) {
return fmt.Errorf("malformed expression, unexpected operator '%s' at first node", op)
}
if l > 1 {
if op, ok := nn[l-1].(operator); ok {
return fmt.Errorf("malformed expression, unexpected operator '%s' at last node", op)
}
}
return
}
func (nn parserNodes) String() (out string) {
for i, n := range nn {
if i > 0 {
out = out + " "
}
out = out + n.String()
}
return
}
func (nn parserNodeSet) Validate() (err error) {
return validate(nn)
}
func (nn parserNodeSet) String() (out string) {
for i, n := range nn {
if i > 0 {
out = out + ", "
}
out = out + n.String()
}
return
}
func validate(nn []parserNode) (err error) {
if len(nn) == 0 {
return fmt.Errorf("empty node set")
}
for _, n := range nn {
if err = n.Validate(); err != nil {
return
}
}
return
}