diff --git a/.gitignore b/.gitignore index 1e974d4835..e2fb5af152 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ *.out coverage.txt +# Output of goyacc +embedded/sql/y.output + # Editor .vscode .idea @@ -50,4 +53,4 @@ token_admin swagger/dist swagger/swaggerembedded -webconsole/webconsoleembedded \ No newline at end of file +webconsole/webconsoleembedded diff --git a/embedded/sql/engine.go b/embedded/sql/engine.go index 0adad90fcd..99b4a8c932 100644 --- a/embedded/sql/engine.go +++ b/embedded/sql/engine.go @@ -118,6 +118,7 @@ type Engine struct { lazyIndexConstraintValidation bool parseTxMetadata func([]byte) (map[string]interface{}, error) multidbHandler MultiDBHandler + tableResolves map[string]TableResolver } type MultiDBHandler interface { @@ -134,6 +135,11 @@ type MultiDBHandler interface { ExecPreparedStmts(ctx context.Context, opts *TxOptions, stmts []SQLStmt, params map[string]interface{}) (ntx *SQLTx, committedTxs []*SQLTx, err error) } +type TableResolver interface { + Table() string + Resolve(ctx context.Context, tx *SQLTx, alias string) (RowReader, error) +} + type User interface { Username() string Permission() Permission @@ -176,6 +182,10 @@ func NewEngine(st *store.ImmuStore, opts *Options) (*Engine, error) { return nil, err } + for _, r := range opts.tableResolvers { + e.registerTableResolver(r.Table(), r) + } + // TODO: find a better way to handle parsing errors yyErrorVerbose = true @@ -728,3 +738,17 @@ func (e *Engine) GetStore() *store.ImmuStore { func (e *Engine) GetPrefix() []byte { return e.prefix } + +func (e *Engine) TableResolveFor(tableName string) TableResolver { + if e.tableResolves == nil { + return nil + } + return e.tableResolves[tableName] +} + +func (e *Engine) registerTableResolver(tableName string, r TableResolver) { + if e.tableResolves == nil { + e.tableResolves = make(map[string]TableResolver) + } + e.tableResolves[tableName] = r +} diff --git a/embedded/sql/engine_test.go b/embedded/sql/engine_test.go index 3c44841de3..315afe04d7 100644 --- a/embedded/sql/engine_test.go +++ b/embedded/sql/engine_test.go @@ -4616,6 +4616,10 @@ func TestOrderBy(t *testing.T) { directions: []int{1, -1}, positionalRefs: []int{4, 5}, }, + { + exps: []string{"weight/(height*height)"}, + directions: []int{1}, + }, } runTest := func(t *testing.T, test *test, expectedTempFiles int) []*Row { diff --git a/embedded/sql/functions.go b/embedded/sql/functions.go index d922c620a1..3a629a0169 100644 --- a/embedded/sql/functions.go +++ b/embedded/sql/functions.go @@ -25,39 +25,45 @@ import ( ) const ( - LengthFnCall string = "LENGTH" - SubstringFnCall string = "SUBSTRING" - ConcatFnCall string = "CONCAT" - LowerFnCall string = "LOWER" - UpperFnCall string = "UPPER" - TrimFnCall string = "TRIM" - NowFnCall string = "NOW" - UUIDFnCall string = "RANDOM_UUID" - DatabasesFnCall string = "DATABASES" - TablesFnCall string = "TABLES" - TableFnCall string = "TABLE" - UsersFnCall string = "USERS" - ColumnsFnCall string = "COLUMNS" - IndexesFnCall string = "INDEXES" - GrantsFnCall string = "GRANTS" - JSONTypeOfFnCall string = "JSON_TYPEOF" + LengthFnCall string = "LENGTH" + SubstringFnCall string = "SUBSTRING" + ConcatFnCall string = "CONCAT" + LowerFnCall string = "LOWER" + UpperFnCall string = "UPPER" + TrimFnCall string = "TRIM" + NowFnCall string = "NOW" + UUIDFnCall string = "RANDOM_UUID" + DatabasesFnCall string = "DATABASES" + TablesFnCall string = "TABLES" + TableFnCall string = "TABLE" + UsersFnCall string = "USERS" + ColumnsFnCall string = "COLUMNS" + IndexesFnCall string = "INDEXES" + GrantsFnCall string = "GRANTS" + JSONTypeOfFnCall string = "JSON_TYPEOF" + PGGetUserByIDFnCall string = "PG_GET_USERBYID" + PgTableIsVisible string = "PG_TABLE_IS_VISIBLE" + PgShobjDescription string = "SHOBJ_DESCRIPTION" ) var builtinFunctions = map[string]Function{ - LengthFnCall: &LengthFn{}, - SubstringFnCall: &SubstringFn{}, - ConcatFnCall: &ConcatFn{}, - LowerFnCall: &LowerUpperFnc{}, - UpperFnCall: &LowerUpperFnc{isUpper: true}, - TrimFnCall: &TrimFnc{}, - NowFnCall: &NowFn{}, - UUIDFnCall: &UUIDFn{}, - JSONTypeOfFnCall: &JsonTypeOfFn{}, + LengthFnCall: &LengthFn{}, + SubstringFnCall: &SubstringFn{}, + ConcatFnCall: &ConcatFn{}, + LowerFnCall: &LowerUpperFnc{}, + UpperFnCall: &LowerUpperFnc{isUpper: true}, + TrimFnCall: &TrimFnc{}, + NowFnCall: &NowFn{}, + UUIDFnCall: &UUIDFn{}, + JSONTypeOfFnCall: &JsonTypeOfFn{}, + PGGetUserByIDFnCall: &pgGetUserByIDFunc{}, + PgTableIsVisible: &pgTableIsVisible{}, + PgShobjDescription: &pgShobjDescription{}, } type Function interface { - requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error - inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) + RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error + InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) } @@ -67,11 +73,11 @@ type Function interface { type LengthFn struct{} -func (f *LengthFn) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { +func (f *LengthFn) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { return IntegerType, nil } -func (f *LengthFn) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { +func (f *LengthFn) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { if t != IntegerType { return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, IntegerType, t) } @@ -98,11 +104,11 @@ func (f *LengthFn) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) { type ConcatFn struct{} -func (f *ConcatFn) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { +func (f *ConcatFn) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { return VarcharType, nil } -func (f *ConcatFn) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { +func (f *ConcatFn) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { if t != VarcharType { return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, VarcharType, t) } @@ -131,11 +137,11 @@ func (f *ConcatFn) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) { type SubstringFn struct { } -func (f *SubstringFn) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { +func (f *SubstringFn) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { return VarcharType, nil } -func (f *SubstringFn) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { +func (f *SubstringFn) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { if t != VarcharType { return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, VarcharType, t) } @@ -180,11 +186,11 @@ type LowerUpperFnc struct { isUpper bool } -func (f *LowerUpperFnc) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { +func (f *LowerUpperFnc) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { return VarcharType, nil } -func (f *LowerUpperFnc) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { +func (f *LowerUpperFnc) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { if t != VarcharType { return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, VarcharType, t) } @@ -226,11 +232,11 @@ func (f *LowerUpperFnc) name() string { type TrimFnc struct { } -func (f *TrimFnc) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { +func (f *TrimFnc) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { return VarcharType, nil } -func (f *TrimFnc) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { +func (f *TrimFnc) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { if t != VarcharType { return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, VarcharType, t) } @@ -261,11 +267,11 @@ func (f *TrimFnc) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) { type NowFn struct{} -func (f *NowFn) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { +func (f *NowFn) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { return TimestampType, nil } -func (f *NowFn) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { +func (f *NowFn) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { if t != TimestampType { return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, TimestampType, t) } @@ -285,11 +291,11 @@ func (f *NowFn) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) { type JsonTypeOfFn struct{} -func (f *JsonTypeOfFn) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { +func (f *JsonTypeOfFn) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { return VarcharType, nil } -func (f *JsonTypeOfFn) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { +func (f *JsonTypeOfFn) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { if t != VarcharType { return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, VarcharType, t) } @@ -319,11 +325,11 @@ func (f *JsonTypeOfFn) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) type UUIDFn struct{} -func (f *UUIDFn) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { +func (f *UUIDFn) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { return UUIDType, nil } -func (f *UUIDFn) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { +func (f *UUIDFn) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { if t != UUIDType { return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, UUIDType, t) } @@ -336,3 +342,88 @@ func (f *UUIDFn) Apply(_ *SQLTx, params []TypedValue) (TypedValue, error) { } return &UUID{val: uuid.New()}, nil } + +// pg functions + +type pgGetUserByIDFunc struct{} + +func (f *pgGetUserByIDFunc) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { + if t != VarcharType { + return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, IntegerType, t) + } + return nil +} + +func (f *pgGetUserByIDFunc) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { + return VarcharType, nil +} + +func (f *pgGetUserByIDFunc) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) { + if len(params) != 1 { + return nil, fmt.Errorf("%w: '%s' function expects %d arguments but %d were provided", ErrIllegalArguments, JSONTypeOfFnCall, 1, len(params)) + } + + if params[0].RawValue() != int64(0) { + return nil, fmt.Errorf("user not found") + } + + users, err := tx.ListUsers(tx.tx.Context()) + if err != nil { + return nil, err + } + + idx := findSysAdmin(users) + if idx < 0 { + return nil, fmt.Errorf("admin not found") + } + return NewVarchar(users[idx].Username()), nil +} + +func findSysAdmin(users []User) int { + for i, u := range users { + if u.Permission() == PermissionSysAdmin { + return i + } + } + return -1 +} + +type pgTableIsVisible struct{} + +func (f *pgTableIsVisible) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { + if t != BooleanType { + return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, BooleanType, t) + } + return nil +} + +func (f *pgTableIsVisible) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { + return BooleanType, nil +} + +func (f *pgTableIsVisible) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) { + if len(params) != 1 { + return nil, fmt.Errorf("%w: '%s' function expects %d arguments but %d were provided", ErrIllegalArguments, JSONTypeOfFnCall, 1, len(params)) + } + return NewBool(true), nil +} + +type pgShobjDescription struct{} + +func (f *pgShobjDescription) RequiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { + if t != VarcharType { + return fmt.Errorf("%w: %v can not be interpreted as type %v", ErrInvalidTypes, VarcharType, t) + } + return nil +} + +func (f *pgShobjDescription) InferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { + return VarcharType, nil +} + +func (f *pgShobjDescription) Apply(tx *SQLTx, params []TypedValue) (TypedValue, error) { + if len(params) != 2 { + return nil, fmt.Errorf("%w: '%s' function expects %d arguments but %d were provided", ErrIllegalArguments, PgShobjDescription, 2, len(params)) + } + return NewVarchar(""), nil +} diff --git a/embedded/sql/functions_test.go b/embedded/sql/functions_test.go new file mode 100644 index 0000000000..96607409d3 --- /dev/null +++ b/embedded/sql/functions_test.go @@ -0,0 +1,62 @@ +package sql + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPGFunctions(t *testing.T) { + t.Run("pg_get_userbyid", func(t *testing.T) { + var f pgGetUserByIDFunc + + err := f.RequiresType(VarcharType, nil, nil, "") + require.NoError(t, err) + + funcType, err := f.InferType(nil, nil, "") + require.NoError(t, err) + require.Equal(t, VarcharType, funcType) + + _, err = f.Apply(nil, []TypedValue{NewInteger(0), NewInteger(0)}) + require.ErrorIs(t, err, ErrIllegalArguments) + + _, err = f.Apply(nil, []TypedValue{NewInteger(1)}) + require.ErrorContains(t, err, "user not found") + }) + + t.Run("pg_table_is_visible", func(t *testing.T) { + var f pgTableIsVisible + + err := f.RequiresType(BooleanType, nil, nil, "") + require.NoError(t, err) + + funcType, err := f.InferType(nil, nil, "") + require.NoError(t, err) + require.Equal(t, BooleanType, funcType) + + _, err = f.Apply(nil, []TypedValue{}) + require.ErrorIs(t, err, ErrIllegalArguments) + + v, err := f.Apply(nil, []TypedValue{NewVarchar("my_table")}) + require.NoError(t, err) + require.True(t, v.RawValue().(bool)) + }) + + t.Run("pg_shobj_description", func(t *testing.T) { + var f pgShobjDescription + + err := f.RequiresType(VarcharType, nil, nil, "") + require.NoError(t, err) + + funcType, err := f.InferType(nil, nil, "") + require.NoError(t, err) + require.Equal(t, VarcharType, funcType) + + _, err = f.Apply(nil, []TypedValue{NewVarchar("")}) + require.ErrorIs(t, err, ErrIllegalArguments) + + v, err := f.Apply(nil, []TypedValue{NewVarchar(""), NewVarchar("")}) + require.NoError(t, err) + require.Empty(t, v.RawValue()) + }) +} diff --git a/embedded/sql/joint_row_reader.go b/embedded/sql/joint_row_reader.go index e01ba14a02..028704e494 100644 --- a/embedded/sql/joint_row_reader.go +++ b/embedded/sql/joint_row_reader.go @@ -85,8 +85,12 @@ func (jointr *jointRowReader) colsBySelector(ctx context.Context) (map[string]Co return nil, err } - for _, jspec := range jointr.joins { + jointDescriptors := make(map[string]ColDescriptor, len(colDescriptors)) + for sel, desc := range colDescriptors { + jointDescriptors[sel] = desc + } + for _, jspec := range jointr.joins { // TODO (byo) optimize this by getting selector list only or opening all joint readers // on jointRowReader creation, // Note: We're using a dummy ScanSpec object that is only used during read, we're only interested @@ -103,7 +107,7 @@ func (jointr *jointRowReader) colsBySelector(ctx context.Context) (map[string]Co } for sel, des := range cd { - if _, exists := colDescriptors[sel]; exists { + if _, exists := jointDescriptors[sel]; exists { return nil, fmt.Errorf( "error resolving '%s' in a join: %w, "+ "use aliasing to assign unique names "+ @@ -112,10 +116,10 @@ func (jointr *jointRowReader) colsBySelector(ctx context.Context) (map[string]Co ErrAmbiguousSelector, ) } - colDescriptors[sel] = des + jointDescriptors[sel] = des } } - return colDescriptors, nil + return jointDescriptors, nil } func (jointr *jointRowReader) colsByPos(ctx context.Context) ([]ColDescriptor, error) { @@ -169,7 +173,6 @@ func (jointr *jointRowReader) InferParameters(ctx context.Context, params map[st return err } } - return err } diff --git a/embedded/sql/options.go b/embedded/sql/options.go index 3939f27655..c13b14bb81 100644 --- a/embedded/sql/options.go +++ b/embedded/sql/options.go @@ -36,6 +36,7 @@ type Options struct { parseTxMetadata func([]byte) (map[string]interface{}, error) multidbHandler MultiDBHandler + tableResolvers []TableResolver } func DefaultOptions() *Options { @@ -98,3 +99,8 @@ func (opts *Options) WithParseTxMetadataFunc(parseFunc func([]byte) (map[string] opts.parseTxMetadata = parseFunc return opts } + +func (opts *Options) WithTableResolvers(resolvers ...TableResolver) *Options { + opts.tableResolvers = append(opts.tableResolvers, resolvers...) + return opts +} diff --git a/embedded/sql/parser.go b/embedded/sql/parser.go index 6b0e0b9277..66917bd602 100644 --- a/embedded/sql/parser.go +++ b/embedded/sql/parser.go @@ -429,6 +429,9 @@ func (l *lexer) Lex(lval *yySymType) int { } op := fmt.Sprintf("%c%s", ch, tail) + if op == "!~" { + return NOT_MATCHES_OP + } cmpOp, ok := cmpOps[op] if !ok { @@ -663,7 +666,7 @@ func isLetter(ch byte) bool { } func isComparison(ch byte) bool { - return ch == '!' || ch == '<' || ch == '=' || ch == '>' + return ch == '!' || ch == '<' || ch == '=' || ch == '>' || ch == '~' } func isQuote(ch byte) bool { diff --git a/embedded/sql/parser_test.go b/embedded/sql/parser_test.go index 4c6efc679e..1970004c13 100644 --- a/embedded/sql/parser_test.go +++ b/embedded/sql/parser_test.go @@ -1817,6 +1817,19 @@ func TestParseExp(t *testing.T) { }, }, }, + { + input: "SELECT name !~ 'laptop.*' FROM products", + expectedOutput: []SQLStmt{ + &SelectStmt{ + ds: &tableRef{table: "products"}, + targets: []TargetEntry{ + { + Exp: NewLikeBoolExp(NewColSelector("", "name"), true, NewVarchar("laptop.*")), + }, + }, + }, + }, + }, } for i, tc := range testCases { diff --git a/embedded/sql/sql_grammar.y b/embedded/sql/sql_grammar.y index 2898e16412..dff45b6d3b 100644 --- a/embedded/sql/sql_grammar.y +++ b/embedded/sql/sql_grammar.y @@ -88,6 +88,7 @@ func setResult(l yyLexer, stmts []SQLStmt) { %token JOINTYPE %token AND OR %token CMPOP +%token NOT_MATCHES_OP %token IDENTIFIER %token TYPE %token INTEGER @@ -106,6 +107,7 @@ func setResult(l yyLexer, stmts []SQLStmt) { %left OR %left AND +%right NOT_MATCHES_OP %right LIKE %right NOT @@ -1108,6 +1110,11 @@ exp: { $$ = &LikeBoolExp{val: $1, notLike: $2, pattern: $4} } +| + boundexp NOT_MATCHES_OP exp + { + $$ = &LikeBoolExp{val: $1, notLike: true, pattern: $3} + } | EXISTS '(' dqlstmt ')' { diff --git a/embedded/sql/sql_parser.go b/embedded/sql/sql_parser.go index e19f1e3f05..157d0b33f8 100644 --- a/embedded/sql/sql_parser.go +++ b/embedded/sql/sql_parser.go @@ -154,18 +154,19 @@ const JOINTYPE = 57435 const AND = 57436 const OR = 57437 const CMPOP = 57438 -const IDENTIFIER = 57439 -const TYPE = 57440 -const INTEGER = 57441 -const FLOAT = 57442 -const VARCHAR = 57443 -const BOOLEAN = 57444 -const BLOB = 57445 -const AGGREGATE_FUNC = 57446 -const ERROR = 57447 -const DOT = 57448 -const ARROW = 57449 -const STMT_SEPARATOR = 57450 +const NOT_MATCHES_OP = 57439 +const IDENTIFIER = 57440 +const TYPE = 57441 +const INTEGER = 57442 +const FLOAT = 57443 +const VARCHAR = 57444 +const BOOLEAN = 57445 +const BLOB = 57446 +const AGGREGATE_FUNC = 57447 +const ERROR = 57448 +const DOT = 57449 +const ARROW = 57450 +const STMT_SEPARATOR = 57451 var yyToknames = [...]string{ "$end", @@ -264,6 +265,7 @@ var yyToknames = [...]string{ "AND", "OR", "CMPOP", + "NOT_MATCHES_OP", "IDENTIFIER", "TYPE", "INTEGER", @@ -300,141 +302,141 @@ var yyExca = [...]int16{ 1, -1, -2, 0, -1, 97, - 78, 193, - 81, 193, + 78, 194, + 81, 194, -2, 175, - -1, 270, + -1, 272, 59, 146, -2, 141, - -1, 320, + -1, 322, 59, 146, -2, 143, } const yyPrivate = 57344 -const yyLast = 611 +const yyLast = 613 var yyAct = [...]int16{ - 131, 433, 313, 107, 341, 153, 264, 357, 325, 202, - 208, 115, 324, 242, 247, 243, 319, 308, 6, 299, - 199, 71, 144, 22, 402, 147, 347, 262, 346, 262, - 421, 294, 130, 403, 364, 394, 405, 262, 386, 106, - 374, 262, 129, 365, 99, 162, 348, 101, 262, 393, - 303, 118, 114, 377, 21, 373, 371, 263, 116, 117, - 332, 358, 96, 330, 119, 329, 109, 110, 111, 112, - 113, 108, 154, 155, 157, 156, 158, 100, 327, 293, - 359, 179, 291, 105, 106, 290, 284, 261, 326, 99, - 298, 178, 101, 283, 178, 278, 118, 114, 277, 276, - 168, 169, 275, 116, 117, 249, 171, 173, 149, 119, - 162, 109, 110, 111, 112, 113, 108, 132, 188, 181, - 177, 162, 100, 176, 161, 170, 143, 142, 105, 24, - 240, 187, 432, 159, 160, 161, 162, 154, 155, 157, - 156, 158, 425, 145, 238, 364, 204, 422, 154, 155, - 157, 156, 158, 217, 295, 218, 219, 220, 221, 222, - 223, 224, 225, 215, 201, 157, 156, 158, 294, 205, - 185, 186, 162, 262, 152, 83, 236, 175, 241, 244, - 239, 179, 213, 134, 159, 160, 161, 289, 258, 232, - 206, 339, 251, 237, 385, 384, 164, 296, 231, 154, - 155, 157, 156, 158, 32, 253, 240, 233, 269, 252, - 200, 33, 76, 380, 106, 267, 368, 352, 270, 99, - 351, 279, 101, 280, 163, 344, 118, 114, 343, 273, - 282, 271, 268, 116, 117, 350, 288, 331, 311, 119, - 148, 109, 110, 111, 112, 113, 108, 211, 212, 214, - 257, 256, 100, 94, 255, 254, 216, 248, 105, 250, - 245, 228, 197, 196, 189, 182, 315, 150, 133, 122, - 297, 120, 91, 54, 317, 210, 80, 323, 305, 79, - 310, 312, 310, 77, 244, 78, 75, 336, 337, 70, - 248, 69, 207, 322, 31, 340, 58, 22, 167, 334, - 401, 383, 333, 434, 435, 227, 281, 166, 382, 274, - 342, 60, 226, 309, 400, 356, 162, 180, 162, 349, - 360, 22, 355, 229, 121, 65, 230, 244, 21, 335, - 159, 160, 161, 367, 235, 369, 370, 362, 372, 376, - 366, 361, 272, 90, 379, 154, 155, 157, 156, 158, - 22, 314, 21, 39, 286, 55, 287, 416, 424, 106, - 265, 56, 57, 59, 99, 64, 408, 101, 390, 49, - 145, 118, 114, 392, 391, 215, 395, 407, 116, 117, - 388, 21, 363, 151, 119, 52, 109, 110, 111, 112, - 113, 108, 62, 66, 67, 414, 404, 100, 88, 413, - 410, 409, 412, 105, 411, 387, 51, 50, 417, 25, - 82, 92, 419, 344, 398, 164, 343, 396, 162, 378, - 190, 426, 423, 193, 194, 430, 428, 427, 162, 431, - 159, 160, 161, 436, 124, 191, 192, 140, 437, 137, - 159, 160, 161, 163, 397, 154, 155, 157, 156, 158, - 304, 260, 375, 259, 420, 154, 155, 157, 156, 158, - 162, 338, 135, 136, 354, 316, 183, 292, 123, 162, - 84, 81, 159, 160, 161, 266, 68, 328, 2, 195, - 162, 159, 160, 161, 128, 127, 184, 154, 155, 157, - 156, 158, 159, 160, 161, 138, 154, 155, 157, 156, - 158, 162, 36, 63, 43, 47, 162, 154, 155, 157, - 156, 158, 125, 159, 160, 161, 307, 34, 159, 35, - 161, 10, 12, 11, 38, 141, 209, 48, 154, 155, - 157, 156, 158, 154, 155, 157, 156, 158, 306, 37, - 73, 74, 139, 203, 13, 44, 53, 23, 234, 46, - 45, 41, 353, 14, 15, 146, 42, 165, 7, 381, - 8, 9, 16, 17, 26, 30, 18, 19, 399, 415, - 429, 40, 345, 22, 300, 301, 302, 85, 86, 87, - 27, 29, 28, 95, 93, 102, 389, 98, 285, 97, - 406, 172, 321, 320, 318, 126, 72, 89, 61, 174, - 103, 104, 418, 198, 21, 246, 20, 5, 4, 3, - 1, + 131, 435, 315, 107, 343, 153, 266, 359, 327, 203, + 209, 115, 326, 244, 249, 245, 321, 310, 6, 301, + 200, 71, 144, 22, 404, 147, 349, 264, 348, 264, + 423, 130, 296, 405, 366, 396, 407, 264, 388, 106, + 264, 376, 129, 367, 99, 162, 350, 101, 264, 305, + 395, 118, 114, 379, 21, 375, 373, 265, 116, 117, + 214, 334, 96, 360, 332, 119, 331, 109, 110, 111, + 112, 113, 108, 154, 155, 157, 156, 158, 100, 329, + 295, 328, 361, 106, 105, 238, 293, 292, 99, 286, + 263, 101, 300, 285, 180, 118, 114, 179, 280, 279, + 169, 170, 116, 117, 179, 278, 172, 174, 149, 119, + 162, 109, 110, 111, 112, 113, 108, 132, 277, 251, + 162, 189, 100, 182, 161, 212, 213, 215, 105, 178, + 177, 188, 159, 160, 161, 217, 171, 143, 154, 155, + 157, 156, 158, 162, 142, 24, 205, 424, 154, 155, + 157, 156, 158, 218, 211, 219, 220, 221, 222, 223, + 224, 225, 226, 216, 202, 145, 434, 232, 242, 206, + 186, 187, 162, 157, 156, 158, 427, 366, 297, 243, + 246, 241, 240, 296, 159, 160, 161, 264, 152, 83, + 234, 176, 180, 134, 291, 260, 253, 239, 387, 386, + 154, 155, 157, 156, 158, 32, 255, 164, 235, 271, + 254, 341, 33, 207, 298, 106, 269, 233, 242, 272, + 99, 201, 281, 101, 282, 382, 76, 118, 114, 370, + 275, 284, 273, 270, 116, 117, 163, 346, 290, 354, + 345, 119, 168, 109, 110, 111, 112, 113, 108, 353, + 352, 167, 333, 313, 100, 94, 148, 259, 258, 257, + 105, 256, 166, 162, 250, 252, 247, 229, 317, 198, + 197, 190, 299, 183, 150, 159, 319, 161, 133, 325, + 307, 122, 312, 314, 312, 120, 246, 436, 437, 338, + 339, 154, 155, 157, 156, 158, 31, 342, 77, 91, + 54, 336, 162, 250, 335, 80, 79, 78, 75, 70, + 69, 208, 344, 22, 159, 160, 161, 358, 324, 311, + 403, 351, 362, 276, 357, 283, 402, 22, 162, 246, + 154, 155, 157, 156, 158, 369, 181, 371, 372, 364, + 374, 378, 368, 363, 21, 385, 381, 228, 65, 121, + 337, 58, 384, 39, 227, 288, 274, 289, 21, 230, + 106, 55, 231, 237, 22, 99, 60, 90, 101, 49, + 418, 316, 118, 114, 267, 394, 393, 216, 397, 116, + 117, 426, 390, 410, 392, 145, 119, 409, 109, 110, + 111, 112, 113, 108, 365, 21, 416, 151, 52, 100, + 62, 415, 412, 411, 414, 105, 413, 164, 406, 389, + 419, 88, 51, 50, 421, 25, 56, 57, 59, 210, + 162, 398, 82, 428, 425, 92, 400, 432, 430, 429, + 162, 433, 159, 160, 161, 438, 163, 140, 346, 53, + 439, 345, 159, 160, 161, 377, 380, 191, 154, 155, + 157, 156, 158, 162, 306, 340, 262, 399, 154, 155, + 157, 156, 158, 162, 137, 159, 160, 161, 194, 195, + 85, 86, 87, 294, 261, 159, 160, 161, 64, 192, + 193, 154, 155, 157, 156, 158, 162, 135, 136, 422, + 356, 154, 155, 157, 156, 158, 162, 318, 159, 160, + 161, 184, 123, 84, 81, 268, 66, 67, 159, 160, + 161, 10, 12, 11, 154, 155, 157, 156, 158, 43, + 47, 26, 30, 68, 154, 155, 157, 156, 158, 2, + 38, 330, 73, 74, 13, 128, 127, 27, 29, 28, + 196, 185, 48, 14, 15, 37, 138, 124, 7, 125, + 8, 9, 16, 17, 63, 36, 18, 19, 309, 308, + 44, 141, 204, 22, 46, 45, 302, 303, 304, 139, + 34, 42, 35, 23, 236, 41, 355, 146, 165, 383, + 401, 417, 431, 347, 95, 93, 40, 102, 391, 98, + 287, 97, 408, 173, 21, 323, 322, 320, 126, 72, + 89, 61, 175, 103, 104, 420, 199, 248, 20, 5, + 4, 3, 1, } var yyPact = [...]int16{ - 517, -1000, -1000, 14, -1000, -1000, -1000, 367, -1000, -1000, - 557, 197, 494, 516, 500, 500, 360, 359, 327, 176, - 285, 273, 335, -1000, 517, -1000, 246, 246, 246, 451, - 194, -1000, 192, 524, 189, 186, 188, 182, 179, 445, - 370, 67, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 444, - 176, 176, 176, 347, -1000, 272, -1000, -1000, 175, -1000, - 372, 142, -1000, -1000, 174, 247, 172, 442, 246, 503, - -1000, -1000, 466, 12, 12, -1000, 171, 77, -1000, 434, - 486, 535, -1000, 500, 518, 11, 10, 309, 143, 241, - -1000, -1000, 170, 325, -1000, 66, 346, 221, -1000, 287, - 287, 9, -1000, -1000, -1000, 287, 287, 70, 7, -1000, - -1000, -1000, -1000, -1000, 4, -1000, -1000, -1000, -1000, -25, - -1000, 237, 3, 168, 440, 476, -1000, 12, 12, -1000, - 287, 419, -1000, 2, 167, 389, 405, 392, 469, 166, - -1000, 165, 113, 113, 537, 287, 82, -1000, 196, -1000, - -1000, 159, 287, -1000, 287, 287, 287, 287, 287, 287, - 287, 287, 228, -1000, 164, 245, 100, -1000, 28, 54, - 241, 90, 261, 419, 69, 92, 33, 287, 287, 163, - -1000, 160, -11, 162, 91, -1000, -1000, 419, 113, -1000, - 160, 158, 157, 154, 153, 87, 423, 421, -30, 65, - -1000, -60, 296, 450, 419, 537, 143, 287, 537, 524, - 294, -14, -17, -18, -21, 127, -22, 346, 54, 54, - 234, 234, 234, 28, 424, -37, -1000, 222, -1000, 287, - -23, -1000, -31, -1000, 281, 287, 86, -1000, -32, -35, - 75, 398, -38, 60, 419, -1000, 46, -1000, 99, 113, - -26, 563, -67, -1000, -1000, 420, -1000, -1000, 563, 530, - 508, 265, 141, 265, 286, 287, 439, 296, -1000, 419, - 200, 127, -28, -39, 456, -52, -54, 140, -57, -1000, - -1000, -1000, 28, -33, -1000, 253, 287, 287, 387, -1000, - -1000, -1000, 93, -1000, 287, 193, -90, -71, 113, -1000, - -1000, -1000, -1000, -1000, 138, -1000, 123, 120, 438, -28, - -1000, -1000, -1000, -1000, 287, 419, -36, 286, 309, -1000, - 200, 323, -1000, -1000, -74, -1000, 287, 127, 119, 127, - 127, -61, 127, -62, -77, -1000, 378, 419, 287, -64, - 419, 386, -1000, 287, 116, 224, 96, 95, -1000, -79, - -1000, -1000, -1000, -1000, 353, 37, 419, -1000, -1000, 113, - -1000, 306, -1000, 159, -28, -1000, -68, -1000, -82, -1000, - -1000, -1000, -1000, -1000, -1000, 287, 419, -1000, 383, 336, - 379, 231, -1000, 216, -95, -84, -1000, 343, -81, 317, - 303, 537, -1000, -1000, 127, 419, -36, 381, 287, -1000, - -1000, -1000, -1000, -1000, 341, -1000, 291, 287, 109, 428, - -1000, -87, -1000, 39, -1000, 296, 295, 419, 34, -1000, - 287, -1000, 381, 286, 287, 109, 419, -1000, -1000, 24, - 236, -1000, 287, -1000, -1000, -1000, 236, -1000, + 507, -1000, -1000, 29, -1000, -1000, -1000, 373, -1000, -1000, + 514, 198, 547, 522, 515, 515, 366, 365, 340, 202, + 291, 328, 343, -1000, 507, -1000, 269, 269, 269, 498, + 212, -1000, 211, 516, 210, 200, 209, 208, 207, 478, + 382, 80, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 477, + 202, 202, 202, 360, -1000, 296, -1000, -1000, 201, -1000, + 386, 143, -1000, -1000, 187, 272, 183, 476, 269, 540, + -1000, -1000, 517, 11, 11, -1000, 180, 86, -1000, 459, + 537, 562, -1000, 515, 554, 27, 20, 324, 158, 257, + -1000, -1000, 176, 339, -1000, 79, 338, 165, -1000, 288, + 288, 19, -1000, -1000, -1000, 288, 288, 83, 13, -1000, + -1000, -1000, -1000, -1000, 12, -1000, -1000, -1000, -1000, -13, + -1000, 256, 6, 175, 475, 531, -1000, 11, 11, -1000, + 288, 414, -1000, 4, 173, 416, 449, 437, 530, 172, + -1000, 171, 123, 123, 556, 288, 104, -1000, 215, -1000, + -1000, 37, 288, -1000, 288, 288, 288, 288, 288, 288, + 288, 288, 270, -1000, 169, 281, 288, 118, -1000, 28, + 61, 257, 90, 290, 414, -23, 95, 70, 288, 288, + 168, -1000, 166, 2, 167, 94, -1000, -1000, 414, 123, + -1000, 166, 163, 161, 160, 159, 93, 444, 426, -28, + 78, -1000, -61, 310, 480, 414, 556, 158, 288, 556, + 516, 308, 1, -12, -18, -19, 138, -20, 338, 61, + 61, 246, 246, 246, 28, 181, -37, -1000, 241, -1000, + 288, -24, 28, -1000, -29, -1000, 282, 288, 92, -1000, + -31, -32, 85, 404, -38, 74, 414, -1000, 69, -1000, + 115, 123, -25, 555, -69, -1000, -1000, 424, -1000, -1000, + 555, 551, 550, 271, 155, 271, 306, 288, 471, 310, + -1000, 414, 225, 138, -36, -39, 510, -52, -54, 154, + -57, -1000, -1000, -1000, 28, -33, -1000, 274, 288, 288, + 381, -1000, -1000, -1000, 112, -1000, 288, 205, -91, -72, + 123, -1000, -1000, -1000, -1000, -1000, 152, -1000, 151, 141, + 464, -36, -1000, -1000, -1000, -1000, 288, 414, -35, 306, + 324, -1000, 225, 335, -1000, -1000, -75, -1000, 288, 138, + 131, 138, 138, -62, 138, -63, -77, -1000, 371, 414, + 288, -65, 414, 413, -1000, 288, 127, 268, 99, 98, + -1000, -80, -1000, -1000, -1000, -1000, 357, 68, 414, -1000, + -1000, 123, -1000, 322, -1000, 37, -36, -1000, -68, -1000, + -83, -1000, -1000, -1000, -1000, -1000, -1000, 288, 414, -1000, + 387, 348, 391, 243, -1000, 236, -96, -85, -1000, 355, + -82, 327, 320, 556, -1000, -1000, 138, 414, -35, 406, + 288, -1000, -1000, -1000, -1000, -1000, 342, -1000, 304, 288, + 120, 463, -1000, -88, -1000, 38, -1000, 310, 318, 414, + 67, -1000, 288, -1000, 406, 306, 288, 120, 414, -1000, + -1000, 57, 220, -1000, 288, -1000, -1000, -1000, 220, -1000, } var yyPgo = [...]int16{ - 0, 610, 478, 609, 608, 607, 18, 606, 605, 14, - 20, 7, 603, 602, 12, 8, 15, 13, 601, 11, - 600, 599, 3, 598, 597, 10, 17, 526, 21, 596, - 595, 42, 594, 16, 593, 592, 4, 0, 591, 22, - 590, 589, 588, 587, 586, 6, 2, 585, 584, 583, - 572, 5, 570, 569, 1, 9, 365, 568, 559, 557, - 25, 555, 552, 19, 551, 353, 548, 547, + 0, 612, 529, 611, 610, 609, 18, 608, 607, 14, + 20, 7, 606, 605, 12, 8, 15, 13, 604, 11, + 603, 602, 3, 601, 600, 10, 17, 419, 21, 599, + 598, 42, 597, 16, 596, 595, 4, 0, 593, 22, + 592, 591, 590, 589, 588, 6, 2, 587, 585, 584, + 583, 5, 582, 581, 1, 9, 478, 580, 579, 578, + 25, 577, 576, 19, 575, 353, 574, 573, } var yyR1 = [...]int8{ @@ -456,9 +458,9 @@ var yyR1 = [...]int8{ 44, 44, 40, 40, 45, 45, 46, 46, 53, 53, 55, 55, 52, 52, 54, 54, 54, 51, 51, 51, 36, 36, 36, 38, 38, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 47, 66, 66, 42, 42, 41, - 41, 41, 41, 59, 59, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, + 37, 37, 37, 37, 37, 47, 66, 66, 42, 42, + 41, 41, 41, 41, 59, 59, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, } var yyR2 = [...]int8{ @@ -480,56 +482,56 @@ var yyR2 = [...]int8{ 0, 3, 0, 2, 0, 2, 0, 2, 0, 3, 0, 4, 2, 4, 0, 1, 1, 0, 1, 2, 0, 4, 6, 0, 1, 1, 1, 2, 2, 4, - 4, 6, 6, 1, 5, 4, 5, 0, 2, 1, - 1, 3, 3, 0, 1, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 4, + 3, 4, 6, 6, 1, 5, 4, 5, 0, 2, + 1, 1, 3, 3, 0, 1, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, } var yyChk = [...]int16{ -1000, -1, -2, -3, -4, -5, -6, 41, 43, 44, 4, 6, 5, 27, 36, 37, 45, 46, 49, 50, - -7, 87, 56, -67, 115, 42, 7, 23, 25, 24, - 8, 97, 7, 14, 23, 25, 8, 23, 8, -65, + -7, 87, 56, -67, 116, 42, 7, 23, 25, 24, + 8, 98, 7, 14, 23, 25, 8, 23, 8, -65, 71, -64, 56, 4, 45, 50, 49, 5, 27, -65, - 47, 47, 58, -27, 97, 70, 88, 89, 23, 90, - 38, -23, 57, -2, -56, 79, -56, -56, 25, 97, - 97, -28, -29, 16, 17, 97, 26, 97, 97, 97, - 97, 26, 40, 108, 26, -27, -27, -27, 51, -24, - 71, 97, 39, -48, 111, -49, -37, -41, -43, 77, - 110, 80, -47, -20, -18, 116, 72, -22, 104, 99, - 100, 101, 102, 103, 85, -19, 91, 92, 84, 97, - 97, 77, 97, 26, -56, 9, -30, 19, 18, -31, - 20, -37, -31, 97, 106, 28, 29, 5, 9, 7, - -65, 7, 116, 116, -39, 61, -61, -60, 97, -6, - 97, 58, 108, -51, 109, 110, 112, 111, 113, 94, - 95, 96, 82, 97, 69, -59, 86, 77, -37, -37, - 116, -37, -38, -37, -21, 107, 116, 116, 116, 106, - 80, 116, 97, 26, 10, -31, -31, -37, 116, 97, - 31, 30, 31, 31, 32, 10, 97, 97, -12, -10, - 97, -10, -55, 6, -37, -39, 108, 96, -25, -27, - 116, 88, 89, 23, 90, -19, 97, -37, -37, -37, - -37, -37, -37, -37, -37, -37, 84, 77, 97, 78, - 81, 98, -6, 117, -66, 73, 107, 101, 111, -22, - 97, -37, -17, -16, -37, 97, -8, -9, 97, 116, - 97, 101, -10, -9, 97, 97, 97, 97, 101, 30, - 30, 117, 108, 117, -45, 64, 25, -55, -60, -37, - -55, -28, 48, -6, 15, 116, 116, 116, 116, -51, - -51, 84, -37, 116, 117, -42, 73, 75, -37, 101, - 117, 117, 69, 117, 108, 108, 98, -10, 116, -63, - 11, 12, 13, 117, 30, -63, 8, 8, -26, 48, - -6, 97, -26, -46, 65, -37, 26, -45, -32, -33, - -34, -35, 93, -51, -14, -15, 116, 117, 21, 117, - 117, 97, 117, -6, -16, 76, -37, -37, 74, 98, - -37, -36, -9, 35, 32, -50, 118, 116, 117, -10, - 97, 97, 97, -62, 26, -14, -37, -11, 97, 116, - -46, -39, -33, 59, 108, 117, -17, -51, 97, -51, - -51, 117, -51, 117, 117, 74, -37, 117, 33, -37, - 97, -58, 84, 77, 99, 99, 117, 52, -10, -44, - 62, -25, -15, 117, 117, -37, 34, 108, 35, -57, - 83, 84, 119, 117, 53, 117, -40, 60, 63, -55, - -51, -11, -36, -37, 54, -53, 66, -37, -13, -22, - 26, 117, 108, -45, 63, 108, -37, -36, -46, -52, - -37, -22, 108, -54, 67, 68, -37, -54, + 47, 47, 58, -27, 98, 70, 88, 89, 23, 90, + 38, -23, 57, -2, -56, 79, -56, -56, 25, 98, + 98, -28, -29, 16, 17, 98, 26, 98, 98, 98, + 98, 26, 40, 109, 26, -27, -27, -27, 51, -24, + 71, 98, 39, -48, 112, -49, -37, -41, -43, 77, + 111, 80, -47, -20, -18, 117, 72, -22, 105, 100, + 101, 102, 103, 104, 85, -19, 91, 92, 84, 98, + 98, 77, 98, 26, -56, 9, -30, 19, 18, -31, + 20, -37, -31, 98, 107, 28, 29, 5, 9, 7, + -65, 7, 117, 117, -39, 61, -61, -60, 98, -6, + 98, 58, 109, -51, 110, 111, 113, 112, 114, 94, + 95, 96, 82, 98, 69, -59, 97, 86, 77, -37, + -37, 117, -37, -38, -37, -21, 108, 117, 117, 117, + 107, 80, 117, 98, 26, 10, -31, -31, -37, 117, + 98, 31, 30, 31, 31, 32, 10, 98, 98, -12, + -10, 98, -10, -55, 6, -37, -39, 109, 96, -25, + -27, 117, 88, 89, 23, 90, -19, 98, -37, -37, + -37, -37, -37, -37, -37, -37, -37, 84, 77, 98, + 78, 81, -37, 99, -6, 118, -66, 73, 108, 102, + 112, -22, 98, -37, -17, -16, -37, 98, -8, -9, + 98, 117, 98, 102, -10, -9, 98, 98, 98, 98, + 102, 30, 30, 118, 109, 118, -45, 64, 25, -55, + -60, -37, -55, -28, 48, -6, 15, 117, 117, 117, + 117, -51, -51, 84, -37, 117, 118, -42, 73, 75, + -37, 102, 118, 118, 69, 118, 109, 109, 99, -10, + 117, -63, 11, 12, 13, 118, 30, -63, 8, 8, + -26, 48, -6, 98, -26, -46, 65, -37, 26, -45, + -32, -33, -34, -35, 93, -51, -14, -15, 117, 118, + 21, 118, 118, 98, 118, -6, -16, 76, -37, -37, + 74, 99, -37, -36, -9, 35, 32, -50, 119, 117, + 118, -10, 98, 98, 98, -62, 26, -14, -37, -11, + 98, 117, -46, -39, -33, 59, 109, 118, -17, -51, + 98, -51, -51, 118, -51, 118, 118, 74, -37, 118, + 33, -37, 98, -58, 84, 77, 100, 100, 118, 52, + -10, -44, 62, -25, -15, 118, 118, -37, 34, 109, + 35, -57, 83, 84, 120, 118, 53, 118, -40, 60, + 63, -55, -51, -11, -36, -37, 54, -53, 66, -37, + -13, -22, 26, 118, 109, -45, 63, 109, -37, -36, + -46, -52, -37, -22, 109, -54, 67, 68, -37, -54, } var yyDef = [...]int16{ @@ -543,53 +545,53 @@ var yyDef = [...]int16{ 15, 16, 136, 0, 0, 18, 0, 0, 30, 0, 0, 0, 33, 0, 0, 0, 0, 148, 0, 0, 107, 101, 0, 0, 110, 111, 167, -2, 176, 0, - 0, 0, 183, 189, 190, 0, 173, 114, 0, 75, + 0, 0, 184, 190, 191, 0, 173, 114, 0, 75, 76, 77, 78, 79, 0, 81, 82, 83, 84, 120, 13, 0, 0, 0, 0, 0, 132, 0, 0, 134, 0, 140, 135, 0, 0, 0, 0, 0, 0, 0, 35, 0, 62, 0, 160, 0, 148, 59, 0, 98, 104, 0, 0, 112, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 168, 0, 0, 0, 194, 177, 178, - 0, 0, 0, 174, 115, 0, 0, 0, 71, 0, - 48, 0, 0, 0, 0, 137, 138, 139, 0, 22, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, - 67, 0, 154, 0, 149, 160, 0, 0, 160, 133, - 0, 0, 0, 0, 0, 167, 131, 167, 195, 196, - 197, 198, 199, 200, 201, 202, 203, 0, 169, 0, - 0, 192, 0, 191, 187, 0, 0, 118, 0, 0, - 120, 0, 0, 72, 73, 121, 0, 86, 0, 0, - 0, 43, 0, 23, 24, 0, 26, 27, 43, 0, - 0, 0, 0, 0, 156, 0, 0, 154, 60, 61, - -2, 167, 0, 0, 0, 0, 0, 0, 0, 129, - 113, 204, 179, 0, 180, 0, 0, 0, 0, 119, - 116, 117, 0, 85, 0, 170, 89, 0, 0, 28, - 44, 45, 46, 21, 0, 29, 0, 0, 57, 0, - 56, 68, 52, 53, 0, 155, 0, 156, 148, 142, - -2, 0, 147, 122, 0, 64, 71, 167, 0, 167, - 167, 0, 167, 0, 0, 184, 0, 188, 0, 0, - 74, 0, 87, 0, 0, 94, 0, 0, 19, 0, - 25, 31, 32, 51, 0, 55, 157, 161, 49, 0, - 54, 150, 144, 0, 0, 123, 0, 124, 0, 125, - 126, 127, 128, 181, 182, 0, 185, 80, 0, 0, - 0, 92, 95, 0, 0, 0, 20, 0, 0, 152, - 0, 160, 65, 66, 167, 186, 0, 170, 0, 88, - 93, 96, 90, 91, 0, 50, 158, 0, 0, 0, - 130, 0, 171, 0, 58, 154, 0, 153, 151, 69, - 0, 17, 170, 156, 0, 0, 145, 172, 105, 159, - 164, 70, 0, 162, 165, 166, 164, 163, + 0, 0, 0, 168, 0, 0, 0, 0, 195, 177, + 178, 0, 0, 0, 174, 115, 0, 0, 0, 71, + 0, 48, 0, 0, 0, 0, 137, 138, 139, 0, + 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 63, 67, 0, 154, 0, 149, 160, 0, 0, 160, + 133, 0, 0, 0, 0, 0, 167, 131, 167, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 0, 169, + 0, 0, 180, 193, 0, 192, 188, 0, 0, 118, + 0, 0, 120, 0, 0, 72, 73, 121, 0, 86, + 0, 0, 0, 43, 0, 23, 24, 0, 26, 27, + 43, 0, 0, 0, 0, 0, 156, 0, 0, 154, + 60, 61, -2, 167, 0, 0, 0, 0, 0, 0, + 0, 129, 113, 205, 179, 0, 181, 0, 0, 0, + 0, 119, 116, 117, 0, 85, 0, 170, 89, 0, + 0, 28, 44, 45, 46, 21, 0, 29, 0, 0, + 57, 0, 56, 68, 52, 53, 0, 155, 0, 156, + 148, 142, -2, 0, 147, 122, 0, 64, 71, 167, + 0, 167, 167, 0, 167, 0, 0, 185, 0, 189, + 0, 0, 74, 0, 87, 0, 0, 94, 0, 0, + 19, 0, 25, 31, 32, 51, 0, 55, 157, 161, + 49, 0, 54, 150, 144, 0, 0, 123, 0, 124, + 0, 125, 126, 127, 128, 182, 183, 0, 186, 80, + 0, 0, 0, 92, 95, 0, 0, 0, 20, 0, + 0, 152, 0, 160, 65, 66, 167, 187, 0, 170, + 0, 88, 93, 96, 90, 91, 0, 50, 158, 0, + 0, 0, 130, 0, 171, 0, 58, 154, 0, 153, + 151, 69, 0, 17, 170, 156, 0, 0, 145, 172, + 105, 159, 164, 70, 0, 162, 165, 166, 164, 163, } var yyTok1 = [...]int8{ 1, 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, 3, 3, 3, 3, 3, 3, 113, 3, 3, - 116, 117, 111, 109, 108, 110, 114, 112, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 114, 3, 3, + 117, 118, 112, 110, 109, 111, 115, 113, 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, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 118, 3, 119, + 3, 119, 3, 120, } var yyTok2 = [...]int8{ @@ -603,7 +605,7 @@ var yyTok2 = [...]int8{ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - 102, 103, 104, 105, 106, 107, 115, + 102, 103, 104, 105, 106, 107, 108, 116, } var yyTok3 = [...]int8{ @@ -1858,26 +1860,31 @@ yydefault: yyVAL.exp = &LikeBoolExp{val: yyDollar[1].exp, notLike: yyDollar[2].boolean, pattern: yyDollar[4].exp} } case 180: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.exp = &LikeBoolExp{val: yyDollar[1].exp, notLike: true, pattern: yyDollar[3].exp} + } + case 181: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.exp = &ExistsBoolExp{q: (yyDollar[3].stmt).(DataSource)} } - case 181: + case 182: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.exp = &InSubQueryExp{val: yyDollar[1].exp, notIn: yyDollar[2].boolean, q: yyDollar[5].stmt.(*SelectStmt)} } - case 182: + case 183: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.exp = &InListExp{val: yyDollar[1].exp, notIn: yyDollar[2].boolean, values: yyDollar[5].values} } - case 183: + case 184: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.exp = yyDollar[1].exp } - case 184: + case 185: yyDollar = yyS[yypt-5 : yypt+1] { yyVAL.exp = &CaseWhenExp{ @@ -1886,102 +1893,102 @@ yydefault: elseExp: yyDollar[4].exp, } } - case 185: + case 186: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.whenThenClauses = []whenThenClause{{when: yyDollar[2].exp, then: yyDollar[4].exp}} } - case 186: + case 187: yyDollar = yyS[yypt-5 : yypt+1] { yyVAL.whenThenClauses = append(yyDollar[1].whenThenClauses, whenThenClause{when: yyDollar[3].exp, then: yyDollar[5].exp}) } - case 187: + case 188: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.exp = nil } - case 188: + case 189: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.exp = yyDollar[2].exp } - case 189: + case 190: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.exp = yyDollar[1].sel } - case 190: + case 191: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.exp = yyDollar[1].value } - case 191: + case 192: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.exp = yyDollar[2].exp } - case 192: + case 193: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.exp = &Cast{val: yyDollar[1].exp, t: yyDollar[3].sqlType} } - case 193: + case 194: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.boolean = false } - case 194: + case 195: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.boolean = true } - case 195: + case 196: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: ADDOP, right: yyDollar[3].exp} } - case 196: + case 197: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: SUBSOP, right: yyDollar[3].exp} } - case 197: + case 198: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: DIVOP, right: yyDollar[3].exp} } - case 198: + case 199: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: MULTOP, right: yyDollar[3].exp} } - case 199: + case 200: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &NumExp{left: yyDollar[1].exp, op: MODOP, right: yyDollar[3].exp} } - case 200: + case 201: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &BinBoolExp{left: yyDollar[1].exp, op: And, right: yyDollar[3].exp} } - case 201: + case 202: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &BinBoolExp{left: yyDollar[1].exp, op: Or, right: yyDollar[3].exp} } - case 202: + case 203: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &CmpBoolExp{left: yyDollar[1].exp, op: yyDollar[2].cmpOp, right: yyDollar[3].exp} } - case 203: + case 204: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.binExp = &CmpBoolExp{left: yyDollar[1].exp, op: EQ, right: &NullValue{t: AnyType}} } - case 204: + case 205: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.binExp = &CmpBoolExp{left: yyDollar[1].exp, op: NE, right: &NullValue{t: AnyType}} diff --git a/embedded/sql/sql_tx.go b/embedded/sql/sql_tx.go index cef090dc32..c411a5885b 100644 --- a/embedded/sql/sql_tx.go +++ b/embedded/sql/sql_tx.go @@ -186,3 +186,10 @@ func (sqlTx *SQLTx) removeTempFiles() error { } return nil } + +func (sqlTx *SQLTx) ListUsers(ctx context.Context) ([]User, error) { + if sqlTx.engine.multidbHandler == nil { + return nil, ErrUnspecifiedMultiDBHandler + } + return sqlTx.engine.multidbHandler.ListUsers(ctx) +} diff --git a/embedded/sql/stmt.go b/embedded/sql/stmt.go index 829e0c93c5..ea864c617d 100644 --- a/embedded/sql/stmt.go +++ b/embedded/sql/stmt.go @@ -2677,9 +2677,9 @@ type FnCall struct { func (v *FnCall) inferType(cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) (SQLValueType, error) { fn, err := v.resolveFunc() if err != nil { - return AnyType, err + return AnyType, nil } - return fn.inferType(cols, params, implicitTable) + return fn.InferType(cols, params, implicitTable) } func (v *FnCall) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { @@ -2687,7 +2687,7 @@ func (v *FnCall) requiresType(t SQLValueType, cols map[string]ColDescriptor, par if err != nil { return err } - return fn.requiresType(t, cols, params, implicitTable) + return fn.RequiresType(t, cols, params, implicitTable) } func (v *FnCall) selectors() []Selector { @@ -2744,7 +2744,7 @@ func (v *FnCall) reduceParams(tx *SQLTx, row *Row, implicitTable string) ([]Type func (v *FnCall) resolveFunc() (Function, error) { fn, exists := builtinFunctions[strings.ToUpper(v.fn)] if !exists { - return nil, fmt.Errorf("%w: unkown function %s", ErrIllegalArguments, v.fn) + return nil, fmt.Errorf("%w: unknown function %s", ErrIllegalArguments, v.fn) } return fn, nil } @@ -2997,7 +2997,11 @@ func (ce *CaseWhenExp) inferType(cols map[string]ColDescriptor, params map[strin } inferredResType = t } - return checkType(ce.elseExp, inferredResType) + + if ce.elseExp != nil { + return checkType(ce.elseExp, inferredResType) + } + return inferredResType, nil } func (ce *CaseWhenExp) requiresType(t SQLValueType, cols map[string]ColDescriptor, params map[string]SQLValueType, implicitTable string) error { @@ -3544,6 +3548,12 @@ func (stmt *SelectStmt) genScanSpecs(tx *SQLTx, params map[string]interface{}) ( table, err := tableRef.referencedTable(tx) if err != nil { + if tx.engine.TableResolveFor(tableRef.table) != nil { + return &ScanSpecs{ + groupBySortExps: groupByCols, + orderBySortExps: orderByCols, + }, nil + } return nil, err } @@ -3849,7 +3859,6 @@ func (stmt *tableRef) referencedTable(tx *SQLTx) (*Table, error) { if err != nil { return nil, err } - return table, nil } @@ -3867,11 +3876,14 @@ func (stmt *tableRef) Resolve(ctx context.Context, tx *SQLTx, params map[string] } table, err := stmt.referencedTable(tx) - if err != nil { - return nil, err + if err == nil { + return newRawRowReader(tx, params, table, stmt.period, stmt.as, scanSpecs) } - return newRawRowReader(tx, params, table, stmt.period, stmt.as, scanSpecs) + if resolver := tx.engine.TableResolveFor(stmt.table); resolver != nil { + return resolver.Resolve(ctx, tx, stmt.Alias()) + } + return nil, err } func (stmt *tableRef) Alias() string { @@ -3955,7 +3967,7 @@ func (ds *valuesDataSource) Resolve(ctx context.Context, tx *SQLTx, params map[s for i, rowSpec := range ds.rows { values[i] = rowSpec.Values } - return newValuesRowReader(tx, params, cols, ds.inferTypes, "values", values) + return NewValuesRowReader(tx, params, cols, ds.inferTypes, "values", values) } type JoinSpec struct { @@ -4018,7 +4030,6 @@ func (sel *ColSelector) inferType(cols map[string]ColDescriptor, params map[stri if !ok { return AnyType, fmt.Errorf("%w (%s)", ErrColumnDoesNotExist, col) } - return desc.Type, nil } @@ -4490,7 +4501,7 @@ func (bexp *LikeBoolExp) reduce(tx *SQLTx, row *Row, implicitTable string) (Type } if rval.IsNull() { - return &Bool{val: false}, nil + return &Bool{val: bexp.notLike}, nil } rvalStr, ok := rval.RawValue().(string) @@ -4623,7 +4634,6 @@ func (bexp *CmpBoolExp) requiresType(t SQLValueType, cols map[string]ColDescript } _, err := bexp.inferType(cols, params, implicitTable) - return err } @@ -5310,7 +5320,7 @@ func (stmt *FnDataSourceStmt) resolveListDatabases(ctx context.Context, tx *SQLT values[i] = []ValueExp{&Varchar{val: db}} } - return newValuesRowReader(tx, params, cols, true, stmt.Alias(), values) + return NewValuesRowReader(tx, params, cols, true, stmt.Alias(), values) } func (stmt *FnDataSourceStmt) resolveListTables(ctx context.Context, tx *SQLTx, params map[string]interface{}, _ *ScanSpecs) (rowReader RowReader, err error) { @@ -5332,7 +5342,7 @@ func (stmt *FnDataSourceStmt) resolveListTables(ctx context.Context, tx *SQLTx, values[i] = []ValueExp{&Varchar{val: t.name}} } - return newValuesRowReader(tx, params, cols, true, stmt.Alias(), values) + return NewValuesRowReader(tx, params, cols, true, stmt.Alias(), values) } func (stmt *FnDataSourceStmt) resolveShowTable(ctx context.Context, tx *SQLTx, params map[string]interface{}, _ *ScanSpecs) (rowReader RowReader, err error) { @@ -5410,7 +5420,7 @@ func (stmt *FnDataSourceStmt) resolveShowTable(ctx context.Context, tx *SQLTx, p } } - return newValuesRowReader(tx, params, cols, true, stmt.Alias(), values) + return NewValuesRowReader(tx, params, cols, true, stmt.Alias(), values) } func (stmt *FnDataSourceStmt) resolveListUsers(ctx context.Context, tx *SQLTx, params map[string]interface{}, _ *ScanSpecs) (rowReader RowReader, err error) { @@ -5429,19 +5439,12 @@ func (stmt *FnDataSourceStmt) resolveListUsers(ctx context.Context, tx *SQLTx, p }, } - var users []User - - if tx.engine.multidbHandler == nil { - return nil, ErrUnspecifiedMultiDBHandler - } else { - users, err = tx.engine.multidbHandler.ListUsers(ctx) - if err != nil { - return nil, err - } + users, err := tx.ListUsers(ctx) + if err != nil { + return nil, err } values := make([][]ValueExp, len(users)) - for i, user := range users { perm := user.Permission() @@ -5450,8 +5453,7 @@ func (stmt *FnDataSourceStmt) resolveListUsers(ctx context.Context, tx *SQLTx, p &Varchar{val: perm}, } } - - return newValuesRowReader(tx, params, cols, true, stmt.Alias(), values) + return NewValuesRowReader(tx, params, cols, true, stmt.Alias(), values) } func (stmt *FnDataSourceStmt) resolveListColumns(ctx context.Context, tx *SQLTx, params map[string]interface{}, _ *ScanSpecs) (RowReader, error) { @@ -5546,7 +5548,7 @@ func (stmt *FnDataSourceStmt) resolveListColumns(ctx context.Context, tx *SQLTx, } } - return newValuesRowReader(tx, params, cols, true, stmt.Alias(), values) + return NewValuesRowReader(tx, params, cols, true, stmt.Alias(), values) } func (stmt *FnDataSourceStmt) resolveListIndexes(ctx context.Context, tx *SQLTx, params map[string]interface{}, _ *ScanSpecs) (RowReader, error) { @@ -5603,7 +5605,7 @@ func (stmt *FnDataSourceStmt) resolveListIndexes(ctx context.Context, tx *SQLTx, } } - return newValuesRowReader(tx, params, cols, true, stmt.Alias(), values) + return NewValuesRowReader(tx, params, cols, true, stmt.Alias(), values) } func (stmt *FnDataSourceStmt) resolveListGrants(ctx context.Context, tx *SQLTx, params map[string]interface{}, _ *ScanSpecs) (RowReader, error) { @@ -5665,7 +5667,7 @@ func (stmt *FnDataSourceStmt) resolveListGrants(ctx context.Context, tx *SQLTx, } } - return newValuesRowReader(tx, params, cols, true, stmt.Alias(), values) + return NewValuesRowReader(tx, params, cols, true, stmt.Alias(), values) } // DropTableStmt represents a statement to delete a table. diff --git a/embedded/sql/values_row_reader.go b/embedded/sql/values_row_reader.go index 398e56843d..490f1e83a9 100644 --- a/embedded/sql/values_row_reader.go +++ b/embedded/sql/values_row_reader.go @@ -37,7 +37,7 @@ type valuesRowReader struct { closed bool } -func newValuesRowReader(tx *SQLTx, params map[string]interface{}, cols []ColDescriptor, checkTypes bool, tableAlias string, values [][]ValueExp) (*valuesRowReader, error) { +func NewValuesRowReader(tx *SQLTx, params map[string]interface{}, cols []ColDescriptor, checkTypes bool, tableAlias string, values [][]ValueExp) (*valuesRowReader, error) { if tableAlias == "" { return nil, fmt.Errorf("%w: table alias is mandatory", ErrIllegalArguments) } diff --git a/embedded/sql/values_row_reader_test.go b/embedded/sql/values_row_reader_test.go index 00b9d94f8d..bd03fef943 100644 --- a/embedded/sql/values_row_reader_test.go +++ b/embedded/sql/values_row_reader_test.go @@ -24,23 +24,23 @@ import ( ) func TestValuesRowReader(t *testing.T) { - _, err := newValuesRowReader(nil, nil, nil, true, "", nil) + _, err := NewValuesRowReader(nil, nil, nil, true, "", nil) require.ErrorIs(t, err, ErrIllegalArguments) cols := []ColDescriptor{ {Column: "col1"}, } - _, err = newValuesRowReader(nil, nil, cols, true, "", nil) + _, err = NewValuesRowReader(nil, nil, cols, true, "", nil) require.ErrorIs(t, err, ErrIllegalArguments) - _, err = newValuesRowReader(nil, nil, cols, true, "", nil) + _, err = NewValuesRowReader(nil, nil, cols, true, "", nil) require.ErrorIs(t, err, ErrIllegalArguments) - _, err = newValuesRowReader(nil, nil, cols, true, "table1", nil) + _, err = NewValuesRowReader(nil, nil, cols, true, "table1", nil) require.NoError(t, err) - _, err = newValuesRowReader(nil, nil, cols, true, "table1", + _, err = NewValuesRowReader(nil, nil, cols, true, "table1", [][]ValueExp{ { &Bool{val: true}, @@ -49,7 +49,7 @@ func TestValuesRowReader(t *testing.T) { }) require.ErrorIs(t, err, ErrInvalidNumberOfValues) - _, err = newValuesRowReader(nil, nil, + _, err = NewValuesRowReader(nil, nil, []ColDescriptor{ {Table: "table1", Column: "col1"}, }, true, "", nil) @@ -65,7 +65,7 @@ func TestValuesRowReader(t *testing.T) { "param1": 1, } - rowReader, err := newValuesRowReader(nil, params, cols, true, "table1", values) + rowReader, err := NewValuesRowReader(nil, params, cols, true, "table1", values) require.NoError(t, err) require.Nil(t, rowReader.OrderBy()) require.Nil(t, rowReader.ScanSpecs()) diff --git a/embedded/store/ongoing_tx.go b/embedded/store/ongoing_tx.go index 08c983ccfc..b82684f89a 100644 --- a/embedded/store/ongoing_tx.go +++ b/embedded/store/ongoing_tx.go @@ -812,6 +812,10 @@ func (tx *OngoingTx) validateAgainst(hdr *TxHeader) error { return nil } +func (tx *OngoingTx) Context() context.Context { + return tx.ctx +} + func cp(s []byte) []byte { if s == nil { return nil diff --git a/pkg/database/database.go b/pkg/database/database.go index 7b1229810a..3359600775 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -34,6 +34,7 @@ import ( "github.com/codenotary/immudb/embedded/logger" "github.com/codenotary/immudb/pkg/api/schema" + "github.com/codenotary/immudb/pkg/pgsql/pgschema" ) const ( @@ -179,7 +180,12 @@ type db struct { } // OpenDB Opens an existing Database from disk -func OpenDB(dbName string, multidbHandler sql.MultiDBHandler, opts *Options, log logger.Logger) (DB, error) { +func OpenDB( + dbName string, + multidbHandler sql.MultiDBHandler, + opts *Options, + log logger.Logger, +) (DB, error) { if dbName == "" { return nil, fmt.Errorf("%w: invalid database name provided '%s'", ErrIllegalArguments, dbName) } @@ -233,13 +239,15 @@ func OpenDB(dbName string, multidbHandler sql.MultiDBHandler, opts *Options, log sqlOpts := sql.DefaultOptions(). WithPrefix([]byte{SQLPrefix}). WithMultiDBHandler(multidbHandler). - WithParseTxMetadataFunc(parseTxMetadata) + WithParseTxMetadataFunc(parseTxMetadata). + WithTableResolvers(pgschema.PgCatalogResolvers()...) dbi.sqlEngine, err = sql.NewEngine(dbi.st, sqlOpts) if err != nil { dbi.Logger.Errorf("unable to load sql-engine for database '%s' {replica = %v}. %v", dbName, opts.replica, err) return nil, err } + dbi.Logger.Infof("sql-engine ready for database '%s' {replica = %v}", dbName, opts.replica) dbi.documentEngine, err = document.NewEngine(dbi.st, document.DefaultOptions().WithPrefix([]byte{DocumentPrefix})) @@ -357,7 +365,8 @@ func NewDB(dbName string, multidbHandler sql.MultiDBHandler, opts *Options, log sqlOpts := sql.DefaultOptions(). WithPrefix([]byte{SQLPrefix}). WithMultiDBHandler(multidbHandler). - WithParseTxMetadataFunc(parseTxMetadata) + WithParseTxMetadataFunc(parseTxMetadata). + WithTableResolvers(pgschema.PgCatalogResolvers()...) dbi.Logger.Infof("loading sql-engine for database '%s' {replica = %v}...", dbName, opts.replica) diff --git a/pkg/pgsql/pgschema/resolvers_test.go b/pkg/pgsql/pgschema/resolvers_test.go new file mode 100644 index 0000000000..08bfe410cc --- /dev/null +++ b/pkg/pgsql/pgschema/resolvers_test.go @@ -0,0 +1,167 @@ +/* +Copyright 2024 Codenotary Inc. All rights reserved. + +SPDX-License-Identifier: BUSL-1.1 +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://mariadb.com/bsl11/ + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pgschema + +import ( + "context" + "fmt" + "testing" + + "github.com/codenotary/immudb/embedded/sql" + "github.com/codenotary/immudb/embedded/store" + "github.com/stretchr/testify/require" +) + +func setupEngine(t *testing.T, multiDBHandler sql.MultiDBHandler) *sql.Engine { + st, err := store.Open(t.TempDir(), store.DefaultOptions().WithMultiIndexing(true)) + require.NoError(t, err) + t.Cleanup(func() { st.Close() }) + + opts := sql.DefaultOptions(). + WithTableResolvers(PgCatalogResolvers()...) + if multiDBHandler != nil { + opts = opts.WithMultiDBHandler(multiDBHandler) + } + + engine, err := sql.NewEngine(st, opts) + require.NoError(t, err) + return engine +} + +func TestQueryPgCatalogTables(t *testing.T) { + engine := setupEngine(t, &mockMultiDBHandler{ + users: []sql.User{ + &user{username: "immudb", perm: sql.PermissionSysAdmin}, + }, + }) + + _, _, err := engine.Exec(context.Background(), + nil, + `CREATE TABLE table1 (id INTEGER, PRIMARY KEY id)`, + nil) + require.NoError(t, err) + + res, err := engine.Query( + context.Background(), + nil, + `SELECT n.nspname as "Schema", + c.relname as "Name", + CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' WHEN 'I' THEN 'partitioned index' END as "Type", + pg_get_userbyid(c.relowner) as "Owner" + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relkind IN ('r','p','') + AND n.nspname <> 'pg_catalog' + AND n.nspname !~ '^pg_toast' + AND n.nspname <> 'information_schema' + AND pg_table_is_visible(c.oid) + ORDER BY 1,2;`, + nil, + ) + require.NoError(t, err) + defer res.Close() + + row, err := res.Read(context.Background()) + require.NoError(t, err) + + name, _ := row.ValuesBySelector[sql.EncodeSelector("", "c", "name")].RawValue().(string) + owner, _ := row.ValuesBySelector[sql.EncodeSelector("", "c", "owner")].RawValue().(string) + relType, _ := row.ValuesBySelector[sql.EncodeSelector("", "c", "type")].RawValue().(string) + schema := row.ValuesBySelector[sql.EncodeSelector("", "n", "schema")].RawValue() + + require.Equal(t, "table1", name) + require.Equal(t, "immudb", owner) + require.Equal(t, "table", relType) + require.Nil(t, schema) +} + +func TestQueryPgRolesTable(t *testing.T) { + engine := setupEngine(t, &mockMultiDBHandler{ + users: []sql.User{ + &user{username: "immudb", perm: sql.PermissionSysAdmin}, + &user{username: "user1", perm: sql.PermissionReadWrite}, + }, + }) + + rows, err := engine.Query( + context.Background(), + nil, + ` + SELECT r.rolname, r.rolsuper, r.rolinherit, + r.rolcreaterole, r.rolcreatedb, r.rolcanlogin, + r.rolconnlimit, r.rolvaliduntil, r.rolreplication, + r.rolbypassrls + + FROM pg_roles r + WHERE r.rolname !~ '^pg_' + ORDER BY 1;`, + nil, + ) + require.NoError(t, err) + + row, err := rows.Read(context.Background()) + require.NoError(t, err) + + name, _ := row.ValuesBySelector[sql.EncodeSelector("", "r", "rolname")].RawValue().(string) + require.Equal(t, "immudb", name) + + roleSuper, _ := row.ValuesBySelector[sql.EncodeSelector("", "r", "rolsuper")].RawValue().(bool) + require.True(t, roleSuper) + + row, err = rows.Read(context.Background()) + require.NoError(t, err) + + name, _ = row.ValuesBySelector[sql.EncodeSelector("", "r", "rolname")].RawValue().(string) + require.Equal(t, "user1", name) + + roleSuper, _ = row.ValuesBySelector[sql.EncodeSelector("", "r", "rolsuper")].RawValue().(bool) + require.False(t, roleSuper) +} + +type mockMultiDBHandler struct { + sql.MultiDBHandler + + users []sql.User +} + +type user struct { + username string + perm sql.Permission +} + +func (u *user) Username() string { + return u.username +} + +func (u *user) Permission() sql.Permission { + return u.perm +} + +func (u *user) SQLPrivileges() []sql.SQLPrivilege { + return []sql.SQLPrivilege{sql.SQLPrivilegeCreate, sql.SQLPrivilegeSelect} +} + +func (h *mockMultiDBHandler) ListUsers(ctx context.Context) ([]sql.User, error) { + return h.users, nil +} + +func (h *mockMultiDBHandler) GetLoggedUser(ctx context.Context) (sql.User, error) { + if len(h.users) == 0 { + return nil, fmt.Errorf("no logged user") + } + return h.users[0], nil +} diff --git a/pkg/pgsql/pgschema/table_resolvers.go b/pkg/pgsql/pgschema/table_resolvers.go new file mode 100644 index 0000000000..2c6480b76f --- /dev/null +++ b/pkg/pgsql/pgschema/table_resolvers.go @@ -0,0 +1,382 @@ +/* +Copyright 2024 Codenotary Inc. All rights reserved. + +SPDX-License-Identifier: BUSL-1.1 +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://mariadb.com/bsl11/ + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pgschema + +import ( + "context" + + "github.com/codenotary/immudb/embedded/sql" +) + +var pgClassCols = []sql.ColDescriptor{ + { + Column: "oid", + Type: sql.IntegerType, + }, + { + Column: "relname", + Type: sql.VarcharType, + }, + { + Column: "relnamespace", + Type: sql.IntegerType, + }, + { + + Column: "reltype", + Type: sql.VarcharType, + }, + { + + Column: "reloftype", + Type: sql.IntegerType, + }, + { + + Column: "relowner", + Type: sql.IntegerType, + }, + { + + Column: "relam", + Type: sql.IntegerType, + }, + { + + Column: "relfilenode", + Type: sql.IntegerType, + }, + { + + Column: "reltablespace", + Type: sql.IntegerType, + }, + { + + Column: "relpages", + Type: sql.IntegerType, + }, + { + + Column: "reltuples", + Type: sql.Float64Type, + }, + { + + Column: "relallvisible", + Type: sql.IntegerType, + }, + { + + Column: "reltoastrelid", + Type: sql.IntegerType, + }, + { + + Column: "relhasindex", + Type: sql.BooleanType, + }, + { + + Column: "relisshared", + Type: sql.BooleanType, + }, + { + + Column: "relpersistence", + Type: sql.VarcharType, + }, + { + + Column: "relkind", + Type: sql.VarcharType, + }, + { + + Column: "relnats", + Type: sql.IntegerType, + }, + { + + Column: "relchecks", + Type: sql.IntegerType, + }, + { + + Column: "relhasrules", + Type: sql.BooleanType, + }, + { + + Column: "relhastriggers", + Type: sql.BooleanType, + }, + { + + Column: "relhassubclass", + Type: sql.BooleanType, + }, + { + + Column: "relrowsecurity", + Type: sql.BooleanType, + }, + { + + Column: "relforcerowsecurity", + Type: sql.BooleanType, + }, + { + Column: "relispopulated", + Type: sql.BooleanType, + }, + { + Column: "relreplident", + Type: sql.VarcharType, + }, + { + Column: "relispartition", + Type: sql.BooleanType, + }, + { + Column: "relrewrite", + Type: sql.IntegerType, + }, + { + Column: "relfrozenxid", + Type: sql.IntegerType, + }, + { + Column: "relminmxid", + Type: sql.IntegerType, + }, + { + Column: "relacl", + Type: sql.AnyType, + }, + { + Column: "reloptions", + Type: sql.AnyType, + }, + { + Column: "relpartbound", + Type: sql.AnyType, + }, +} + +type pgClassResolver struct{} + +func (r *pgClassResolver) Resolve(ctx context.Context, tx *sql.SQLTx, alias string) (sql.RowReader, error) { + catalog := tx.Catalog() + tables := catalog.GetTables() + + rows := make([][]sql.ValueExp, len(tables)) + for i, t := range tables { + rows[i] = []sql.ValueExp{ + sql.NewInteger(int64(t.ID())), // oid + sql.NewVarchar(t.Name()), // relname + sql.NewInteger(-1), // relnamespace + sql.NewVarchar(""), // reltype + sql.NewNull(sql.IntegerType), // reloftype + sql.NewInteger(0), // relowner + sql.NewNull(sql.IntegerType), // relam + sql.NewNull(sql.IntegerType), // relfilenode + sql.NewNull(sql.IntegerType), // reltablespace + sql.NewNull(sql.IntegerType), // relpages + sql.NewNull(sql.Float64Type), // reltuples + sql.NewNull(sql.IntegerType), // relallvisible + sql.NewNull(sql.IntegerType), // reltoastrelid + sql.NewBool(len(t.GetIndexes()) > 1), // relhasindex + sql.NewBool(false), // relisshared + sql.NewNull(sql.VarcharType), // relpersistence + sql.NewVarchar("r"), // relkind + sql.NewNull(sql.IntegerType), // relnats + sql.NewNull(sql.IntegerType), // relchecks + sql.NewBool(false), // relhasrules + sql.NewBool(false), // relhastriggers + sql.NewBool(false), // relhassubclass + sql.NewBool(false), // relrowsecurity + sql.NewBool(false), // relforcerowsecurity + sql.NewBool(false), // relispopulated + sql.NewVarchar(""), // relreplident + sql.NewBool(false), // relispartition + sql.NewInteger(0), // relrewrite + sql.NewNull(sql.IntegerType), // relfrozenxid + sql.NewNull(sql.IntegerType), // relminmxid + sql.NewNull(sql.AnyType), // relacl + sql.NewNull(sql.AnyType), // reloptions + sql.NewNull(sql.AnyType), // relpartbound + } + } + + return sql.NewValuesRowReader( + tx, + nil, + pgClassCols, + true, + alias, + rows, + ) +} + +func (r *pgClassResolver) Table() string { + return "pg_class" +} + +var pgNamespaceCols = []sql.ColDescriptor{ + { + Column: "oid", + Type: sql.IntegerType, + }, + { + Column: "nspname", + Type: sql.VarcharType, + }, + { + Column: "nspowner", + Type: sql.IntegerType, + }, + { + Column: "nspacl", + Type: sql.AnyType, + }, +} + +type pgNamespaceResolver struct{} + +func (r *pgNamespaceResolver) Resolve(ctx context.Context, tx *sql.SQLTx, alias string) (sql.RowReader, error) { + return sql.NewValuesRowReader( + tx, + nil, + pgNamespaceCols, + true, + alias, + nil, + ) +} + +func (r *pgNamespaceResolver) Table() string { + return "pg_namespace" +} + +var pgRolesCols = []sql.ColDescriptor{ + { + Column: "rolname", + Type: sql.VarcharType, + }, + { + Column: "rolsuper", + Type: sql.BooleanType, + }, + { + Column: "rolinherit", + Type: sql.BooleanType, + }, + { + Column: "rolcreaterole", + Type: sql.BooleanType, + }, + { + Column: "rolcreatedb", + Type: sql.BooleanType, + }, + { + Column: "rolcanlogin", + Type: sql.BooleanType, + }, + { + Column: "rolreplication", + Type: sql.BooleanType, + }, + { + Column: "rolconnlimit", + Type: sql.IntegerType, + }, + { + Column: "rolpassword", + Type: sql.VarcharType, + }, + { + Column: "rolvaliduntil", + Type: sql.TimestampType, + }, + { + Column: "rolbypassrls", + Type: sql.BooleanType, + }, + { + Column: "rolconfig", + Type: sql.AnyType, + }, + { + Column: "oid", + Type: sql.IntegerType, + }, +} + +type pgRolesResolver struct{} + +func (r *pgRolesResolver) Resolve(ctx context.Context, tx *sql.SQLTx, alias string) (sql.RowReader, error) { + users, err := tx.ListUsers(ctx) + if err != nil { + return nil, err + } + + rows := make([][]sql.ValueExp, len(users)) + for i, u := range users { + isAdmin := u.Permission() == sql.PermissionSysAdmin || u.Permission() == sql.PermissionAdmin + + rows[i] = []sql.ValueExp{ + sql.NewVarchar(u.Username()), // name + sql.NewBool(isAdmin), // rolsuper + sql.NewBool(isAdmin), // rolinherit + sql.NewBool(isAdmin), // rolcreaterole + sql.NewBool(isAdmin), // rolcreatedb + sql.NewBool(true), // rolcanlogin + sql.NewBool(false), // rolreplication + sql.NewInteger(-1), // rolconnlimit + sql.NewVarchar("********"), // rolpassword + sql.NewNull(sql.TimestampType), // rolvaliduntil + sql.NewBool(isAdmin), // rolbypassrls + sql.NewNull(sql.AnyType), // rolconfig + sql.NewNull(sql.IntegerType), // oid + } + } + + return sql.NewValuesRowReader( + tx, + nil, + pgRolesCols, + true, + alias, + rows, + ) +} + +func (r *pgRolesResolver) Table() string { + return "pg_roles" +} + +var tableResolvers = []sql.TableResolver{ + &pgClassResolver{}, + &pgNamespaceResolver{}, + &pgRolesResolver{}, +} + +func PgCatalogResolvers() []sql.TableResolver { + return tableResolvers +} diff --git a/pkg/pgsql/server/pgmeta/pg_type.go b/pkg/pgsql/server/pgmeta/pg_type.go index 7a2839290a..89ee3e5f26 100644 --- a/pkg/pgsql/server/pgmeta/pg_type.go +++ b/pkg/pgsql/server/pgmeta/pg_type.go @@ -47,6 +47,7 @@ var PgTypeMap = map[string][]int{ sql.UUIDType: {2950, 16}, //uuid sql.Float64Type: {701, 8}, //double-precision floating point number sql.JSONType: {114, -1}, //json + sql.AnyType: {17, -1}, // bytea } const PgSeverityError = "ERROR" diff --git a/pkg/pgsql/server/query_machine.go b/pkg/pgsql/server/query_machine.go index 0de4e4bedd..9e6b0949ca 100644 --- a/pkg/pgsql/server/query_machine.go +++ b/pkg/pgsql/server/query_machine.go @@ -272,7 +272,11 @@ func (s *session) fetchAndWriteResults(statements string, parameters []*schema.N return err } - stmts, err := sql.ParseSQL(strings.NewReader(statements)) + stmts, err := sql.ParseSQL( + strings.NewReader( + removePGCatalogReferences(statements), + ), + ) if err != nil { return err } @@ -302,6 +306,10 @@ func (s *session) fetchAndWriteResults(statements string, parameters []*schema.N return nil } +func removePGCatalogReferences(sql string) string { + return strings.ReplaceAll(sql, "pg_catalog.", "") +} + func (s *session) query(st *sql.SelectStmt, parameters []*schema.NamedParam, resultColumnFormatCodes []int16, skipRowDesc bool) error { tx, err := s.sqlTx() if err != nil { diff --git a/pkg/pgsql/server/stmts_handler.go b/pkg/pgsql/server/stmts_handler.go index 0a5e40ea22..0fb6796675 100644 --- a/pkg/pgsql/server/stmts_handler.go +++ b/pkg/pgsql/server/stmts_handler.go @@ -22,14 +22,17 @@ import ( pserr "github.com/codenotary/immudb/pkg/pgsql/errors" ) -var set = regexp.MustCompile(`(?i)set\s+.+`) -var selectVersion = regexp.MustCompile(`(?i)select\s+version\(\s*\)`) -var dealloc = regexp.MustCompile(`(?i)deallocate\s+\"([^\"]+)\"`) +var ( + set = regexp.MustCompile(`(?i)set\s+.+`) + selectVersion = regexp.MustCompile(`(?i)select\s+version\(\s*\)`) + dealloc = regexp.MustCompile(`(?i)deallocate\s+\"([^\"]+)\"`) +) func (s *session) isInBlackList(statement string) bool { if set.MatchString(statement) { return true } + if statement == ";" { return true } diff --git a/pkg/server/server.go b/pkg/server/server.go index 93d90f683d..e864ca963e 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -278,7 +278,6 @@ func (s *ImmuServer) Initialize() error { return err } } - return err }