2020-08-06 20:58:47 +03:00
|
|
|
package ast
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
2023-07-03 18:34:19 +03:00
|
|
|
"github.com/neilotoole/sq/testh/tutil"
|
|
|
|
|
2023-04-02 22:49:45 +03:00
|
|
|
"github.com/neilotoole/slogt"
|
|
|
|
|
2023-08-12 22:26:25 +03:00
|
|
|
"github.com/antlr4-go/antlr/v4"
|
2020-08-06 20:58:47 +03:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2020-08-23 14:16:16 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/ast/internal/slq"
|
2020-08-06 20:58:47 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// getSLQParser returns a parser for the given SQL input.
|
|
|
|
func getSLQParser(input string) *slq.SLQParser {
|
|
|
|
is := antlr.NewInputStream(input)
|
|
|
|
lex := slq.NewSLQLexer(is)
|
|
|
|
ts := antlr.NewCommonTokenStream(lex, 0)
|
|
|
|
p := slq.NewSLQParser(ts)
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
// buildInitialAST returns a new AST created by parseTreeVisitor. The AST has not
|
|
|
|
// yet been processed.
|
|
|
|
func buildInitialAST(t *testing.T, input string) (*AST, error) {
|
2023-04-02 22:49:45 +03:00
|
|
|
log := slogt.New(t)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
p := getSLQParser(input)
|
2022-12-18 10:18:35 +03:00
|
|
|
q, _ := p.Query().(*slq.QueryContext)
|
2020-08-06 20:58:47 +03:00
|
|
|
v := &parseTreeVisitor{log: log}
|
|
|
|
err := q.Accept(v)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err.(error)
|
|
|
|
}
|
|
|
|
|
2023-03-22 09:17:34 +03:00
|
|
|
return v.ast, nil
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2023-03-19 07:58:00 +03:00
|
|
|
// mustParse builds a full AST from the input SLQ, or fails on any error.
|
|
|
|
func mustParse(t *testing.T, input string) *AST {
|
2023-04-02 22:49:45 +03:00
|
|
|
log := slogt.New(t)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-03-19 07:58:00 +03:00
|
|
|
ast, err := Parse(log, input)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return ast
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSimpleQuery(t *testing.T) {
|
2023-07-03 18:34:19 +03:00
|
|
|
const q1 = `@mydb1 | .user | .uid, .username`
|
2023-04-02 22:49:45 +03:00
|
|
|
log := slogt.New(t)
|
2023-03-19 07:58:00 +03:00
|
|
|
|
2023-07-03 18:34:19 +03:00
|
|
|
ptree, err := parseSLQ(log, q1)
|
2020-08-06 20:58:47 +03:00
|
|
|
require.Nil(t, err)
|
|
|
|
require.NotNil(t, ptree)
|
|
|
|
|
|
|
|
ast, err := buildAST(log, ptree)
|
|
|
|
require.Nil(t, err)
|
|
|
|
require.NotNil(t, ast)
|
|
|
|
}
|
|
|
|
|
2023-07-03 18:34:19 +03:00
|
|
|
// TestParseBuild performs some basic testing of the parser.
|
|
|
|
// These tests are largely duplicates of other tests, and
|
|
|
|
// probably should be consolidated.
|
2020-08-06 20:58:47 +03:00
|
|
|
func TestParseBuild(t *testing.T) {
|
2023-07-03 18:34:19 +03:00
|
|
|
testCases := []struct {
|
|
|
|
name string
|
|
|
|
in string
|
|
|
|
}{
|
|
|
|
{"rr1", `@mydb1 | .user | .uid, .username | .[]`},
|
|
|
|
{"rr2", `@mydb1 | .user | .uid, .username | .[2]`},
|
|
|
|
{"rr3", `@mydb1 | .user | .uid, .username | .[1:3]`},
|
|
|
|
{"rr4", `@mydb1 | .user | .uid, .username | .[0:3]`},
|
|
|
|
{"rr5", `@mydb1 | .user | .uid, .username | .[:3]`},
|
|
|
|
{"rr6", `@mydb1 | .user | .uid, .username | .[2:]`},
|
|
|
|
{"join with row range", `@my1 |.user | join(.address, .uid) | .[0:4] | .user.uid, .username, .country`},
|
|
|
|
{"join1", `@mydb1 | .user | join(.address, .user.uid == .address.uid) | .uid, .username, .country`},
|
|
|
|
{"select1", `@mydb1 | .user | .uid, .username`},
|
|
|
|
{"tbl datasource", `@mydb1.user | .uid, .username`},
|
|
|
|
{"count1", `@mydb1.user | count`},
|
|
|
|
}
|
2023-03-22 09:17:34 +03:00
|
|
|
|
2023-07-03 18:34:19 +03:00
|
|
|
for i, tc := range testCases {
|
|
|
|
t.Run(tutil.Name(i, tc.name), func(t *testing.T) {
|
|
|
|
t.Logf(tc.in)
|
2023-04-02 22:49:45 +03:00
|
|
|
log := slogt.New(t)
|
2023-03-22 09:17:34 +03:00
|
|
|
|
2023-07-03 18:34:19 +03:00
|
|
|
ptree, err := parseSLQ(log, tc.in)
|
2023-03-22 09:17:34 +03:00
|
|
|
require.Nil(t, err)
|
|
|
|
require.NotNil(t, ptree)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-03-22 09:17:34 +03:00
|
|
|
ast, err := buildAST(log, ptree)
|
|
|
|
require.Nil(t, err)
|
|
|
|
require.NotNil(t, ast)
|
|
|
|
})
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInspector_FindWhereClauses(t *testing.T) {
|
2023-04-02 22:49:45 +03:00
|
|
|
log := slogt.New(t)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-06-17 07:54:25 +03:00
|
|
|
// Verify that "where(.uid > 4)" becomes a WHERE clause.
|
|
|
|
const input = "@my1 | .actor | where(.uid > 4) | .uid, .username"
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
ptree, err := parseSLQ(log, input)
|
|
|
|
require.Nil(t, err)
|
|
|
|
require.NotNil(t, ptree)
|
|
|
|
|
|
|
|
nRoot, err := buildAST(log, ptree)
|
|
|
|
require.Nil(t, err)
|
|
|
|
|
2023-04-07 11:00:49 +03:00
|
|
|
insp := NewInspector(nRoot)
|
2020-08-06 20:58:47 +03:00
|
|
|
whereNodes, err := insp.FindWhereClauses()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, whereNodes, 1)
|
|
|
|
}
|