Repository navigation
Expand file tree
/
Copy pathinterval_arithmetic_test.go
More file actions
93 lines (88 loc) · 3.23 KB
/
Copy pathinterval_arithmetic_test.go
File metadata and controls
93 lines (88 loc) · 3.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package sqlparser
import (
"github.com/viant/sqlparser/expr"
"testing"
)
func TestIntervalArithmeticUsesExistingExpressionParser(t *testing.T) {
for _, amount := range []string{":seconds * 2", "15 * 2", "-15", "(:seconds + 1) * 2", ":seconds /* amount */ * 2", "1 + 2 * 3", "CAST(:seconds AS SIGNED) * 2"} {
t.Run(amount, func(t *testing.T) {
SQL := "SELECT CURRENT_TIMESTAMP() - INTERVAL " + amount + " SECOND AS cutoff FROM records WHERE id=1"
parsed, err := ParseQuery(SQL)
if err != nil {
t.Fatal(err)
}
if parsed.Qualify == nil || len(parsed.List) != 1 || parsed.List[0].Alias != "cutoff" {
t.Fatalf("interval consumed surrounding SQL: %#v", parsed)
}
arithmetic, ok := parsed.List[0].Expr.(*expr.Binary)
if !ok {
t.Fatalf("expected subtraction, got %T", parsed.List[0].Expr)
}
interval, ok := arithmetic.Y.(*expr.Unary)
if !ok {
t.Fatalf("expected interval operand, got %T", arithmetic.Y)
}
unit, ok := interval.X.(*expr.Binary)
if !ok {
t.Fatalf("expected interval unit, got %T", interval.X)
}
name, ok := unit.Y.(*expr.Ident)
if !ok || name.Name != "SECOND" {
t.Fatalf("unit lost: %#v", unit.Y)
}
if unit.X == nil {
t.Fatal("amount lost")
}
if amount == "1 + 2 * 3" {
addition, ok := unit.X.(*expr.Binary)
if !ok || addition.Op != "+" {
t.Fatalf("expected addition in amount, got %#v", unit.X)
}
product, ok := addition.Y.(*expr.Binary)
if !ok || product.Op != "*" {
t.Fatalf("multiplication precedence lost: %#v", addition.Y)
}
}
})
}
}
func TestIntervalArithmeticKeepsDerivedSourceAndClauseBoundaries(t *testing.T) {
SQL := "SELECT journal.* FROM (SELECT COUNT(*) AS total FROM records WHERE created >= CURRENT_TIMESTAMP() - INTERVAL :seconds * 2 SECOND AND id=1) journal ORDER BY total DESC"
parsed, err := ParseQuery(SQL)
if err != nil {
t.Fatal(err)
}
raw, ok := parsed.From.X.(*expr.Raw)
if !ok || raw.Raw != "(SELECT COUNT(*) AS total FROM records WHERE created >= CURRENT_TIMESTAMP() - INTERVAL :seconds * 2 SECOND AND id=1)" {
t.Fatalf("derived SQL changed: %#v", parsed.From.X)
}
if parsed.From.Alias != "journal" || len(parsed.OrderBy) != 1 {
t.Fatal("outer SQL boundary lost")
}
}
func TestIntervalArithmeticRequiresCompleteAmountAndUnit(t *testing.T) {
for _, SQL := range []string{"SELECT INTERVAL 1 * SECOND FROM records", "SELECT INTERVAL 1 + FROM records", "SELECT INTERVAL 1 * 2 BOGUS FROM records", "SELECT INTERVAL 1 * 2 FROM records"} {
if _, err := ParseQuery(SQL); err == nil {
t.Fatalf("invalid interval accepted: %s", SQL)
}
}
}
func TestIntervalQuotedLiteralKeepsFollowingArithmetic(t *testing.T) {
parsed, err := ParseQuery("SELECT INTERVAL '1 day' + INTERVAL '2 hours' AS duration FROM records")
if err != nil {
t.Fatal(err)
}
addition, ok := parsed.List[0].Expr.(*expr.Binary)
if !ok || addition.Op != "+" {
t.Fatalf("expected addition between intervals, got %#v", parsed.List[0].Expr)
}
for _, operand := range []interface{}{addition.X, addition.Y} {
interval, ok := operand.(*expr.Unary)
if !ok {
t.Fatalf("expected interval, got %T", operand)
}
if _, ok := interval.X.(*expr.Literal); !ok {
t.Fatalf("quoted interval consumed following expression: %#v", interval.X)
}
}
}