* Bootstrapped CESQL parser

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Added tck

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* WIP, bootstrapped casting and types
Implemented literal and negate expression

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Progress

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Most expressions implemented

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Implemented functions resolution

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Implemented

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* README + fix the tag-release.sh

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Copyright headers

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
Francesco Guardiani 2021-04-26 09:47:30 +02:00 committed by GitHub
parent 82f2b61ecd
commit 5198fc7683
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 6148 additions and 0 deletions

View File

@ -49,6 +49,7 @@ do
"github.com/cloudevents/sdk-go/protocol/kafka_sarama/v2"
"github.com/cloudevents/sdk-go/protocol/ws/v2"
"github.com/cloudevents/sdk-go/observability/opencensus/v2"
"github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/v2" # NOTE: this needs to be last.
)
shift
@ -111,6 +112,7 @@ MODULES=(
"protocol/kafka_sarama"
"protocol/ws"
"observability/opencensus"
"sql"
)
for i in "${MODULES[@]}"; do

79
sql/v2/CESQLLexer.g4 Normal file
View File

@ -0,0 +1,79 @@
lexer grammar CESQLLexer;
// NOTE:
// This grammar is case-sensitive, although CESQL keywords are case-insensitive.
// In order to implement case-insensitivity, check out
// https://github.com/antlr/antlr4/blob/master/doc/case-insensitive-lexing.md#custom-character-streams-approach
// Skip tab, carriage return and newlines
SPACE: [ \t\r\n]+ -> skip;
// Fragments for Literal primitives
fragment ID_LITERAL: [a-zA-Z0-9]+;
fragment DQUOTA_STRING: '"' ( '\\'. | '""' | ~('"'| '\\') )* '"';
fragment SQUOTA_STRING: '\'' ('\\'. | '\'\'' | ~('\'' | '\\'))* '\'';
fragment INT_DIGIT: [0-9];
fragment FN_LITERAL: [A-Z] [A-Z_]*;
// Constructors symbols
LR_BRACKET: '(';
RR_BRACKET: ')';
COMMA: ',';
SINGLE_QUOTE_SYMB: '\'';
DOUBLE_QUOTE_SYMB: '"';
fragment QUOTE_SYMB
: SINGLE_QUOTE_SYMB | DOUBLE_QUOTE_SYMB
;
// Operators
// - Logic
AND: 'AND';
OR: 'OR';
XOR: 'XOR';
NOT: 'NOT';
// - Arithmetics
STAR: '*';
DIVIDE: '/';
MODULE: '%';
PLUS: '+';
MINUS: '-';
// - Comparison
EQUAL: '=';
NOT_EQUAL: '!=';
GREATER: '>';
GREATER_OR_EQUAL: '>=';
LESS: '<';
LESS_GREATER: '<>';
LESS_OR_EQUAL: '<=';
// Like, exists, in
LIKE: 'LIKE';
EXISTS: 'EXISTS';
IN: 'IN';
// Booleans
TRUE: 'TRUE';
FALSE: 'FALSE';
// Literals
DQUOTED_STRING_LITERAL: DQUOTA_STRING;
SQUOTED_STRING_LITERAL: SQUOTA_STRING;
INTEGER_LITERAL: INT_DIGIT+;
// Identifiers
IDENTIFIER: [a-zA-Z]+;
IDENTIFIER_WITH_NUMBER: [a-zA-Z0-9]+;
FUNCTION_IDENTIFIER_WITH_UNDERSCORE: [A-Z] [A-Z_]*;

62
sql/v2/CESQLParser.g4 Normal file
View File

@ -0,0 +1,62 @@
grammar CESQLParser;
import CESQLLexer;
// Entrypoint
cesql: expression EOF;
// Structure of operations, function invocations and expression
expression
: functionIdentifier functionParameterList #functionInvocationExpression
// unary operators are the highest priority
| NOT expression #unaryLogicExpression
| MINUS expression # unaryNumericExpression
// LIKE, EXISTS and IN takes precedence over all the other binary operators
| expression NOT? LIKE stringLiteral #likeExpression
| EXISTS identifier #existsExpression
| expression NOT? IN setExpression #inExpression
// Numeric operations
| expression (STAR | DIVIDE | MODULE) expression #binaryMultiplicativeExpression
| expression (PLUS | MINUS) expression #binaryAdditiveExpression
// Comparison operations
| expression (EQUAL | NOT_EQUAL | LESS_GREATER | GREATER_OR_EQUAL | LESS_OR_EQUAL | LESS | GREATER) expression #binaryComparisonExpression
// Logic operations
|<assoc=right> expression (AND | OR | XOR) expression #binaryLogicExpression
// Subexpressions and atoms
| LR_BRACKET expression RR_BRACKET #subExpression
| atom #atomExpression
;
atom
: booleanLiteral #booleanAtom
| integerLiteral #integerAtom
| stringLiteral #stringAtom
| identifier #identifierAtom
;
// Identifiers
identifier
: (IDENTIFIER | IDENTIFIER_WITH_NUMBER)
;
functionIdentifier
: (IDENTIFIER | FUNCTION_IDENTIFIER_WITH_UNDERSCORE)
;
// Literals
booleanLiteral: (TRUE | FALSE);
stringLiteral: (DQUOTED_STRING_LITERAL | SQUOTED_STRING_LITERAL);
integerLiteral: INTEGER_LITERAL;
// Functions
functionParameterList
: LR_BRACKET ( expression ( COMMA expression )* )? RR_BRACKET
;
// Sets
setExpression
: LR_BRACKET expression ( COMMA expression )* RR_BRACKET // Empty sets are not allowed
;

27
sql/v2/README.md Normal file
View File

@ -0,0 +1,27 @@
# CloudEvents Expression Language Go implementation
CloudEvents Expression Language implementation.
Note: this package is a work in progress, APIs might break in future releases.
## User guide
To start using it:
```go
import cesqlparser "github.com/cloudevents/sdk-go/sql/v2/parser"
// Parse the expression
expression, err := cesqlparser.Parse("subject = 'Hello world'")
// Res can be either int32, bool or string
res, err := expression.Evaluate(event)
```
## Development guide
To regenerate the parser, make sure you have [ANTLR4 installed](https://github.com/antlr/antlr4/blob/master/doc/getting-started.md) and then run:
```shell
antlr4 -Dlanguage=Go -package gen -o gen -visitor -no-listener CESQLParser.g4
```

17
sql/v2/expression.go Normal file
View File

@ -0,0 +1,17 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package v2
import cloudevents "github.com/cloudevents/sdk-go/v2"
// Expression represents a parsed CloudEvents SQL Expression.
type Expression interface {
// Evaluate the expression using the provided input type.
// The return value can be either int32, bool or string.
// The evaluation fails as soon as an error arises.
Evaluate(event cloudevents.Event) (interface{}, error)
}

View File

@ -0,0 +1,17 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import cesql "github.com/cloudevents/sdk-go/sql/v2"
type baseUnaryExpression struct {
child cesql.Expression
}
type baseBinaryExpression struct {
left cesql.Expression
right cesql.Expression
}

View File

@ -0,0 +1,56 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type equalExpression struct {
baseBinaryExpression
equal bool
}
func (s equalExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
leftVal, err := s.left.Evaluate(event)
if err != nil {
return nil, err
}
rightVal, err := s.right.Evaluate(event)
if err != nil {
return nil, err
}
leftVal, err = utils.Cast(leftVal, cesql.TypeFromVal(rightVal))
if err != nil {
return nil, err
}
return (leftVal == rightVal) == s.equal, nil
}
func NewEqualExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return equalExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
equal: true,
}
}
func NewNotEqualExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return equalExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
equal: false,
}
}

View File

@ -0,0 +1,24 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type existsExpression struct {
identifier string
}
func (l existsExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
return utils.ContainsAttribute(event, l.identifier), nil
}
func NewExistsExpression(identifier string) cesql.Expression {
return existsExpression{identifier: identifier}
}

View File

@ -0,0 +1,57 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
"fmt"
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/runtime"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type functionInvocationExpression struct {
name string
argumentsExpression []cesql.Expression
}
func (expr functionInvocationExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
fn := runtime.ResolveFunction(expr.name, len(expr.argumentsExpression))
if fn == nil {
return nil, fmt.Errorf("cannot resolve function %s", expr.name)
}
args := make([]interface{}, len(expr.argumentsExpression))
for i, expr := range expr.argumentsExpression {
arg, err := expr.Evaluate(event)
if err != nil {
return nil, err
}
argType := fn.ArgType(i)
if argType == nil {
return nil, fmt.Errorf("cannot resolve arg type at index %d", i)
}
arg, err = utils.Cast(arg, *argType)
if err != nil {
return nil, err
}
args[i] = arg
}
return fn.Run(event, args)
}
func NewFunctionInvocationExpression(name string, argumentsExpression []cesql.Expression) cesql.Expression {
return functionInvocationExpression{
name: name,
argumentsExpression: argumentsExpression,
}
}

View File

@ -0,0 +1,31 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
"fmt"
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type identifierExpression struct {
identifier string
}
func (l identifierExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
value := utils.GetAttribute(event, l.identifier)
if value == nil {
return nil, fmt.Errorf("missing attribute '%s'", l.identifier)
}
return value, nil
}
func NewIdentifierExpression(identifier string) cesql.Expression {
return identifierExpression{identifier: identifier}
}

View File

@ -0,0 +1,46 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type inExpression struct {
leftExpression cesql.Expression
setExpression []cesql.Expression
}
func (l inExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
leftValue, err := l.leftExpression.Evaluate(event)
if err != nil {
return nil, err
}
for _, rightExpression := range l.setExpression {
rightValue, err := rightExpression.Evaluate(event)
if err != nil {
return nil, err
}
rightValue, err = utils.Cast(rightValue, cesql.TypeFromVal(leftValue))
if err != nil {
return nil, err
}
if leftValue == rightValue {
return true, nil
}
}
return false, nil
}
func NewInExpression(leftExpression cesql.Expression, setExpression []cesql.Expression) cesql.Expression {
return inExpression{leftExpression, setExpression}
}

View File

@ -0,0 +1,89 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type integerComparisonExpression struct {
baseBinaryExpression
fn func(x, y int32) bool
}
func (s integerComparisonExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
leftVal, err := s.left.Evaluate(event)
if err != nil {
return nil, err
}
rightVal, err := s.right.Evaluate(event)
if err != nil {
return nil, err
}
leftVal, err = utils.Cast(leftVal, cesql.IntegerType)
if err != nil {
return nil, err
}
rightVal, err = utils.Cast(rightVal, cesql.IntegerType)
if err != nil {
return nil, err
}
return s.fn(leftVal.(int32), rightVal.(int32)), nil
}
func NewLessExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return integerComparisonExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) bool {
return x < y
},
}
}
func NewLessOrEqualExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return integerComparisonExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) bool {
return x <= y
},
}
}
func NewGreaterExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return integerComparisonExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) bool {
return x > y
},
}
}
func NewGreaterOrEqualExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return integerComparisonExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) bool {
return x >= y
},
}
}

View File

@ -0,0 +1,96 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
"regexp"
"strings"
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type likeExpression struct {
baseUnaryExpression
pattern *regexp.Regexp
}
func (l likeExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
val, err := l.child.Evaluate(event)
if err != nil {
return nil, err
}
val, err = utils.Cast(val, cesql.StringType)
if err != nil {
return nil, err
}
return l.pattern.MatchString(val.(string)), nil
}
func NewLikeExpression(child cesql.Expression, pattern string) (cesql.Expression, error) {
// Converting to regex is not the most performant impl, but it works
p, err := convertLikePatternToRegex(pattern)
if err != nil {
return nil, err
}
return likeExpression{
baseUnaryExpression: baseUnaryExpression{
child: child,
},
pattern: p,
}, nil
}
func convertLikePatternToRegex(pattern string) (*regexp.Regexp, error) {
var chunks []string
chunks = append(chunks, "^")
var chunk strings.Builder
for i := 0; i < len(pattern); i++ {
if pattern[i] == '\\' && i < len(pattern)-1 {
if pattern[i+1] == '%' {
// \% case
chunk.WriteRune('%')
chunks = append(chunks, "\\Q"+chunk.String()+"\\E")
chunk.Reset()
i++
continue
} else if pattern[i+1] == '_' {
// \_ case
chunk.WriteRune('_')
chunks = append(chunks, "\\Q"+chunk.String()+"\\E")
chunk.Reset()
i++
continue
}
} else if pattern[i] == '_' {
// replace with .
chunks = append(chunks, "\\Q"+chunk.String()+"\\E")
chunk.Reset()
chunks = append(chunks, ".")
} else if pattern[i] == '%' {
// replace with .*
chunks = append(chunks, "\\Q"+chunk.String()+"\\E")
chunk.Reset()
chunks = append(chunks, ".*")
} else {
chunk.WriteByte(pattern[i])
}
}
if chunk.Len() != 0 {
chunks = append(chunks, "\\Q"+chunk.String()+"\\E")
}
chunks = append(chunks, "$")
return regexp.Compile(strings.Join(chunks, ""))
}

