Line data Source code
1 : // Tests for `EvalExpr` (core expression forms).
2 :
3 : #include "backend/engine/semantic/error.h"
4 : #include "backend/engine/semantic/eval_expr_test_fixture.h"
5 : #include "backend/engine/semantic/value.h"
6 :
7 : namespace bigquery_emulator {
8 : namespace backend {
9 : namespace engine {
10 : namespace semantic {
11 :
12 1 : TEST_F(EvalExprTest, LiteralInt64) {
13 1 : const auto* expr = AnalyzeExpr("42");
14 1 : ASSERT_NE(expr, nullptr);
15 1 : auto v = EvalExpr(*expr, EvalContext{});
16 2 : ASSERT_TRUE(v.ok()) << v.status();
17 1 : EXPECT_EQ(v->int64_value(), 42);
18 1 : }
19 :
20 1 : TEST_F(EvalExprTest, LiteralStringRoundTrips) {
21 1 : const auto* expr = AnalyzeExpr("'hello'");
22 1 : ASSERT_NE(expr, nullptr);
23 1 : auto v = EvalExpr(*expr, EvalContext{});
24 2 : ASSERT_TRUE(v.ok()) << v.status();
25 1 : EXPECT_EQ(v->string_value(), "hello");
26 1 : }
27 :
28 1 : TEST_F(EvalExprTest, AdditionInt64) {
29 1 : const auto* expr = AnalyzeExpr("1 + 2");
30 1 : ASSERT_NE(expr, nullptr);
31 1 : auto v = EvalExpr(*expr, EvalContext{});
32 2 : ASSERT_TRUE(v.ok()) << v.status();
33 1 : EXPECT_EQ(v->int64_value(), 3);
34 1 : }
35 :
36 1 : TEST_F(EvalExprTest, AdditionNullOperandIsNull) {
37 1 : const auto* expr = AnalyzeExpr("CAST(NULL AS INT64) + 1");
38 1 : ASSERT_NE(expr, nullptr);
39 1 : auto v = EvalExpr(*expr, EvalContext{});
40 2 : ASSERT_TRUE(v.ok()) << v.status();
41 1 : EXPECT_TRUE(v->is_null());
42 1 : }
43 :
44 1 : TEST_F(EvalExprTest, Int64OverflowSurfacesAsSemanticError) {
45 1 : const auto* expr = AnalyzeExpr("9223372036854775807 + 1");
46 1 : ASSERT_NE(expr, nullptr);
47 1 : auto v = EvalExpr(*expr, EvalContext{});
48 1 : ASSERT_FALSE(v.ok());
49 1 : EXPECT_EQ(GetSemanticErrorReason(v.status()), SemanticErrorReason::kOverflow);
50 1 : }
51 :
52 1 : TEST_F(EvalExprTest, DivisionByZeroFloat64SurfacesReason) {
53 1 : const auto* expr = AnalyzeExpr("CAST(1 AS FLOAT64) / 0");
54 1 : ASSERT_NE(expr, nullptr);
55 1 : auto v = EvalExpr(*expr, EvalContext{});
56 1 : ASSERT_FALSE(v.ok());
57 1 : EXPECT_EQ(GetSemanticErrorReason(v.status()),
58 1 : SemanticErrorReason::kDivisionByZero);
59 1 : }
60 :
61 1 : TEST_F(EvalExprTest, ComparisonLessThan) {
62 1 : const auto* expr = AnalyzeExpr("1 < 2");
63 1 : ASSERT_NE(expr, nullptr);
64 1 : auto v = EvalExpr(*expr, EvalContext{});
65 2 : ASSERT_TRUE(v.ok()) << v.status();
66 1 : EXPECT_TRUE(v->bool_value());
67 1 : }
68 :
69 1 : TEST_F(EvalExprTest, ComparisonNullOperandIsNull) {
70 1 : const auto* expr = AnalyzeExpr("CAST(NULL AS INT64) = 1");
71 1 : ASSERT_NE(expr, nullptr);
72 1 : auto v = EvalExpr(*expr, EvalContext{});
73 2 : ASSERT_TRUE(v.ok()) << v.status();
74 1 : EXPECT_TRUE(v->is_null());
75 1 : }
76 :
77 1 : TEST_F(EvalExprTest, LogicalAndShortCircuitOnFalse) {
78 1 : const auto* expr = AnalyzeExpr("FALSE AND CAST(NULL AS BOOL)");
79 1 : ASSERT_NE(expr, nullptr);
80 1 : auto v = EvalExpr(*expr, EvalContext{});
81 2 : ASSERT_TRUE(v.ok()) << v.status();
82 1 : EXPECT_FALSE(v->bool_value());
83 1 : }
84 :
85 1 : TEST_F(EvalExprTest, LogicalOrShortCircuitOnTrue) {
86 1 : const auto* expr = AnalyzeExpr("TRUE OR CAST(NULL AS BOOL)");
87 1 : ASSERT_NE(expr, nullptr);
88 1 : auto v = EvalExpr(*expr, EvalContext{});
89 2 : ASSERT_TRUE(v.ok()) << v.status();
90 1 : EXPECT_TRUE(v->bool_value());
91 1 : }
92 :
93 1 : TEST_F(EvalExprTest, IfReturnsThenBranchOnTrue) {
94 1 : const auto* expr = AnalyzeExpr("IF(1 < 2, 'yes', 'no')");
95 1 : ASSERT_NE(expr, nullptr);
96 1 : auto v = EvalExpr(*expr, EvalContext{});
97 2 : ASSERT_TRUE(v.ok()) << v.status();
98 1 : EXPECT_EQ(v->string_value(), "yes");
99 1 : }
100 :
101 1 : TEST_F(EvalExprTest, IfReturnsElseBranchOnFalse) {
102 1 : const auto* expr = AnalyzeExpr("IF(1 > 2, 'yes', 'no')");
103 1 : ASSERT_NE(expr, nullptr);
104 1 : auto v = EvalExpr(*expr, EvalContext{});
105 2 : ASSERT_TRUE(v.ok()) << v.status();
106 1 : EXPECT_EQ(v->string_value(), "no");
107 1 : }
108 :
109 1 : TEST_F(EvalExprTest, CoalescePicksFirstNonNull) {
110 1 : const auto* expr = AnalyzeExpr("COALESCE(CAST(NULL AS INT64), 2, 3)");
111 1 : ASSERT_NE(expr, nullptr);
112 1 : auto v = EvalExpr(*expr, EvalContext{});
113 2 : ASSERT_TRUE(v.ok()) << v.status();
114 1 : EXPECT_EQ(v->int64_value(), 2);
115 1 : }
116 :
117 1 : TEST_F(EvalExprTest, CoalesceAllNullProducesNull) {
118 1 : const auto* expr =
119 1 : AnalyzeExpr("COALESCE(CAST(NULL AS INT64), CAST(NULL AS INT64))");
120 1 : ASSERT_NE(expr, nullptr);
121 1 : auto v = EvalExpr(*expr, EvalContext{});
122 2 : ASSERT_TRUE(v.ok()) << v.status();
123 1 : EXPECT_TRUE(v->is_null());
124 1 : }
125 :
126 1 : TEST_F(EvalExprTest, IfNullReturnsFallbackOnNull) {
127 1 : const auto* expr = AnalyzeExpr("IFNULL(CAST(NULL AS INT64), 7)");
128 1 : ASSERT_NE(expr, nullptr);
129 1 : auto v = EvalExpr(*expr, EvalContext{});
130 2 : ASSERT_TRUE(v.ok()) << v.status();
131 1 : EXPECT_EQ(v->int64_value(), 7);
132 1 : }
133 :
134 1 : TEST_F(EvalExprTest, NullIfReturnsNullWhenEqual) {
135 1 : const auto* expr = AnalyzeExpr("NULLIF(1, 1)");
136 1 : ASSERT_NE(expr, nullptr);
137 1 : auto v = EvalExpr(*expr, EvalContext{});
138 2 : ASSERT_TRUE(v.ok()) << v.status();
139 1 : EXPECT_TRUE(v->is_null());
140 1 : }
141 :
142 1 : TEST_F(EvalExprTest, NullIfReturnsFirstWhenDifferent) {
143 1 : const auto* expr = AnalyzeExpr("NULLIF(1, 2)");
144 1 : ASSERT_NE(expr, nullptr);
145 1 : auto v = EvalExpr(*expr, EvalContext{});
146 2 : ASSERT_TRUE(v.ok()) << v.status();
147 1 : EXPECT_EQ(v->int64_value(), 1);
148 1 : }
149 :
150 1 : TEST_F(EvalExprTest, CaseWhenBranchesEvaluate) {
151 1 : const auto* expr =
152 1 : AnalyzeExpr("CASE WHEN 1 = 1 THEN 'a' WHEN 2 = 2 THEN 'b' ELSE 'c' END");
153 1 : ASSERT_NE(expr, nullptr);
154 1 : auto v = EvalExpr(*expr, EvalContext{});
155 2 : ASSERT_TRUE(v.ok()) << v.status();
156 1 : EXPECT_EQ(v->string_value(), "a");
157 1 : }
158 :
159 1 : TEST_F(EvalExprTest, CaseWithValueBranchesEvaluate) {
160 1 : const auto* expr =
161 1 : AnalyzeExpr("CASE 2 WHEN 1 THEN 'a' WHEN 2 THEN 'b' ELSE 'c' END");
162 1 : ASSERT_NE(expr, nullptr);
163 1 : auto v = EvalExpr(*expr, EvalContext{});
164 2 : ASSERT_TRUE(v.ok()) << v.status();
165 1 : EXPECT_EQ(v->string_value(), "b");
166 1 : }
167 :
168 1 : TEST_F(EvalExprTest, CaseFallsThroughToElseOnNoMatch) {
169 1 : const auto* expr =
170 1 : AnalyzeExpr("CASE WHEN 1 = 2 THEN 'a' WHEN 1 = 3 THEN 'b' ELSE 'c' END");
171 1 : ASSERT_NE(expr, nullptr);
172 1 : auto v = EvalExpr(*expr, EvalContext{});
173 2 : ASSERT_TRUE(v.ok()) << v.status();
174 1 : EXPECT_EQ(v->string_value(), "c");
175 1 : }
176 :
177 1 : TEST_F(EvalExprTest, SafeAddOverflowProducesNull) {
178 1 : const auto* expr = AnalyzeExpr("SAFE_ADD(9223372036854775807, 1)");
179 1 : ASSERT_NE(expr, nullptr);
180 1 : auto v = EvalExpr(*expr, EvalContext{});
181 2 : ASSERT_TRUE(v.ok()) << v.status();
182 1 : EXPECT_TRUE(v->is_null());
183 1 : }
184 :
185 1 : TEST_F(EvalExprTest, SafeDivideByZeroProducesNull) {
186 1 : const auto* expr = AnalyzeExpr("SAFE_DIVIDE(1, 0)");
187 1 : ASSERT_NE(expr, nullptr);
188 1 : auto v = EvalExpr(*expr, EvalContext{});
189 2 : ASSERT_TRUE(v.ok()) << v.status();
190 1 : EXPECT_TRUE(v->is_null());
191 1 : }
192 :
193 : } // namespace semantic
194 : } // namespace engine
195 : } // namespace backend
196 : } // namespace bigquery_emulator
|