View File

@ -0,0 +1,23 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type literalExpression struct {
value interface{}
}
func (l literalExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
return l.value, nil
}
func NewLiteralExpression(value interface{}) cesql.Expression {
return literalExpression{value: value}
}

View File

@ -0,0 +1,77 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type logicExpression struct {
baseBinaryExpression
fn func(x, y bool) bool
}
func (s logicExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
leftVal, err := s.left.Evaluate(event)
if err != nil {
return nil, err
}
rightVal, err := s.right.Evaluate(event)
if err != nil {
return nil, err
}
leftVal, err = utils.Cast(leftVal, cesql.BooleanType)
if err != nil {
return nil, err
}
rightVal, err = utils.Cast(rightVal, cesql.BooleanType)
if err != nil {
return nil, err
}
return s.fn(leftVal.(bool), rightVal.(bool)), nil
}
func NewAndExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return logicExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y bool) bool {
return x && y
},
}
}
func NewOrExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return logicExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y bool) bool {
return x || y
},
}
}
func NewXorExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return logicExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y bool) bool {
return x != y
},
}
}

View File

@ -0,0 +1,109 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
"errors"
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type mathExpression struct {
baseBinaryExpression
fn func(x, y int32) (int32, error)
}
func (s mathExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
leftVal, err := s.left.Evaluate(event)
if err != nil {
return nil, err
}
rightVal, err := s.right.Evaluate(event)
if err != nil {
return nil, err
}
leftVal, err = utils.Cast(leftVal, cesql.IntegerType)
if err != nil {
return nil, err
}
rightVal, err = utils.Cast(rightVal, cesql.IntegerType)
if err != nil {
return nil, err
}
return s.fn(leftVal.(int32), rightVal.(int32))
}
func NewSumExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return mathExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) (int32, error) {
return x + y, nil
},
}
}
func NewDifferenceExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return mathExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) (int32, error) {
return x - y, nil
},
}
}
func NewMultiplicationExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return mathExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) (int32, error) {
return x * y, nil
},
}
}
func NewModuleExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return mathExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) (int32, error) {
if y == 0 {
return 0, errors.New("math error: division by zero")
}
return x % y, nil
},
}
}
func NewDivisionExpression(left cesql.Expression, right cesql.Expression) cesql.Expression {
return mathExpression{
baseBinaryExpression: baseBinaryExpression{
left: left,
right: right,
},
fn: func(x, y int32) (int32, error) {
if y == 0 {
return 0, errors.New("math error: division by zero")
}
return x / y, nil
},
}
}

View File

@ -0,0 +1,32 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type negateExpression baseUnaryExpression
func (l negateExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
val, err := l.child.Evaluate(event)
if err != nil {
return nil, err
}
val, err = utils.Cast(val, cesql.IntegerType)
if err != nil {
return nil, err
}
return -(val.(int32)), nil
}
func NewNegateExpression(child cesql.Expression) cesql.Expression {
return negateExpression{child: child}
}

View File

@ -0,0 +1,32 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package expression
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type notExpression baseUnaryExpression
func (l notExpression) Evaluate(event cloudevents.Event) (interface{}, error) {
val, err := l.child.Evaluate(event)
if err != nil {
return nil, err
}
val, err = utils.Cast(val, cesql.BooleanType)
if err != nil {
return nil, err
}
return !(val.(bool)), nil
}
func NewNotExpression(child cesql.Expression) cesql.Expression {
return notExpression{child: child}
}

17
sql/v2/function.go Normal file
View File

@ -0,0 +1,17 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package v2
import cloudevents "github.com/cloudevents/sdk-go/v2"
type Function interface {
Name() string
Arity() int
IsVariadic() bool
ArgType(index int) *Type
Run(event cloudevents.Event, arguments []interface{}) (interface{}, error)
}

View File

@ -0,0 +1,57 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package function
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/utils"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
var IntFunction function = function{
name: "INT",
fixedArgs: []cesql.Type{cesql.AnyType},
variadicArgs: nil,
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return utils.Cast(i[0], cesql.IntegerType)
},
}
var BoolFunction function = function{
name: "BOOL",
fixedArgs: []cesql.Type{cesql.AnyType},
variadicArgs: nil,
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return utils.Cast(i[0], cesql.BooleanType)
},
}
var StringFunction function = function{
name: "STRING",
fixedArgs: []cesql.Type{cesql.AnyType},
variadicArgs: nil,
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return utils.Cast(i[0], cesql.StringType)
},
}
var IsIntFunction function = function{
name: "IS_INT",
fixedArgs: []cesql.Type{cesql.AnyType},
variadicArgs: nil,
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return utils.CanCast(i[0], cesql.IntegerType), nil
},
}
var IsBoolFunction function = function{
name: "IS_BOOL",
fixedArgs: []cesql.Type{cesql.AnyType},
variadicArgs: nil,
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return utils.CanCast(i[0], cesql.BooleanType), nil
},
}

View File

@ -0,0 +1,41 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package function
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type function struct {
name string
fixedArgs []cesql.Type
variadicArgs *cesql.Type
fn func(cloudevents.Event, []interface{}) (interface{}, error)
}
func (f function) Name() string {
return f.name
}
func (f function) Arity() int {
return len(f.fixedArgs)
}
func (f function) IsVariadic() bool {
return f.variadicArgs != nil
}
func (f function) ArgType(index int) *cesql.Type {
if index < len(f.fixedArgs) {
return &f.fixedArgs[index]
}
return f.variadicArgs
}
func (f function) Run(event cloudevents.Event, arguments []interface{}) (interface{}, error) {
return f.fn(event, arguments)
}

View File

@ -0,0 +1,24 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package function
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
var AbsFunction function = function{
name: "ABS",
fixedArgs: []cesql.Type{cesql.IntegerType},
variadicArgs: nil,
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
x := i[0].(int32)
if x < 0 {
return -x, nil
}
return x, nil
},
}

View File

@ -0,0 +1,177 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package function
import (
"fmt"
"strings"
cesql "github.com/cloudevents/sdk-go/sql/v2"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
var LengthFunction function = function{
name: "LENGTH",
fixedArgs: []cesql.Type{cesql.StringType},
variadicArgs: nil,
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return int32(len(i[0].(string))), nil
},
}
var ConcatFunction function = function{
name: "CONCAT",
variadicArgs: cesql.TypePtr(cesql.StringType),
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
var sb strings.Builder
for _, v := range i {
sb.WriteString(v.(string))
}
return sb.String(), nil
},
}
var ConcatWSFunction function = function{
name: "CONCAT_WS",
fixedArgs: []cesql.Type{cesql.StringType},
variadicArgs: cesql.TypePtr(cesql.StringType),
fn: func(event cloudevents.Event, args []interface{}) (interface{}, error) {
if len(args) == 1 {
return "", nil
}
separator := args[0].(string)
var sb strings.Builder
for i := 1; i < len(args)-1; i++ {
sb.WriteString(args[i].(string))
sb.WriteString(separator)
}
sb.WriteString(args[len(args)-1].(string))
return sb.String(), nil
},
}
var LowerFunction function = function{
name: "LOWER",
fixedArgs: []cesql.Type{cesql.StringType},
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return strings.ToLower(i[0].(string)), nil
},
}
var UpperFunction function = function{
name: "UPPER",
fixedArgs: []cesql.Type{cesql.StringType},
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return strings.ToUpper(i[0].(string)), nil
},
}
var TrimFunction function = function{
name: "TRIM",
fixedArgs: []cesql.Type{cesql.StringType},
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
return strings.TrimSpace(i[0].(string)), nil
},
}
var LeftFunction function = function{
name: "LEFT",
fixedArgs: []cesql.Type{cesql.StringType, cesql.IntegerType},
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
str := i[0].(string)
y := int(i[1].(int32))
if y > len(str) {
return str, nil
}
if y < 0 {
return nil, fmt.Errorf("LEFT y argument is < 0: %d", y)
}
return str[0:y], nil
},
}
var RightFunction function = function{
name: "RIGHT",
fixedArgs: []cesql.Type{cesql.StringType, cesql.IntegerType},
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
str := i[0].(string)
y := int(i[1].(int32))
if y > len(str) {
return str, nil
}
if y < 0 {
return nil, fmt.Errorf("RIGHT y argument is < 0: %d", y)
}
return str[len(str)-y:], nil
},
}
var SubstringFunction function = function{
name: "SUBSTRING",
fixedArgs: []cesql.Type{cesql.StringType, cesql.IntegerType},
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
str := i[0].(string)
pos := int(i[1].(int32))
if pos == 0 {
return "", nil
}
if pos < -len(str) || pos > len(str) {
return "", fmt.Errorf("SUBSTRING invalid pos argument: %d", pos)
}
var beginning int
if pos < 0 {
beginning = len(str) + pos
} else {
beginning = pos - 1
}
return str[beginning:], nil
},
}
var SubstringWithLengthFunction function = function{
name: "SUBSTRING",
fixedArgs: []cesql.Type{cesql.StringType, cesql.IntegerType, cesql.IntegerType},
fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) {
str := i[0].(string)
pos := int(i[1].(int32))
length := int(i[2].(int32))
if pos == 0 {
return "", nil
}
if pos < -len(str) || pos > len(str) {
return "", fmt.Errorf("SUBSTRING invalid pos argument: %d", pos)
}
var beginning int
if pos < 0 {
beginning = len(str) + pos
} else {
beginning = pos - 1
}
var end int
if beginning+length > len(str) {
end = len(str)
} else {
end = beginning + length
}
return str[beginning:end], nil
},
}

View File

@ -0,0 +1,87 @@
token literal names:
null
null
'('
')'
','
'\''
'"'
'AND'
'OR'
'XOR'
'NOT'
'*'
'/'
'%'
'+'
'-'
'='
'!='
'>'
'>='
'<'
'<>'
'<='
'LIKE'
'EXISTS'
'IN'
'TRUE'
'FALSE'
null
null
null
null
null
null
token symbolic names:
null
SPACE
LR_BRACKET
RR_BRACKET
COMMA
SINGLE_QUOTE_SYMB
DOUBLE_QUOTE_SYMB
AND
OR
XOR
NOT
STAR
DIVIDE
MODULE
PLUS
MINUS
EQUAL
NOT_EQUAL
GREATER
GREATER_OR_EQUAL
LESS
LESS_GREATER
LESS_OR_EQUAL
LIKE
EXISTS
IN
TRUE
FALSE
DQUOTED_STRING_LITERAL
SQUOTED_STRING_LITERAL
INTEGER_LITERAL
IDENTIFIER
IDENTIFIER_WITH_NUMBER
FUNCTION_IDENTIFIER_WITH_UNDERSCORE
rule names:
cesql
expression
atom
identifier
functionIdentifier
booleanLiteral
stringLiteral
integerLiteral
functionParameterList
setExpression
atn:
[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 35, 112, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 41, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 57, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 63, 10, 3, 3, 3, 3, 3, 7, 3, 67, 10, 3, 12, 3, 14, 3, 70, 11, 3, 3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 76, 10, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 7, 10, 92, 10, 10, 12, 10, 14, 10, 95, 11, 10, 5, 10, 97, 10, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 7, 11, 105, 10, 11, 12, 11, 14, 11, 108, 11, 11, 3, 11, 3, 11, 3, 11, 2, 3, 4, 12, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 2, 10, 3, 2, 13, 15, 3, 2, 16, 17, 3, 2, 18, 24, 3, 2, 9, 11, 3, 2, 33, 34, 4, 2, 33, 33, 35, 35, 3, 2, 28, 29, 3, 2, 30, 31, 2, 120, 2, 22, 3, 2, 2, 2, 4, 40, 3, 2, 2, 2, 6, 75, 3, 2, 2, 2, 8, 77, 3, 2, 2, 2, 10, 79, 3, 2, 2, 2, 12, 81, 3, 2, 2, 2, 14, 83, 3, 2, 2, 2, 16, 85, 3, 2, 2, 2, 18, 87, 3, 2, 2, 2, 20, 100, 3, 2, 2, 2, 22, 23, 5, 4, 3, 2, 23, 24, 7, 2, 2, 3, 24, 3, 3, 2, 2, 2, 25, 26, 8, 3, 1, 2, 26, 27, 5, 10, 6, 2, 27, 28, 5, 18, 10, 2, 28, 41, 3, 2, 2, 2, 29, 30, 7, 12, 2, 2, 30, 41, 5, 4, 3, 13, 31, 32, 7, 17, 2, 2, 32, 41, 5, 4, 3, 12, 33, 34, 7, 26, 2, 2, 34, 41, 5, 8, 5, 2, 35, 36, 7, 4, 2, 2, 36, 37, 5, 4, 3, 2, 37, 38, 7, 5, 2, 2, 38, 41, 3, 2, 2, 2, 39, 41, 5, 6, 4, 2, 40, 25, 3, 2, 2, 2, 40, 29, 3, 2, 2, 2, 40, 31, 3, 2, 2, 2, 40, 33, 3, 2, 2, 2, 40, 35, 3, 2, 2, 2, 40, 39, 3, 2, 2, 2, 41, 68, 3, 2, 2, 2, 42, 43, 12, 8, 2, 2, 43, 44, 9, 2, 2, 2, 44, 67, 5, 4, 3, 9, 45, 46, 12, 7, 2, 2, 46, 47, 9, 3, 2, 2, 47, 67, 5, 4, 3, 8, 48, 49, 12, 6, 2, 2, 49, 50, 9, 4, 2, 2, 50, 67, 5, 4, 3, 7, 51, 52, 12, 5, 2, 2, 52, 53, 9, 5, 2, 2, 53, 67, 5, 4, 3, 5, 54, 56, 12, 11, 2, 2, 55, 57, 7, 12, 2, 2, 56, 55, 3, 2, 2, 2, 56, 57, 3, 2, 2, 2, 57, 58, 3, 2, 2, 2, 58, 59, 7, 25, 2, 2, 59, 67, 5, 14, 8, 2, 60, 62, 12, 9, 2, 2, 61, 63, 7, 12, 2, 2, 62, 61, 3, 2, 2, 2, 62, 63, 3, 2, 2, 2, 63, 64, 3, 2, 2, 2, 64, 65, 7, 27, 2, 2, 65, 67, 5, 20, 11, 2, 66, 42, 3, 2, 2, 2, 66, 45, 3, 2, 2, 2, 66, 48, 3, 2, 2, 2, 66, 51, 3, 2, 2, 2, 66, 54, 3, 2, 2, 2, 66, 60, 3, 2, 2, 2, 67, 70, 3, 2, 2, 2, 68, 66, 3, 2, 2, 2, 68, 69, 3, 2, 2, 2, 69, 5, 3, 2, 2, 2, 70, 68, 3, 2, 2, 2, 71, 76, 5, 12, 7, 2, 72, 76, 5, 16, 9, 2, 73, 76, 5, 14, 8, 2, 74, 76, 5, 8, 5, 2, 75, 71, 3, 2, 2, 2, 75, 72, 3, 2, 2, 2, 75, 73, 3, 2, 2, 2, 75, 74, 3, 2, 2, 2, 76, 7, 3, 2, 2, 2, 77, 78, 9, 6, 2, 2, 78, 9, 3, 2, 2, 2, 79, 80, 9, 7, 2, 2, 80, 11, 3, 2, 2, 2, 81, 82, 9, 8, 2, 2, 82, 13, 3, 2, 2, 2, 83, 84, 9, 9, 2, 2, 84, 15, 3, 2, 2, 2, 85, 86, 7, 32, 2, 2, 86, 17, 3, 2, 2, 2, 87, 96, 7, 4, 2, 2, 88, 93, 5, 4, 3, 2, 89, 90, 7, 6, 2, 2, 90, 92, 5, 4, 3, 2, 91, 89, 3, 2, 2, 2, 92, 95, 3, 2, 2, 2, 93, 91, 3, 2, 2, 2, 93, 94, 3, 2, 2, 2, 94, 97, 3, 2, 2, 2, 95, 93, 3, 2, 2, 2, 96, 88, 3, 2, 2, 2, 96, 97, 3, 2, 2, 2, 97, 98, 3, 2, 2, 2, 98, 99, 7, 5, 2, 2, 99, 19, 3, 2, 2, 2, 100, 101, 7, 4, 2, 2, 101, 106, 5, 4, 3, 2, 102, 103, 7, 6, 2, 2, 103, 105, 5, 4, 3, 2, 104, 102, 3, 2, 2, 2, 105, 108, 3, 2, 2, 2, 106, 104, 3, 2, 2, 2, 106, 107, 3, 2, 2, 2, 107, 109, 3, 2, 2, 2, 108, 106, 3, 2, 2, 2, 109, 110, 7, 5, 2, 2, 110, 21, 3, 2, 2, 2, 11, 40, 56, 62, 66, 68, 75, 93, 96, 106]

View File

@ -0,0 +1,59 @@
SPACE=1
LR_BRACKET=2
RR_BRACKET=3
COMMA=4
SINGLE_QUOTE_SYMB=5
DOUBLE_QUOTE_SYMB=6
AND=7
OR=8
XOR=9
NOT=10
STAR=11
DIVIDE=12
MODULE=13
PLUS=14
MINUS=15
EQUAL=16
NOT_EQUAL=17
GREATER=18
GREATER_OR_EQUAL=19
LESS=20
LESS_GREATER=21
LESS_OR_EQUAL=22
LIKE=23
EXISTS=24
IN=25
TRUE=26
FALSE=27
DQUOTED_STRING_LITERAL=28
SQUOTED_STRING_LITERAL=29
INTEGER_LITERAL=30
IDENTIFIER=31
IDENTIFIER_WITH_NUMBER=32
FUNCTION_IDENTIFIER_WITH_UNDERSCORE=33
'('=2
')'=3
','=4
'\''=5
'"'=6
'AND'=7
'OR'=8
'XOR'=9
'NOT'=10
'*'=11
'/'=12
'%'=13
'+'=14
'-'=15
'='=16
'!='=17
'>'=18
'>='=19
'<'=20
'<>'=21
'<='=22
'LIKE'=23
'EXISTS'=24
'IN'=25
'TRUE'=26
'FALSE'=27

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,59 @@
SPACE=1
LR_BRACKET=2
RR_BRACKET=3
COMMA=4
SINGLE_QUOTE_SYMB=5
DOUBLE_QUOTE_SYMB=6
AND=7
OR=8
XOR=9
NOT=10
STAR=11
DIVIDE=12
MODULE=13
PLUS=14
MINUS=15
EQUAL=16
NOT_EQUAL=17
GREATER=18
GREATER_OR_EQUAL=19
LESS=20
LESS_GREATER=21
LESS_OR_EQUAL=22
LIKE=23
EXISTS=24
IN=25
TRUE=26
FALSE=27
DQUOTED_STRING_LITERAL=28
SQUOTED_STRING_LITERAL=29
INTEGER_LITERAL=30
IDENTIFIER=31
IDENTIFIER_WITH_NUMBER=32
FUNCTION_IDENTIFIER_WITH_UNDERSCORE=33
'('=2
')'=3
','=4
'\''=5
'"'=6
'AND'=7
'OR'=8
'XOR'=9
'NOT'=10
'*'=11
'/'=12
'%'=13
'+'=14
'-'=15
'='=16
'!='=17
'>'=18
'>='=19
'<'=20
'<>'=21
'<='=22
'LIKE'=23
'EXISTS'=24
'IN'=25
'TRUE'=26
'FALSE'=27

View File

@ -0,0 +1,109 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
// Code generated from CESQLParser.g4 by ANTLR 4.9. DO NOT EDIT.
package gen // CESQLParser
import "github.com/antlr/antlr4/runtime/Go/antlr"
type BaseCESQLParserVisitor struct {
*antlr.BaseParseTreeVisitor
}
func (v *BaseCESQLParserVisitor) VisitCesql(ctx *CesqlContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitInExpression(ctx *InExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitBinaryComparisonExpression(ctx *BinaryComparisonExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitAtomExpression(ctx *AtomExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitExistsExpression(ctx *ExistsExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitBinaryLogicExpression(ctx *BinaryLogicExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitLikeExpression(ctx *LikeExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitFunctionInvocationExpression(ctx *FunctionInvocationExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitBinaryMultiplicativeExpression(ctx *BinaryMultiplicativeExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitUnaryLogicExpression(ctx *UnaryLogicExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitUnaryNumericExpression(ctx *UnaryNumericExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitSubExpression(ctx *SubExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitBinaryAdditiveExpression(ctx *BinaryAdditiveExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitBooleanAtom(ctx *BooleanAtomContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitIntegerAtom(ctx *IntegerAtomContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitStringAtom(ctx *StringAtomContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitIdentifierAtom(ctx *IdentifierAtomContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitIdentifier(ctx *IdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitFunctionIdentifier(ctx *FunctionIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitBooleanLiteral(ctx *BooleanLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitStringLiteral(ctx *StringLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitIntegerLiteral(ctx *IntegerLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitFunctionParameterList(ctx *FunctionParameterListContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCESQLParserVisitor) VisitSetExpression(ctx *SetExpressionContext) interface{} {
return v.VisitChildren(ctx)
}

View File

@ -0,0 +1,231 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
// Code generated from CESQLParser.g4 by ANTLR 4.9. DO NOT EDIT.
package gen
import (
"fmt"
"unicode"
"github.com/antlr/antlr4/runtime/Go/antlr"
)
// Suppress unused import error
var _ = fmt.Printf
var _ = unicode.IsLetter
var serializedLexerAtn = []uint16{
3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 35, 237,
8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7,
9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12,
4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4,
18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23,
9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9,
28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33,
4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4,
39, 9, 39, 4, 40, 9, 40, 3, 2, 6, 2, 83, 10, 2, 13, 2, 14, 2, 84, 3, 2,
3, 2, 3, 3, 6, 3, 90, 10, 3, 13, 3, 14, 3, 91, 3, 4, 3, 4, 3, 4, 3, 4,
3, 4, 3, 4, 7, 4, 100, 10, 4, 12, 4, 14, 4, 103, 11, 4, 3, 4, 3, 4, 3,
5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 7, 5, 113, 10, 5, 12, 5, 14, 5, 116, 11,
5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 7, 7, 124, 10, 7, 12, 7, 14, 7,
127, 11, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12,
3, 12, 3, 13, 3, 13, 5, 13, 141, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3,
15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17,
3, 18, 3, 18, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3,
23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 27,
3, 27, 3, 28, 3, 28, 3, 28, 3, 29, 3, 29, 3, 29, 3, 30, 3, 30, 3, 30, 3,
30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32,
3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 34, 3,
34, 3, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 37, 6, 37, 217, 10, 37, 13, 37,
14, 37, 218, 3, 38, 6, 38, 222, 10, 38, 13, 38, 14, 38, 223, 3, 39, 6,
39, 227, 10, 39, 13, 39, 14, 39, 228, 3, 40, 3, 40, 7, 40, 233, 10, 40,
12, 40, 14, 40, 236, 11, 40, 2, 2, 41, 3, 3, 5, 2, 7, 2, 9, 2, 11, 2, 13,
2, 15, 4, 17, 5, 19, 6, 21, 7, 23, 8, 25, 2, 27, 9, 29, 10, 31, 11, 33,
12, 35, 13, 37, 14, 39, 15, 41, 16, 43, 17, 45, 18, 47, 19, 49, 20, 51,
21, 53, 22, 55, 23, 57, 24, 59, 25, 61, 26, 63, 27, 65, 28, 67, 29, 69,
30, 71, 31, 73, 32, 75, 33, 77, 34, 79, 35, 3, 2, 10, 5, 2, 11, 12, 15,
15, 34, 34, 5, 2, 50, 59, 67, 92, 99, 124, 4, 2, 36, 36, 94, 94, 4, 2,
41, 41, 94, 94, 3, 2, 50, 59, 3, 2, 67, 92, 4, 2, 67, 92, 97, 97, 4, 2,
67, 92, 99, 124, 2, 244, 2, 3, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3,
2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 27,
3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2,
35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2,
2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2, 2, 2, 49, 3, 2, 2,
2, 2, 51, 3, 2, 2, 2, 2, 53, 3, 2, 2, 2, 2, 55, 3, 2, 2, 2, 2, 57, 3, 2,
2, 2, 2, 59, 3, 2, 2, 2, 2, 61, 3, 2, 2, 2, 2, 63, 3, 2, 2, 2, 2, 65, 3,
2, 2, 2, 2, 67, 3, 2, 2, 2, 2, 69, 3, 2, 2, 2, 2, 71, 3, 2, 2, 2, 2, 73,
3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2, 79, 3, 2, 2, 2, 3,
82, 3, 2, 2, 2, 5, 89, 3, 2, 2, 2, 7, 93, 3, 2, 2, 2, 9, 106, 3, 2, 2,
2, 11, 119, 3, 2, 2, 2, 13, 121, 3, 2, 2, 2, 15, 128, 3, 2, 2, 2, 17, 130,
3, 2, 2, 2, 19, 132, 3, 2, 2, 2, 21, 134, 3, 2, 2, 2, 23, 136, 3, 2, 2,
2, 25, 140, 3, 2, 2, 2, 27, 142, 3, 2, 2, 2, 29, 146, 3, 2, 2, 2, 31, 149,
3, 2, 2, 2, 33, 153, 3, 2, 2, 2, 35, 157, 3, 2, 2, 2, 37, 159, 3, 2, 2,
2, 39, 161, 3, 2, 2, 2, 41, 163, 3, 2, 2, 2, 43, 165, 3, 2, 2, 2, 45, 167,
3, 2, 2, 2, 47, 169, 3, 2, 2, 2, 49, 172, 3, 2, 2, 2, 51, 174, 3, 2, 2,
2, 53, 177, 3, 2, 2, 2, 55, 179, 3, 2, 2, 2, 57, 182, 3, 2, 2, 2, 59, 185,
3, 2, 2, 2, 61, 190, 3, 2, 2, 2, 63, 197, 3, 2, 2, 2, 65, 200, 3, 2, 2,
2, 67, 205, 3, 2, 2, 2, 69, 211, 3, 2, 2, 2, 71, 213, 3, 2, 2, 2, 73, 216,
3, 2, 2, 2, 75, 221, 3, 2, 2, 2, 77, 226, 3, 2, 2, 2, 79, 230, 3, 2, 2,
2, 81, 83, 9, 2, 2, 2, 82, 81, 3, 2, 2, 2, 83, 84, 3, 2, 2, 2, 84, 82,
3, 2, 2, 2, 84, 85, 3, 2, 2, 2, 85, 86, 3, 2, 2, 2, 86, 87, 8, 2, 2, 2,
87, 4, 3, 2, 2, 2, 88, 90, 9, 3, 2, 2, 89, 88, 3, 2, 2, 2, 90, 91, 3, 2,
2, 2, 91, 89, 3, 2, 2, 2, 91, 92, 3, 2, 2, 2, 92, 6, 3, 2, 2, 2, 93, 101,
7, 36, 2, 2, 94, 95, 7, 94, 2, 2, 95, 100, 11, 2, 2, 2, 96, 97, 7, 36,
2, 2, 97, 100, 7, 36, 2, 2, 98, 100, 10, 4, 2, 2, 99, 94, 3, 2, 2, 2, 99,
96, 3, 2, 2, 2, 99, 98, 3, 2, 2, 2, 100, 103, 3, 2, 2, 2, 101, 99, 3, 2,
2, 2, 101, 102, 3, 2, 2, 2, 102, 104, 3, 2, 2, 2, 103, 101, 3, 2, 2, 2,
104, 105, 7, 36, 2, 2, 105, 8, 3, 2, 2, 2, 106, 114, 7, 41, 2, 2, 107,
108, 7, 94, 2, 2, 108, 113, 11, 2, 2, 2, 109, 110, 7, 41, 2, 2, 110, 113,
7, 41, 2, 2, 111, 113, 10, 5, 2, 2, 112, 107, 3, 2, 2, 2, 112, 109, 3,
2, 2, 2, 112, 111, 3, 2, 2, 2, 113, 116, 3, 2, 2, 2, 114, 112, 3, 2, 2,
2, 114, 115, 3, 2, 2, 2, 115, 117, 3, 2, 2, 2, 116, 114, 3, 2, 2, 2, 117,
118, 7, 41, 2, 2, 118, 10, 3, 2, 2, 2, 119, 120, 9, 6, 2, 2, 120, 12, 3,
2, 2, 2, 121, 125, 9, 7, 2, 2, 122, 124, 9, 8, 2, 2, 123, 122, 3, 2, 2,
2, 124, 127, 3, 2, 2, 2, 125, 123, 3, 2, 2, 2, 125, 126, 3, 2, 2, 2, 126,
14, 3, 2, 2, 2, 127, 125, 3, 2, 2, 2, 128, 129, 7, 42, 2, 2, 129, 16, 3,
2, 2, 2, 130, 131, 7, 43, 2, 2, 131, 18, 3, 2, 2, 2, 132, 133, 7, 46, 2,
2, 133, 20, 3, 2, 2, 2, 134, 135, 7, 41, 2, 2, 135, 22, 3, 2, 2, 2, 136,
137, 7, 36, 2, 2, 137, 24, 3, 2, 2, 2, 138, 141, 5, 21, 11, 2, 139, 141,
5, 23, 12, 2, 140, 138, 3, 2, 2, 2, 140, 139, 3, 2, 2, 2, 141, 26, 3, 2,
2, 2, 142, 143, 7, 67, 2, 2, 143, 144, 7, 80, 2, 2, 144, 145, 7, 70, 2,
2, 145, 28, 3, 2, 2, 2, 146, 147, 7, 81, 2, 2, 147, 148, 7, 84, 2, 2, 148,
30, 3, 2, 2, 2, 149, 150, 7, 90, 2, 2, 150, 151, 7, 81, 2, 2, 151, 152,
7, 84, 2, 2, 152, 32, 3, 2, 2, 2, 153, 154, 7, 80, 2, 2, 154, 155, 7, 81,
2, 2, 155, 156, 7, 86, 2, 2, 156, 34, 3, 2, 2, 2, 157, 158, 7, 44, 2, 2,
158, 36, 3, 2, 2, 2, 159, 160, 7, 49, 2, 2, 160, 38, 3, 2, 2, 2, 161, 162,
7, 39, 2, 2, 162, 40, 3, 2, 2, 2, 163, 164, 7, 45, 2, 2, 164, 42, 3, 2,
2, 2, 165, 166, 7, 47, 2, 2, 166, 44, 3, 2, 2, 2, 167, 168, 7, 63, 2, 2,
168, 46, 3, 2, 2, 2, 169, 170, 7, 35, 2, 2, 170, 171, 7, 63, 2, 2, 171,
48, 3, 2, 2, 2, 172, 173, 7, 64, 2, 2, 173, 50, 3, 2, 2, 2, 174, 175, 7,
64, 2, 2, 175, 176, 7, 63, 2, 2, 176, 52, 3, 2, 2, 2, 177, 178, 7, 62,
2, 2, 178, 54, 3, 2, 2, 2, 179, 180, 7, 62, 2, 2, 180, 181, 7, 64, 2, 2,
181, 56, 3, 2, 2, 2, 182, 183, 7, 62, 2, 2, 183, 184, 7, 63, 2, 2, 184,
58, 3, 2, 2, 2, 185, 186, 7, 78, 2, 2, 186, 187, 7, 75, 2, 2, 187, 188,
7, 77, 2, 2, 188, 189, 7, 71, 2, 2, 189, 60, 3, 2, 2, 2, 190, 191, 7, 71,
2, 2, 191, 192, 7, 90, 2, 2, 192, 193, 7, 75, 2, 2, 193, 194, 7, 85, 2,
2, 194, 195, 7, 86, 2, 2, 195, 196, 7, 85, 2, 2, 196, 62, 3, 2, 2, 2, 197,
198, 7, 75, 2, 2, 198, 199, 7, 80, 2, 2, 199, 64, 3, 2, 2, 2, 200, 201,
7, 86, 2, 2, 201, 202, 7, 84, 2, 2, 202, 203, 7, 87, 2, 2, 203, 204, 7,
71, 2, 2, 204, 66, 3, 2, 2, 2, 205, 206, 7, 72, 2, 2, 206, 207, 7, 67,
2, 2, 207, 208, 7, 78, 2, 2, 208, 209, 7, 85, 2, 2, 209, 210, 7, 71, 2,
2, 210, 68, 3, 2, 2, 2, 211, 212, 5, 7, 4, 2, 212, 70, 3, 2, 2, 2, 213,
214, 5, 9, 5, 2, 214, 72, 3, 2, 2, 2, 215, 217, 5, 11, 6, 2, 216, 215,
3, 2, 2, 2, 217, 218, 3, 2, 2, 2, 218, 216, 3, 2, 2, 2, 218, 219, 3, 2,
2, 2, 219, 74, 3, 2, 2, 2, 220, 222, 9, 9, 2, 2, 221, 220, 3, 2, 2, 2,
222, 223, 3, 2, 2, 2, 223, 221, 3, 2, 2, 2, 223, 224, 3, 2, 2, 2, 224,
76, 3, 2, 2, 2, 225, 227, 9, 3, 2, 2, 226, 225, 3, 2, 2, 2, 227, 228, 3,
2, 2, 2, 228, 226, 3, 2, 2, 2, 228, 229, 3, 2, 2, 2, 229, 78, 3, 2, 2,
2, 230, 234, 9, 7, 2, 2, 231, 233, 9, 8, 2, 2, 232, 231, 3, 2, 2, 2, 233,
236, 3, 2, 2, 2, 234, 232, 3, 2, 2, 2, 234, 235, 3, 2, 2, 2, 235, 80, 3,
2, 2, 2, 236, 234, 3, 2, 2, 2, 15, 2, 84, 91, 99, 101, 112, 114, 125, 140,
218, 223, 228, 234, 3, 8, 2, 2,
}
var lexerChannelNames = []string{
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
}
var lexerModeNames = []string{
"DEFAULT_MODE",
}
var lexerLiteralNames = []string{
"", "", "'('", "')'", "','", "'''", "'\"'", "'AND'", "'OR'", "'XOR'", "'NOT'",
"'*'", "'/'", "'%'", "'+'", "'-'", "'='", "'!='", "'>'", "'>='", "'<'",
"'<>'", "'<='", "'LIKE'", "'EXISTS'", "'IN'", "'TRUE'", "'FALSE'",
}
var lexerSymbolicNames = []string{
"", "SPACE", "LR_BRACKET", "RR_BRACKET", "COMMA", "SINGLE_QUOTE_SYMB",
"DOUBLE_QUOTE_SYMB", "AND", "OR", "XOR", "NOT", "STAR", "DIVIDE", "MODULE",
"PLUS", "MINUS", "EQUAL", "NOT_EQUAL", "GREATER", "GREATER_OR_EQUAL", "LESS",
"LESS_GREATER", "LESS_OR_EQUAL", "LIKE", "EXISTS", "IN", "TRUE", "FALSE",
"DQUOTED_STRING_LITERAL", "SQUOTED_STRING_LITERAL", "INTEGER_LITERAL",
"IDENTIFIER", "IDENTIFIER_WITH_NUMBER", "FUNCTION_IDENTIFIER_WITH_UNDERSCORE",
}
var lexerRuleNames = []string{
"SPACE", "ID_LITERAL", "DQUOTA_STRING", "SQUOTA_STRING", "INT_DIGIT", "FN_LITERAL",
"LR_BRACKET", "RR_BRACKET", "COMMA", "SINGLE_QUOTE_SYMB", "DOUBLE_QUOTE_SYMB",
"QUOTE_SYMB", "AND", "OR", "XOR", "NOT", "STAR", "DIVIDE", "MODULE", "PLUS",
"MINUS", "EQUAL", "NOT_EQUAL", "GREATER", "GREATER_OR_EQUAL", "LESS", "LESS_GREATER",
"LESS_OR_EQUAL", "LIKE", "EXISTS", "IN", "TRUE", "FALSE", "DQUOTED_STRING_LITERAL",
"SQUOTED_STRING_LITERAL", "INTEGER_LITERAL", "IDENTIFIER", "IDENTIFIER_WITH_NUMBER",
"FUNCTION_IDENTIFIER_WITH_UNDERSCORE",
}
type CESQLParserLexer struct {
*antlr.BaseLexer
channelNames []string
modeNames []string
// TODO: EOF string
}
// NewCESQLParserLexer produces a new lexer instance for the optional input antlr.CharStream.
//
// The *CESQLParserLexer instance produced may be reused by calling the SetInputStream method.
// The initial lexer configuration is expensive to construct, and the object is not thread-safe;
// however, if used within a Golang sync.Pool, the construction cost amortizes well and the
// objects can be used in a thread-safe manner.
func NewCESQLParserLexer(input antlr.CharStream) *CESQLParserLexer {
l := new(CESQLParserLexer)
lexerDeserializer := antlr.NewATNDeserializer(nil)
lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
lexerDecisionToDFA := make([]*antlr.DFA, len(lexerAtn.DecisionToState))
for index, ds := range lexerAtn.DecisionToState {
lexerDecisionToDFA[index] = antlr.NewDFA(ds, index)
}
l.BaseLexer = antlr.NewBaseLexer(input)
l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache())
l.channelNames = lexerChannelNames
l.modeNames = lexerModeNames
l.RuleNames = lexerRuleNames
l.LiteralNames = lexerLiteralNames
l.SymbolicNames = lexerSymbolicNames
l.GrammarFileName = "CESQLParser.g4"
// TODO: l.EOF = antlr.TokenEOF
return l
}
// CESQLParserLexer tokens.
const (
CESQLParserLexerSPACE = 1
CESQLParserLexerLR_BRACKET = 2
CESQLParserLexerRR_BRACKET = 3
CESQLParserLexerCOMMA = 4
CESQLParserLexerSINGLE_QUOTE_SYMB = 5
CESQLParserLexerDOUBLE_QUOTE_SYMB = 6
CESQLParserLexerAND = 7
CESQLParserLexerOR = 8
CESQLParserLexerXOR = 9
CESQLParserLexerNOT = 10
CESQLParserLexerSTAR = 11
CESQLParserLexerDIVIDE = 12
CESQLParserLexerMODULE = 13
CESQLParserLexerPLUS = 14
CESQLParserLexerMINUS = 15
CESQLParserLexerEQUAL = 16
CESQLParserLexerNOT_EQUAL = 17
CESQLParserLexerGREATER = 18
CESQLParserLexerGREATER_OR_EQUAL = 19
CESQLParserLexerLESS = 20
CESQLParserLexerLESS_GREATER = 21
CESQLParserLexerLESS_OR_EQUAL = 22
CESQLParserLexerLIKE = 23
CESQLParserLexerEXISTS = 24
CESQLParserLexerIN = 25
CESQLParserLexerTRUE = 26
CESQLParserLexerFALSE = 27
CESQLParserLexerDQUOTED_STRING_LITERAL = 28
CESQLParserLexerSQUOTED_STRING_LITERAL = 29
CESQLParserLexerINTEGER_LITERAL = 30
CESQLParserLexerIDENTIFIER = 31
CESQLParserLexerIDENTIFIER_WITH_NUMBER = 32
CESQLParserLexerFUNCTION_IDENTIFIER_WITH_UNDERSCORE = 33
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
// Code generated from CESQLParser.g4 by ANTLR 4.9. DO NOT EDIT.
package gen // CESQLParser
import "github.com/antlr/antlr4/runtime/Go/antlr"
// A complete Visitor for a parse tree produced by CESQLParserParser.
type CESQLParserVisitor interface {
antlr.ParseTreeVisitor
// Visit a parse tree produced by CESQLParserParser#cesql.
VisitCesql(ctx *CesqlContext) interface{}
// Visit a parse tree produced by CESQLParserParser#inExpression.
VisitInExpression(ctx *InExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#binaryComparisonExpression.
VisitBinaryComparisonExpression(ctx *BinaryComparisonExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#atomExpression.
VisitAtomExpression(ctx *AtomExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#existsExpression.
VisitExistsExpression(ctx *ExistsExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#binaryLogicExpression.
VisitBinaryLogicExpression(ctx *BinaryLogicExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#likeExpression.
VisitLikeExpression(ctx *LikeExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#functionInvocationExpression.
VisitFunctionInvocationExpression(ctx *FunctionInvocationExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#binaryMultiplicativeExpression.
VisitBinaryMultiplicativeExpression(ctx *BinaryMultiplicativeExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#unaryLogicExpression.
VisitUnaryLogicExpression(ctx *UnaryLogicExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#unaryNumericExpression.
VisitUnaryNumericExpression(ctx *UnaryNumericExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#subExpression.
VisitSubExpression(ctx *SubExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#binaryAdditiveExpression.
VisitBinaryAdditiveExpression(ctx *BinaryAdditiveExpressionContext) interface{}
// Visit a parse tree produced by CESQLParserParser#booleanAtom.
VisitBooleanAtom(ctx *BooleanAtomContext) interface{}
// Visit a parse tree produced by CESQLParserParser#integerAtom.
VisitIntegerAtom(ctx *IntegerAtomContext) interface{}
// Visit a parse tree produced by CESQLParserParser#stringAtom.
VisitStringAtom(ctx *StringAtomContext) interface{}
// Visit a parse tree produced by CESQLParserParser#identifierAtom.
VisitIdentifierAtom(ctx *IdentifierAtomContext) interface{}
// Visit a parse tree produced by CESQLParserParser#identifier.
VisitIdentifier(ctx *IdentifierContext) interface{}
// Visit a parse tree produced by CESQLParserParser#functionIdentifier.
VisitFunctionIdentifier(ctx *FunctionIdentifierContext) interface{}
// Visit a parse tree produced by CESQLParserParser#booleanLiteral.
VisitBooleanLiteral(ctx *BooleanLiteralContext) interface{}
// Visit a parse tree produced by CESQLParserParser#stringLiteral.
VisitStringLiteral(ctx *StringLiteralContext) interface{}
// Visit a parse tree produced by CESQLParserParser#integerLiteral.
VisitIntegerLiteral(ctx *IntegerLiteralContext) interface{}
// Visit a parse tree produced by CESQLParserParser#functionParameterList.
VisitFunctionParameterList(ctx *FunctionParameterListContext) interface{}
// Visit a parse tree produced by CESQLParserParser#setExpression.
VisitSetExpression(ctx *SetExpressionContext) interface{}
}

12
sql/v2/go.mod Normal file
View File

@ -0,0 +1,12 @@
module github.com/cloudevents/sdk-go/sql/v2
go 1.14
require (
github.com/antlr/antlr4 v0.0.0-20210105192202-5c2b686f95e1
github.com/cloudevents/sdk-go/v2 v2.4.1
github.com/stretchr/testify v1.5.1
sigs.k8s.io/yaml v1.2.0
)
replace github.com/cloudevents/sdk-go/v2 => ../../v2

49
sql/v2/go.sum Normal file
View File

@ -0,0 +1,49 @@
github.com/antlr/antlr4 v0.0.0-20210105192202-5c2b686f95e1 h1:9K5yytxEEQc4yIn6c1rvQD6qQilQn9mYIF7pXKPT8i4=
github.com/antlr/antlr4 v0.0.0-20210105192202-5c2b686f95e1/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
github.com/antlr/antlr4 v0.0.0-20210412152734-e404d26f6142 h1:ucCGXTGwxNdEi4/QGohyvI0e3FCsS8cdCW15x2e1bDk=
github.com/antlr/antlr4 v0.0.0-20210412152734-e404d26f6142/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@ -0,0 +1,44 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package parser
import (
"unicode"
"github.com/antlr/antlr4/runtime/Go/antlr"
)
// Took from https://github.com/antlr/antlr4/blob/master/doc/resources/case_changing_stream.go
// CaseChangingStream wraps an existing CharStream, but upper cases, or
// lower cases the input before it is tokenized.
type CaseChangingStream struct {
antlr.CharStream
upper bool
}
// NewCaseChangingStream returns a new CaseChangingStream that forces
// all tokens read from the underlying stream to be either upper case
// or lower case based on the upper argument.
func NewCaseChangingStream(in antlr.CharStream, upper bool) *CaseChangingStream {
return &CaseChangingStream{in, upper}
}
// LA gets the value of the symbol at offset from the current position
// from the underlying CharStream and converts it to either upper case
// or lower case.
func (is *CaseChangingStream) LA(offset int) int {
in := is.CharStream.LA(offset)
if in < 0 {
// Such as antlr.TokenEOF which is -1
return in
}
if is.upper {
return int(unicode.ToUpper(rune(in)))
}
return int(unicode.ToLower(rune(in)))
}

View File

@ -0,0 +1,343 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package parser
import (
"strconv"
"strings"
"github.com/antlr/antlr4/runtime/Go/antlr"
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/expression"
"github.com/cloudevents/sdk-go/sql/v2/gen"
cloudevents "github.com/cloudevents/sdk-go/v2"
)
type expressionVisitor struct {
parsingErrors []error
}
var _ gen.CESQLParserVisitor = (*expressionVisitor)(nil)
func NewExpressionVisitor() gen.CESQLParserVisitor {
return &expressionVisitor{}
}
// antlr.ParseTreeVisitor implementation
func (v *expressionVisitor) Visit(tree antlr.ParseTree) interface{} {
// If you're wondering why I had to manually implement this stuff:
// https://github.com/antlr/antlr4/issues/2504
switch tree.(type) {
case *gen.CesqlContext:
return v.VisitCesql(tree.(*gen.CesqlContext))
case *gen.AtomExpressionContext:
return v.VisitAtomExpression(tree.(*gen.AtomExpressionContext))
case *gen.UnaryNumericExpressionContext:
return v.VisitUnaryNumericExpression(tree.(*gen.UnaryNumericExpressionContext))
case *gen.UnaryLogicExpressionContext:
return v.VisitUnaryLogicExpression(tree.(*gen.UnaryLogicExpressionContext))
case *gen.BooleanAtomContext:
return v.VisitBooleanAtom(tree.(*gen.BooleanAtomContext))
case *gen.BooleanLiteralContext:
return v.VisitBooleanLiteral(tree.(*gen.BooleanLiteralContext))
case *gen.IntegerAtomContext:
return v.VisitIntegerAtom(tree.(*gen.IntegerAtomContext))
case *gen.IntegerLiteralContext:
return v.VisitIntegerLiteral(tree.(*gen.IntegerLiteralContext))
case *gen.StringAtomContext:
return v.VisitStringAtom(tree.(*gen.StringAtomContext))
case *gen.StringLiteralContext:
return v.VisitStringLiteral(tree.(*gen.StringLiteralContext))
case *gen.ExistsExpressionContext:
return v.VisitExistsExpression(tree.(*gen.ExistsExpressionContext))
case *gen.InExpressionContext:
return v.VisitInExpression(tree.(*gen.InExpressionContext))
case *gen.IdentifierAtomContext:
return v.VisitIdentifierAtom(tree.(*gen.IdentifierAtomContext))
case *gen.IdentifierContext:
return v.VisitIdentifier(tree.(*gen.IdentifierContext))
case *gen.BinaryMultiplicativeExpressionContext:
return v.VisitBinaryMultiplicativeExpression(tree.(*gen.BinaryMultiplicativeExpressionContext))
case *gen.BinaryAdditiveExpressionContext:
return v.VisitBinaryAdditiveExpression(tree.(*gen.BinaryAdditiveExpressionContext))
case *gen.SubExpressionContext:
return v.VisitSubExpression(tree.(*gen.SubExpressionContext))
case *gen.BinaryLogicExpressionContext:
return v.VisitBinaryLogicExpression(tree.(*gen.BinaryLogicExpressionContext))
case *gen.BinaryComparisonExpressionContext:
return v.VisitBinaryComparisonExpression(tree.(*gen.BinaryComparisonExpressionContext))
case *gen.LikeExpressionContext:
return v.VisitLikeExpression(tree.(*gen.LikeExpressionContext))
case *gen.FunctionInvocationExpressionContext:
return v.VisitFunctionInvocationExpression(tree.(*gen.FunctionInvocationExpressionContext))
}
return nil
}
func (v *expressionVisitor) VisitChildren(node antlr.RuleNode) interface{} {
return v.Visit(node.GetChild(0).(antlr.ParseTree))
}
func (v *expressionVisitor) VisitTerminal(node antlr.TerminalNode) interface{} {
// We never visit terminal nodes
return nil
}
func (v *expressionVisitor) VisitErrorNode(node antlr.ErrorNode) interface{} {
// We already collect errors using the error listener
return nil
}
// gen.CESQLParserVisitor implementation
func (v *expressionVisitor) VisitInExpression(ctx *gen.InExpressionContext) interface{} {
leftExpression := v.Visit(ctx.Expression()).(cesql.Expression)
var setExpression []cesql.Expression
for _, expr := range ctx.SetExpression().(*gen.SetExpressionContext).AllExpression() {
setExpression = append(setExpression, v.Visit(expr).(cesql.Expression))
}
if ctx.NOT() != nil {
return expression.NewNotExpression(expression.NewInExpression(leftExpression, setExpression))
}
return expression.NewInExpression(leftExpression, setExpression)
}
func (v *expressionVisitor) VisitBinaryComparisonExpression(ctx *gen.BinaryComparisonExpressionContext) interface{} {
if ctx.LESS() != nil {
return expression.NewLessExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else if ctx.LESS_OR_EQUAL() != nil {
return expression.NewLessOrEqualExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else if ctx.GREATER() != nil {
return expression.NewGreaterExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else if ctx.GREATER_OR_EQUAL() != nil {
return expression.NewGreaterOrEqualExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else if ctx.EQUAL() != nil {
return expression.NewEqualExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else {
return expression.NewNotEqualExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
}
}
func (v *expressionVisitor) VisitExistsExpression(ctx *gen.ExistsExpressionContext) interface{} {
return expression.NewExistsExpression(strings.ToLower(ctx.Identifier().GetText()))
}
func (v *expressionVisitor) VisitBinaryLogicExpression(ctx *gen.BinaryLogicExpressionContext) interface{} {
if ctx.AND() != nil {
return expression.NewAndExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else if ctx.OR() != nil {
return expression.NewOrExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else {
return expression.NewXorExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
}
}
func (v *expressionVisitor) VisitLikeExpression(ctx *gen.LikeExpressionContext) interface{} {
patternContext := ctx.StringLiteral().(*gen.StringLiteralContext)
var pattern string
if patternContext.DQUOTED_STRING_LITERAL() != nil {
// Parse double quoted string
pattern = dQuotedStringToString(patternContext.DQUOTED_STRING_LITERAL().GetText())
} else {
// Parse single quoted string
pattern = sQuotedStringToString(patternContext.SQUOTED_STRING_LITERAL().GetText())
}
likeExpression, err := expression.NewLikeExpression(v.Visit(ctx.Expression()).(cesql.Expression), pattern)
if err != nil {
v.parsingErrors = append(v.parsingErrors, err)
return noopExpression{}
}
if ctx.NOT() != nil {
return expression.NewNotExpression(likeExpression)
}
return likeExpression
}
func (v *expressionVisitor) VisitFunctionInvocationExpression(ctx *gen.FunctionInvocationExpressionContext) interface{} {
paramsCtx := ctx.FunctionParameterList().(*gen.FunctionParameterListContext)
name := ctx.FunctionIdentifier().GetText()
var args []cesql.Expression
for _, expr := range paramsCtx.AllExpression() {
args = append(args, v.Visit(expr).(cesql.Expression))
}
return expression.NewFunctionInvocationExpression(strings.ToUpper(name), args)
}
func (v *expressionVisitor) VisitBinaryMultiplicativeExpression(ctx *gen.BinaryMultiplicativeExpressionContext) interface{} {
if ctx.STAR() != nil {
return expression.NewMultiplicationExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else if ctx.MODULE() != nil {
return expression.NewModuleExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else {
return expression.NewDivisionExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
}
}
func (v *expressionVisitor) VisitUnaryLogicExpression(ctx *gen.UnaryLogicExpressionContext) interface{} {
return expression.NewNotExpression(
v.Visit(ctx.Expression()).(cesql.Expression),
)
}
func (v *expressionVisitor) VisitUnaryNumericExpression(ctx *gen.UnaryNumericExpressionContext) interface{} {
return expression.NewNegateExpression(
v.Visit(ctx.Expression()).(cesql.Expression),
)
}
func (v *expressionVisitor) VisitSubExpression(ctx *gen.SubExpressionContext) interface{} {
return v.Visit(ctx.Expression())
}
func (v *expressionVisitor) VisitBinaryAdditiveExpression(ctx *gen.BinaryAdditiveExpressionContext) interface{} {
if ctx.PLUS() != nil {
return expression.NewSumExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
} else {
return expression.NewDifferenceExpression(
v.Visit(ctx.Expression(0)).(cesql.Expression),
v.Visit(ctx.Expression(1)).(cesql.Expression),
)
}
}
func (v *expressionVisitor) VisitIdentifier(ctx *gen.IdentifierContext) interface{} {
return expression.NewIdentifierExpression(strings.ToLower(ctx.GetText()))
}
func (v *expressionVisitor) VisitBooleanLiteral(ctx *gen.BooleanLiteralContext) interface{} {
return expression.NewLiteralExpression(ctx.TRUE() != nil)
}
func (v *expressionVisitor) VisitStringLiteral(ctx *gen.StringLiteralContext) interface{} {
var str string
if ctx.DQUOTED_STRING_LITERAL() != nil {
// Parse double quoted string
str = dQuotedStringToString(ctx.DQUOTED_STRING_LITERAL().GetText())
} else {
// Parse single quoted string
str = sQuotedStringToString(ctx.SQUOTED_STRING_LITERAL().GetText())
}
return expression.NewLiteralExpression(str)
}
func (v *expressionVisitor) VisitIntegerLiteral(ctx *gen.IntegerLiteralContext) interface{} {
val, err := strconv.Atoi(ctx.GetText())
if err != nil {
v.parsingErrors = append(v.parsingErrors, err)
}
return expression.NewLiteralExpression(int32(val))
}
// gen.CESQLParserVisitor implementation - noop methods
func (v *expressionVisitor) VisitCesql(ctx *gen.CesqlContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *expressionVisitor) VisitAtomExpression(ctx *gen.AtomExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *expressionVisitor) VisitBooleanAtom(ctx *gen.BooleanAtomContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *expressionVisitor) VisitIntegerAtom(ctx *gen.IntegerAtomContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *expressionVisitor) VisitStringAtom(ctx *gen.StringAtomContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *expressionVisitor) VisitIdentifierAtom(ctx *gen.IdentifierAtomContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *expressionVisitor) VisitSetExpression(ctx *gen.SetExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *expressionVisitor) VisitFunctionIdentifier(ctx *gen.FunctionIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *expressionVisitor) VisitFunctionParameterList(ctx *gen.FunctionParameterListContext) interface{} {
return v.VisitChildren(ctx)
}
// noop expression. This is necessary to continue to walk through the tree, even if there's a failure in the parsing
type noopExpression struct{}
func (n noopExpression) Evaluate(cloudevents.Event) (interface{}, error) {
return 0, nil
}
// Utilities
func dQuotedStringToString(str string) string {
str = str[1 : len(str)-1]
return strings.ReplaceAll(str, "\\\"", "\"")
}
func sQuotedStringToString(str string) string {
str = str[1 : len(str)-1]
return strings.ReplaceAll(str, "\\'", "'")
}

74
sql/v2/parser/parser.go Normal file
View File

@ -0,0 +1,74 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package parser
import (
"errors"
"fmt"
"strings"
"github.com/antlr/antlr4/runtime/Go/antlr"
"github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/gen"
)
type Parser struct {
// TODO parser options
}
func (p *Parser) Parse(input string) (v2.Expression, error) {
var is antlr.CharStream = antlr.NewInputStream(input)
is = NewCaseChangingStream(is, true)
// Create the JSON Lexer
lexer := gen.NewCESQLParserLexer(is)
var stream antlr.TokenStream = antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
// Create the JSON Parser
antlrParser := gen.NewCESQLParserParser(stream)
antlrParser.RemoveErrorListeners()
collectingErrorListener := errorListener{}
antlrParser.AddErrorListener(&collectingErrorListener)
// Finally walk the tree
visitor := expressionVisitor{}
result := antlrParser.Cesql().Accept(&visitor)
if result == nil {
return nil, mergeErrs(append(collectingErrorListener.errs, visitor.parsingErrors...))
}
return result.(v2.Expression), mergeErrs(append(collectingErrorListener.errs, visitor.parsingErrors...))
}
type errorListener struct {
antlr.DefaultErrorListener
errs []error
}
func (d *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
d.errs = append(d.errs, fmt.Errorf("syntax error: %v", e.GetMessage()))
}
func mergeErrs(errs []error) error {
if len(errs) == 0 {
return nil
}
var errStrings []string
for _, err := range errs {
errStrings = append(errStrings, err.Error())
}
return errors.New(strings.Join(errStrings, ","))
}
var defaultParser = Parser{}
func Parse(input string) (v2.Expression, error) {
return defaultParser.Parse(input)
}

View File

@ -0,0 +1,107 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package runtime
import (
"errors"
"strings"
cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/function"
)
type functionItem struct {
fixedArgsFunctions map[int]cesql.Function
variadicFunction cesql.Function
}
type functionTable map[string]*functionItem
func (table functionTable) AddFunction(function cesql.Function) error {
item := table[function.Name()]
if item == nil {
item = &functionItem{
fixedArgsFunctions: make(map[int]cesql.Function),
}
table[function.Name()] = item
}
if function.IsVariadic() {
if item.variadicFunction != nil {
return errors.New("cannot add the variadic function, " +
"because there is already another variadic function defined with the same name")
}
maxArity := -1
for a := range item.fixedArgsFunctions {
if a > maxArity {
maxArity = a
}
}
if maxArity >= function.Arity() {
return errors.New("cannot add the variadic function, " +
"because there is already another function defined with the same name and same or greater arity")
}
item.variadicFunction = function
return nil
} else {
if _, ok := item.fixedArgsFunctions[function.Arity()]; ok {
return errors.New("cannot add the function, " +
"because there is already another function defined with the same arity and same name")
}
item.fixedArgsFunctions[function.Arity()] = function
return nil
}
}
func (table functionTable) ResolveFunction(name string, args int) cesql.Function {
item := table[strings.ToUpper(name)]
if item == nil {
return nil
}
if fn, ok := item.fixedArgsFunctions[args]; ok {
return fn
}
if item.variadicFunction == nil || item.variadicFunction.Arity() > args {
return nil
}
return item.variadicFunction
}
var globalFunctionTable = functionTable{}
func init() {
for _, fn := range []cesql.Function{
function.IntFunction,
function.BoolFunction,
function.StringFunction,
function.IsBoolFunction,
function.IsIntFunction,
function.AbsFunction,
function.LengthFunction,
function.ConcatFunction,
function.ConcatWSFunction,
function.LowerFunction,
function.UpperFunction,
function.TrimFunction,
function.LeftFunction,
function.RightFunction,
function.SubstringFunction,
function.SubstringWithLengthFunction,
} {
if err := globalFunctionTable.AddFunction(fn); err != nil {
panic(err)
}
}
}
func ResolveFunction(name string, args int) cesql.Function {
return globalFunctionTable.ResolveFunction(name, args)
}

View File

@ -0,0 +1,90 @@
name: Binary comparison operations
tests:
- name: True is equal to false
expression: TRUE = FALSE
result: false
- name: False is equal to false
expression: FALSE = FALSE
result: true
- name: 1 is equal to 2
expression: 1 = 2
result: false
- name: 2 is equal to 2
expression: 2 = 2
result: true
- name: abc is equal to 123
expression: "'abc' = '123'"
result: false
- name: abc is equal to abc
expression: "'abc' = 'abc'"
result: true
- name: True is not equal to false
expression: TRUE != FALSE
result: true
- name: False is not equal to false
expression: FALSE != FALSE
result: false
- name: 1 is not equal to 2
expression: 1 != 2
result: true
- name: 2 is not equal to 2
expression: 2 != 2
result: false
- name: abc is not equal to 123
expression: "'abc' != '123'"
result: true
- name: abc is not equal to abc
expression: "'abc' != 'abc'"
result: false
- name: True is not equal to false (diamond operator)
expression: TRUE <> FALSE
result: true
- name: False is not equal to false (diamond operator)
expression: FALSE <> FALSE
result: false
- name: 1 is not equal to 2 (diamond operator)
expression: 1 <> 2
result: true
- name: 2 is not equal to 2 (diamond operator)
expression: 2 <> 2
result: false
- name: abc is not equal to 123 (diamond operator)
expression: "'abc' <> '123'"
result: true
- name: abc is not equal to abc (diamond operator)
expression: "'abc' <> 'abc'"
result: false
- name: 1 is less or equal than 2
expression: 2 <= 2
result: true
- name: 3 is less or equal than 2
expression: 3 <= 2
result: false
- name: 1 is less than 2
expression: 1 < 2
result: true
- name: 2 is less than 2
expression: 2 < 2
result: false
- name: 2 is greater or equal than 2
expression: 2 >= 2
result: true
- name: 2 is greater or equal than 3
expression: 2 >= 3
result: false
- name: 2 is greater than 1
expression: 2 > 1
result: true
- name: 2 is greater than 2
expression: 2 > 2
result: false
- name: implicit casting with string as right type
expression: "true = 'TRUE'"
result: false
- name: implicit casting with boolean as right type
expression: "'TRUE' = true"
result: true

View File

@ -0,0 +1,40 @@
name: Binary logical operations
tests:
- name: False and false
expression: FALSE AND FALSE
result: false
- name: False and true
expression: FALSE AND TRUE
result: false
- name: True and false
expression: TRUE AND FALSE
result: false
- name: True and true
expression: TRUE AND TRUE
result: true
- name: False or false
expression: FALSE OR FALSE
result: false
- name: False or true
expression: FALSE OR TRUE
result: true
- name: True or false
expression: TRUE OR FALSE
result: true
- name: True or true
expression: TRUE OR TRUE
result: true
- name: False xor false
expression: FALSE XOR FALSE
result: false
- name: False xor true
expression: FALSE XOR TRUE
result: true
- name: True xor false
expression: TRUE XOR FALSE
result: true
- name: True xor true
expression: TRUE XOR TRUE
result: false

View File

@ -0,0 +1,60 @@
name: Binary math operations
tests:
- name: Operator precedence without parenthesis
expression: 4 * 2 + 4 / 2
result: 10
- name: Operator precedence with parenthesis
expression: 4 * (2 + 4) / 2
result: 12
- name: Truncated division
expression: 5 / 3
result: 1
- name: Division by zero returns 0 and fail
expression: 5 / 0
result: 0
error: math
- name: Module
expression: 5 % 2
result: 1
- name: Module by zero returns 0 and fail
expression: 5 % 0
result: 0
error: math
- name: Positive plus positive number
expression: 4 + 1
result: 5
- name: Negative plus positive number
expression: -4 + 1
result: -3
- name: Negative plus Negative number
expression: -4 + -1
result: -5
- name: Positive plus negative number
expression: 4 + -1
result: 3
- name: Positive minus positive number
expression: 4 - 1
result: 3
- name: Negative minus positive number
expression: -4 - 1
result: -5
- name: Implicit casting, with left value string
expression: "'5' + 3"
result: 8
- name: Implicit casting, with right value string
expression: "5 + '3'"
result: 8
- name: Implicit casting, with both values string
expression: "'5' + '3'"
result: 8
- name: Implicit casting, with invalid boolean value
expression: "5 + TRUE"
result: 5
error: cast
- name: Implicit casting, with invalid string value
expression: "'5avc4' + 10"
result: 10
error: cast

View File

@ -0,0 +1,25 @@
name: Case sensitivity
tests:
- name: TRUE
expression: TRUE
result: true
- name: true
expression: true
result: true
- name: tRuE
expression: tRuE
result: true
- name: FALSE
expression: FALSE
result: false
- name: false
expression: false
result: false
- name: FaLsE
expression: FaLsE
result: false
- name: String literals casing preserved
expression: "'aBcD'"
result: aBcD

View File

@ -0,0 +1,89 @@
name: Casting functions
tests:
- name: Cast '1' to integer
expression: INT('1')
result: 1
- name: Cast '-1' to integer
expression: INT('-1')
result: -1
- name: Cast identity 1
expression: INT(1)
result: 1
- name: Cast identity -1
expression: INT(-1)
result: -1
- name: Invalid cast from boolean to int
expression: INT(TRUE)
result: 0
error: cast
- name: Invalid cast from string to int
expression: INT('ABC')
result: 0
error: cast
- name: Cast 'TRUE' to boolean
expression: BOOL('TRUE')
result: true
- name: Cast "false" to boolean
expression: BOOL("false")
result: false
- name: Cast identity TRUE
expression: BOOL(TRUE)
result: true
- name: Cast identity FALSE
expression: BOOL(FALSE)
result: FALSE
- name: Invalid cast from string to boolean
expression: BOOL('ABC')
result: false
error: cast
- name: Invalid cast from int to boolean
expression: BOOL(1)
result: false
error: cast
- name: Cast TRUE to string
expression: STRING(TRUE)
result: 'true'
- name: Cast FALSE to string
expression: STRING(FALSE)
result: 'false'
- name: Cast 1 to string
expression: STRING(1)
result: '1'
- name: Cast -1 to string
expression: STRING(-1)
result: '-1'
- name: Cast identity "abc"
expression: STRING("abc")
result: "abc"
- name: "'true' is a boolean"
expression: IS_BOOL('true')
result: true
- name: "'FALSE' is a boolean"
expression: IS_BOOL('FALSE')
result: true
- name: 1 is not a boolean
expression: IS_BOOL(1)
result: false
- name: "'abc' is not a boolean"
expression: IS_BOOL('abc')
result: false
- name: "'-1' is an int"
expression: IS_INT('-1')
result: true
- name: "'1' is an int"
expression: IS_INT('1')
result: true
- name: true is not an int
expression: IS_INT(TRUE)
result: false
- name: "'abc' is not an int"
expression: IS_INT('abc')
result: false
- name: IS_STRING does not exists
expression: IS_STRING('ABC')
error: missingFunction

View File

@ -0,0 +1,53 @@
name: Context attributest test
tests:
- name: Access to required attribute
expression: id
eventOverrides:
id: myId
result: myId
- name: Access to optional attribute
expression: subject
eventOverrides:
subject: mySubject
result: mySubject
- name: Absent optional attribute
expression: subject
event:
specversion: "1.0"
id: myId
source: localhost.localdomain
type: myType
result: ""
error: missingAttribute
- name: Access to optional boolean extension
expression: mybool
eventOverrides:
mybool: true
result: true
- name: Access to optional integer extension
expression: myint
eventOverrides:
myint: 10
result: 10
- name: Access to optional string extension
expression: myext
eventOverrides:
myext: "my extension"
result: "my extension"
- name: URL type cohercion to string
expression: source
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
result: "http://localhost/source"
- name: Timestamp type cohercion to string
expression: time
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
time: 2018-04-26T14:48:09+02:00
result: 2018-04-26T14:48:09+02:00

View File

@ -0,0 +1,57 @@
name: Exists expression
tests:
- name: required attributes always exist
expression: EXISTS specversion AND EXISTS id AND EXISTS type AND EXISTS SOURCE
result: true
- name: optional attribute available
expression: EXISTS time
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
time: 2018-04-26T14:48:09+02:00
result: true
- name: optional attribute absent
expression: EXISTS time
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
result: false
- name: optional attribute absent (negated)
expression: NOT EXISTS time
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
result: true
- name: optional extension available
expression: EXISTS myext
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
myext: my value
result: true
- name: optional extension absent
expression: EXISTS myext
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
result: false
- name: optional extension absent (negated)
expression: NOT EXISTS myext
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
result: true

View File

@ -0,0 +1,78 @@
name: In expression
tests:
- name: int in int set
expression: 123 IN (1, 2, 3, 12, 13, 23, 123)
result: true
- name: int not in int set
expression: 123 NOT IN (1, 2, 3, 12, 13, 23, 123)
result: false
- name: string in string set
expression: "'abc' IN ('abc', \"bcd\")"
result: true
- name: string not in string set
expression: "'aaa' IN ('abc', \"bcd\")"
result: false
- name: bool in bool set
expression: TRUE IN (TRUE, FALSE)
result: true
- name: bool not in bool set
expression: TRUE IN (FALSE)
result: false
- name: mix literals and identifiers (1)
expression: source IN (myext, 'abc')
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
myext: "http://localhost/source"
result: true
- name: mix literals and identifiers (2)
expression: source IN (source)
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
myext: "http://localhost/source"
result: true
- name: mix literals and identifiers (3)
expression: "source IN (id, \"http://localhost/source\")"
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
myext: "http://localhost/source"
result: true
- name: mix literals and identifiers (4)
expression: source IN (id, 'xyz')
event:
specversion: "1.0"
id: myId
source: "http://localhost/source"
type: myType
result: false
- name: type coercion with booleans (1)
expression: "'true' IN (TRUE, 'false')"
result: true
- name: type coercion with booleans (2)
expression: "'true' IN ('TRUE', 'false')"
result: false
- name: type coercion with booleans (3)
expression: TRUE IN ('true', 'false')
result: true
- name: type coercion with booleans (4)
expression: "'TRUE' IN (TRUE, 'false')"
result: false
- name: type coercion with int (1)
expression: "1 IN ('1', '2')"
result: true
- name: type coercion with int (2)
expression: "'1' IN (1, 2)"
result: true

View File

@ -0,0 +1,11 @@
name: Integer builtin functions
tests:
- name: ABS (1)
expression: ABS(10)
result: 10
- name: ABS (2)
expression: ABS(-10)
result: 10
- name: ABS (3)
expression: ABS(0)
result: 0

View File

@ -0,0 +1,80 @@
name: Like expression
tests:
- name: Exact match
expression: "'abc' LIKE 'abc'"
result: true
- name: Exact match (negate)
expression: "'abc' NOT LIKE 'abc'"
result: false
- name: Percentage operator (1)
expression: "'abc' LIKE 'a%b%c'"
result: true
- name: Percentage operator (2)
expression: "'azbc' LIKE 'a%b%c'"
result: true
- name: Percentage operator (3)
expression: "'azzzbzzzc' LIKE 'a%b%c'"
result: true
- name: Percentage operator (4)
expression: "'a%b%c' LIKE 'a%b%c'"
result: true
- name: Percentage operator (5)
expression: "'ac' LIKE 'abc'"
result: false
- name: Percentage operator (6)
expression: "'' LIKE 'abc'"
result: false
- name: Underscore operator (1)
expression: "'abc' LIKE 'a_b_c'"
result: false
- name: Underscore operator (2)
expression: "'a_b_c' LIKE 'a_b_c'"
result: true
- name: Underscore operator (3)
expression: "'abzc' LIKE 'a_b_c'"
result: false
- name: Underscore operator (4)
expression: "'azbc' LIKE 'a_b_c'"
result: false
- name: Underscore operator (5)
expression: "'azbzc' LIKE 'a_b_c'"
result: true
- name: Escaped underscore wildcards (1)
expression: "'a_b_c' LIKE 'a\\_b\\_c'"
result: true
- name: Escaped underscore wildcards (2)
expression: "'a_b_c' NOT LIKE 'a\\_b\\_c'"
result: false
- name: Escaped underscore wildcards (3)
expression: "'azbzc' LIKE 'a\\_b\\_c'"
result: false
- name: Escaped underscore wildcards (4)
expression: "'abc' LIKE 'a\\_b\\_c'"
result: false
- name: Escaped percentage wildcards (1)
expression: "'abc' LIKE 'a\\%b\\%c'"
result: false
- name: Escaped percentage wildcards (2)
expression: "'a%b%c' LIKE 'a\\%b\\%c'"
result: true
- name: Escaped percentage wildcards (3)
expression: "'azbzc' LIKE 'a\\%b\\%c'"
result: false
- name: Escaped percentage wildcards (4)
expression: "'abc' LIKE 'a\\%b\\%c'"
result: false
- name: With access to event attributes
expression: "myext LIKE 'abc%123\\%456\\_d_f'"
eventOverrides:
myext: "abc123123%456_dzf"
result: true
- name: With access to event attributes (negated)
expression: "myext NOT LIKE 'abc%123\\%456\\_d_f'"
eventOverrides:
myext: "abc123123%456_dzf"
result: false

View File

@ -0,0 +1,36 @@
name: Literals
tests:
- name: TRUE literal
expression: TRUE
result: true
- name: FALSE literal
expression: FALSE
result: false
- name: 0 literal
expression: 0
result: 0
- name: 1 literal
expression: 1
result: 1
- name: String literal single quoted
expression: "'abc'"
result: abc
- name: String literal double quoted
expression: "\"abc\""
result: abc
- name: String literal single quoted with case
expression: "'aBc'"
result: aBc
- name: String literal double quoted with case
expression: "\"AbC\""
result: AbC
- name: Escaped string literal (1)
expression: "'a\"b\\'c'"
result: a"b'c
- name: Escaped string literal (2)
expression: "\"a'b\\\"c\""
result: a'b"c

View File

@ -0,0 +1,20 @@
name: Negate operator
tests:
- name: Minus 10
expression: -10
result: -10
- name: Minus minus 10
expression: --10
result: 10
- name: Minus 10 with casting
expression: -'10'
result: -10
- name: Minus minus 10 with casting
expression: --'10'
result: 10
- name: Invalid boolean cast
expression: -TRUE
result: 0
error: cast

View File

@ -0,0 +1,20 @@
name: Not operator
tests:
- name: Not true
expression: NOT TRUE
result: false
- name: Not false
expression: NOT FALSE
result: true
- name: Not true with casting
expression: NOT 'TRUE'
result: false
- name: Not false 10 with casting
expression: NOT 'FALSE'
result: true
- name: Invalid int cast
expression: NOT 10
result: true
error: cast

View File

@ -0,0 +1,5 @@
name: Parsing errors
tests:
- name: No closed parenthesis
expression: ABC(
error: parse

View File

@ -0,0 +1,64 @@
name: Specification examples
tests:
- name: Case insensitive hops (1)
expression: int(hop) < int(ttl) and int(hop) < 1000
eventOverrides:
hop: '5'
ttl: '10'
result: true
- name: Case insensitive hops (2)
expression: INT(hop) < INT(ttl) AND INT(hop) < 1000
eventOverrides:
hop: '5'
ttl: '10'
result: true
- name: Case insensitive hops (3)
expression: hop < ttl
eventOverrides:
hop: '5'
ttl: '10'
result: true
- name: Equals with casting (1)
expression: sequence = 5
eventOverrides:
sequence: '5'
result: true
- name: Equals with casting (2)
expression: sequence = 5
eventOverrides:
sequence: '6'
result: false
- name: Logic expression (1)
expression: firstname = 'Francesco' OR subject = 'Francesco'
eventOverrides:
subject: Francesco
firstname: Doug
result: true
- name: Logic expression (2)
expression: firstname = 'Francesco' OR subject = 'Francesco'
eventOverrides:
firstname: Francesco
subject: Doug
result: true
- name: Logic expression (3)
expression: (firstname = 'Francesco' AND lastname = 'Guardiani') OR subject = 'Francesco Guardiani'
eventOverrides:
subject: Doug
firstname: Francesco
lastname: Guardiani
result: true
- name: Logic expression (4)
expression: (firstname = 'Francesco' AND lastname = 'Guardiani') OR subject = 'Francesco Guardiani'
eventOverrides:
subject: Francesco Guardiani
firstname: Doug
lastname: Davis
result: true
- name: Subject exists
expression: EXISTS subject
eventOverrides:
subject: Francesco Guardiani
result: true

View File

@ -0,0 +1,142 @@
name: String builtin functions
tests:
- name: LENGTH (1)
expression: "LENGTH('abc')"
result: 3
- name: LENGTH (2)
expression: "LENGTH('')"
result: 0
- name: LENGTH (3)
expression: "LENGTH('2')"
result: 1
- name: LENGTH (4)
expression: "LENGTH(TRUE)"
result: 4
- name: CONCAT (1)
expression: "CONCAT('a', 'b', 'c')"
result: abc
- name: CONCAT (2)
expression: "CONCAT()"
result: ""
- name: CONCAT (3)
expression: "CONCAT('a')"
result: "a"
- name: CONCAT_WS (1)
expression: "CONCAT_WS(',', 'a', 'b', 'c')"
result: a,b,c
- name: CONCAT_WS (2)
expression: "CONCAT_WS(',')"
result: ""
- name: CONCAT_WS (3)
expression: "CONCAT_WS(',', 'a')"
result: "a"
- name: CONCAT_WS without arguments doesn't exist
expression: CONCAT_WS()
error: missingFunction
- name: LOWER (1)
expression: "LOWER('ABC')"
result: abc
- name: LOWER (2)
expression: "LOWER('AbC')"
result: abc
- name: LOWER (3)
expression: "LOWER('abc')"
result: abc
- name: UPPER (1)
expression: "UPPER('ABC')"
result: ABC
- name: UPPER (2)
expression: "UPPER('AbC')"
result: ABC
- name: UPPER (3)
expression: "UPPER('abc')"
result: ABC
- name: TRIM (1)
expression: "TRIM(' a b c ')"
result: "a b c"
- name: TRIM (2)
expression: "TRIM(' a b c')"
result: "a b c"
- name: TRIM (3)
expression: "TRIM('a b c ')"
result: "a b c"
- name: TRIM (4)
expression: "TRIM('a b c')"
result: "a b c"
- name: LEFT (1)
expression: LEFT('abc', 2)
result: ab
- name: LEFT (2)
expression: LEFT('abc', 10)
result: abc
- name: LEFT (3)
expression: LEFT('', 0)
result: ""
- name: LEFT (4)
expression: LEFT('abc', -2)
result: "abc"
error: functionEvaluation
- name: RIGHT (1)
expression: RIGHT('abc', 2)
result: bc
- name: RIGHT (2)
expression: RIGHT('abc', 10)
result: abc
- name: RIGHT (3)
expression: RIGHT('', 0)
result: ""
- name: RIGHT (4)
expression: RIGHT('abc', -2)
result: "abc"
error: functionEvaluation
- name: SUBSTRING (1)
expression: "SUBSTRING('abcdef', 1)"
result: "abcdef"
- name: SUBSTRING (2)
expression: "SUBSTRING('abcdef', 2)"
result: "bcdef"
- name: SUBSTRING (3)
expression: "SUBSTRING('Quadratically', 5)"
result: "ratically"
- name: SUBSTRING (4)
expression: "SUBSTRING('Sakila', -3)"
result: "ila"
- name: SUBSTRING (5)
expression: "SUBSTRING('abcdef', 1, 6)"
result: "abcdef"
- name: SUBSTRING (6)
expression: "SUBSTRING('abcdef', 2, 4)"
result: "bcde"
- name: SUBSTRING (7)
expression: "SUBSTRING('Sakila', -5, 3)"
result: "aki"
- name: SUBSTRING (8)
expression: "SUBSTRING('Quadratically', 0)"
result: ""
- name: SUBSTRING (9)
expression: "SUBSTRING('Quadratically', 0, 1)"
result: ""
- name: SUBSTRING (10)
expression: "SUBSTRING('abcdef', 10)"
result: ""
error: functionEvaluation
- name: SUBSTRING (11)
expression: "SUBSTRING('abcdef', -10)"
result: ""
error: functionEvaluation
- name: SUBSTRING (12)
expression: "SUBSTRING('abcdef', 10, 10)"
result: ""
error: functionEvaluation
- name: SUBSTRING (13)
expression: "SUBSTRING('abcdef', -10, 10)"
result: ""
error: functionEvaluation

View File

@ -0,0 +1,12 @@
name: Sub expressions
tests:
- name: Sub expression with literal
expression: "(TRUE)"
result: true
- name: Math (1)
expression: "4 * (2 + 3)"
result: 20
- name: Math (2)
expression: "(2 + 3) * 4"
result: 20

157
sql/v2/test/tck_test.go Normal file
View File

@ -0,0 +1,157 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package test
import (
"io/ioutil"
"os"
"path"
"runtime"
"testing"
"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"
"github.com/cloudevents/sdk-go/sql/v2/parser"
cloudevents "github.com/cloudevents/sdk-go/v2"
"github.com/cloudevents/sdk-go/v2/binding/spec"
"github.com/cloudevents/sdk-go/v2/event"
"github.com/cloudevents/sdk-go/v2/test"
)
var TCKFileNames = []string{
"binary_math_operators",
"binary_logical_operators",
"binary_comparison_operators",
"case_sensitivity",
"casting_functions",
"context_attributes_access",
"exists_expression",
"in_expression",
"integer_builtin_functions",
"like_expression",
"literals",
"negate_operator",
"not_operator",
"parse_errors",
"spec_examples",
"string_builtin_functions",
"sub_expression",
}
type ErrorType string
const (
ParseError ErrorType = "parse"
MathError ErrorType = "math"
CastError ErrorType = "cast"
MissingAttributeError ErrorType = "missingAttribute"
MissingFunctionError ErrorType = "missingFunction"
FunctionEvaluationError ErrorType = "functionEvaluation"
)
type TckFile struct {
Name string `json:"name"`
Tests []TckTestCase `json:"tests"`
}
type TckTestCase struct {
Name string `json:"name"`
Expression string `json:"expression"`
Result interface{} `json:"result"`
Error ErrorType `json:"error"`
Event *cloudevents.Event `json:"event"`
EventOverrides map[string]interface{} `json:"eventOverrides"`
}
func (tc TckTestCase) InputEvent(t *testing.T) cloudevents.Event {
var inputEvent cloudevents.Event
if tc.Event != nil {
inputEvent = *tc.Event
} else {
inputEvent = test.FullEvent()
}
// Make sure the event is v1
inputEvent.SetSpecVersion(event.CloudEventsVersionV1)
for k, v := range tc.EventOverrides {
require.NoError(t, spec.V1.SetAttribute(inputEvent.Context, k, v))
}
return inputEvent
}
func (tc TckTestCase) ExpectedResult() interface{} {
switch tc.Result.(type) {
case int:
return int32(tc.Result.(int))
case float64:
return int32(tc.Result.(float64))
}
return tc.Result
}
func TestTCK(t *testing.T) {
tckFiles := make([]TckFile, 0, len(TCKFileNames))
_, basePath, _, _ := runtime.Caller(0)
basePath, _ = path.Split(basePath)
for _, testFile := range TCKFileNames {
testFilePath := path.Join(basePath, "tck", testFile+".yaml")
t.Logf("Loading file %s", testFilePath)
file, err := os.Open(testFilePath)
require.NoError(t, err)
fileBytes, err := ioutil.ReadAll(file)
require.NoError(t, err)
tckFileModel := TckFile{}
require.NoError(t, yaml.Unmarshal(fileBytes, &tckFileModel))
tckFiles = append(tckFiles, tckFileModel)
}
for i, file := range tckFiles {
i := i
t.Run(file.Name, func(t *testing.T) {
for j, testCase := range tckFiles[i].Tests {
j := j
t.Run(testCase.Name, func(t *testing.T) {
t.Parallel()
testCase := tckFiles[i].Tests[j]
t.Logf("Test expression: '%s'", testCase.Expression)
if testCase.Error == ParseError {
_, err := parser.Parse(testCase.Expression)
require.NotNil(t, err)
return
}
expr, err := parser.Parse(testCase.Expression)
require.NoError(t, err)
require.NotNil(t, expr)
inputEvent := testCase.InputEvent(t)
result, err := expr.Evaluate(inputEvent)
if testCase.Error != "" {
require.NotNil(t, err)
} else {
require.NoError(t, err)
require.Equal(t, testCase.ExpectedResult(), result)
}
})
}
})
}
}

47
sql/v2/types.go Normal file
View File

@ -0,0 +1,47 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package v2
type Type uint8
const (
StringType Type = iota
IntegerType
BooleanType
AnyType
)
func TypePtr(t Type) *Type {
return &t
}
func (t Type) IsSameType(val interface{}) bool {
return TypeFromVal(val) == t
}
func (t Type) String() string {
switch t {
case IntegerType:
return "Integer"
case BooleanType:
return "Boolean"
case StringType:
return "String"
}
return "Any"
}
func TypeFromVal(val interface{}) Type {
switch val.(type) {
case string:
return StringType
case int32:
return IntegerType
case bool:
return BooleanType
}
return AnyType
}

65
sql/v2/utils/casting.go Normal file
View File

@ -0,0 +1,65 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package utils
import (
"fmt"
"strconv"
"strings"
cesql "github.com/cloudevents/sdk-go/sql/v2"
)
func Cast(val interface{}, target cesql.Type) (interface{}, error) {
if target.IsSameType(val) {
return val, nil
}
switch target {
case cesql.StringType:
switch val.(type) {
case int32:
return strconv.Itoa(int(val.(int32))), nil
case bool:
if val.(bool) {
return "true", nil
} else {
return "false", nil
}
}
// Casting to string is always defined
return fmt.Sprintf("%v", val), nil
case cesql.IntegerType:
switch val.(type) {
case string:
v, err := strconv.Atoi(val.(string))
if err != nil {
err = fmt.Errorf("cannot cast from String to Integer: %w", err)
}
return int32(v), err
}
return 0, fmt.Errorf("undefined cast from %v to %v", cesql.TypeFromVal(val), target)
case cesql.BooleanType:
switch val.(type) {
case string:
lowerCase := strings.ToLower(val.(string))
if lowerCase == "true" {
return true, nil
} else if lowerCase == "false" {
return false, nil
}
return false, fmt.Errorf("cannot cast String to Boolean, actual value: %v", val)
}
return false, fmt.Errorf("undefined cast from %v to %v", cesql.TypeFromVal(val), target)
}
// AnyType doesn't need casting
return val, nil
}
func CanCast(val interface{}, target cesql.Type) bool {
_, err := Cast(val, target)
return err == nil
}

67
sql/v2/utils/event.go Normal file
View File

@ -0,0 +1,67 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/
package utils
import (
"fmt"
"time"
cloudevents "github.com/cloudevents/sdk-go/v2"
"github.com/cloudevents/sdk-go/v2/binding/spec"
"github.com/cloudevents/sdk-go/v2/types"
)
func GetAttribute(event cloudevents.Event, attributeName string) interface{} {
var val interface{}
if a := spec.V1.Attribute(attributeName); a != nil { // Standard attribute
val = a.Get(event.Context)
} else {
val = event.Extensions()[attributeName]
}
if val == nil {
return nil
}
// Type cohercion
switch val.(type) {
case bool, int32, string:
return val
case int8:
return int32(val.(int8))
case uint8:
return int32(val.(uint8))
case int16:
return int32(val.(int16))
case uint16:
return int32(val.(uint16))
case uint32:
return int32(val.(uint32))
case int64:
return int32(val.(int64))
case uint64:
return int32(val.(uint64))
case time.Time:
return val.(time.Time).Format(time.RFC3339Nano)
case []byte:
return types.FormatBinary(val.([]byte))
}
return fmt.Sprintf("%v", val)
}
func ContainsAttribute(event cloudevents.Event, attributeName string) bool {
if attributeName == "specversion" || attributeName == "id" || attributeName == "source" || attributeName == "type" {
return true
}
if attr := spec.V1.Attribute(attributeName); attr != nil {
return attr.Get(event.Context) != nil
}
_, ok := event.Extensions()[attributeName]
return ok
}