Line data Source code
1 : // Matrix coverage for ParseParameterValue wire forms (query-parameter
2 : // wire-form matrix).
3 : #include "absl/time/time.h"
4 : #include "backend/engine/semantic/value.h"
5 : #include "googlesql/public/numeric_value.h"
6 : #include "googlesql/public/type.h"
7 : #include "googlesql/public/value.h"
8 : #include "gtest/gtest.h"
9 :
10 : namespace bigquery_emulator {
11 : namespace backend {
12 : namespace engine {
13 : namespace semantic {
14 : namespace {
15 :
16 : struct ParameterMatrixCase {
17 : const char* type_kind;
18 : const char* type_json;
19 : const char* value_json;
20 : bool should_succeed;
21 : const char* label;
22 : };
23 :
24 1 : std::vector<ParameterMatrixCase> AcceptedMatrixCases() {
25 1 : return {
26 1 : {"TIMESTAMP",
27 1 : nullptr,
28 1 : "2026-06-22T10:00:00",
29 1 : true,
30 1 : "timestamp_naive_iso_t"},
31 1 : {"TIMESTAMP", nullptr, "2026-06-22 10:00:00", true, "timestamp_space"},
32 1 : {"TIMESTAMP", nullptr, "2026-06-22T10:00:00Z", true, "timestamp_z"},
33 1 : {"TIMESTAMP",
34 1 : nullptr,
35 1 : "2026-06-22T10:00:00+00:00",
36 1 : true,
37 1 : "timestamp_offset"},
38 1 : {"TIMESTAMP",
39 1 : nullptr,
40 1 : "2026-06-22T10:00:00.123456",
41 1 : true,
42 1 : "timestamp_fractional"},
43 1 : {"TIMESTAMP", nullptr, "2026-06-22", true, "timestamp_date_only"},
44 1 : {"DATE", nullptr, "2020-06-15", true, "date_iso"},
45 1 : {"DATETIME", nullptr, "2020-06-15 12:30:45", true, "datetime_space"},
46 1 : {"DATETIME", nullptr, "2020-06-15T12:30:45", true, "datetime_iso_t"},
47 1 : {"TIME", nullptr, "12:30:45", true, "time_hms"},
48 1 : {"TIME", nullptr, "12:30:45.123456", true, "time_fractional"},
49 1 : {"NUMERIC", nullptr, "3.14159", true, "numeric"},
50 1 : {"BIGNUMERIC",
51 1 : nullptr,
52 1 : "99999999999999999999999999999.999999999",
53 1 : true,
54 1 : "bignumeric"},
55 1 : {"BOOL", nullptr, "true", true, "bool_true"},
56 1 : {"BOOL", nullptr, "false", true, "bool_false"},
57 1 : {"BYTES", nullptr, "\"SGVsbG8=\"", true, "bytes_base64"},
58 1 : {"ARRAY", "STRING", R"(["a","b"])", true, "array_string"},
59 1 : {"STRUCT", "x:INT64,y:STRING", R"(["1","foo"])", true, "struct_mixed"},
60 1 : };
61 1 : }
62 :
63 1 : std::vector<ParameterMatrixCase> RejectedMatrixCases() {
64 1 : return {
65 1 : {"TIMESTAMP", nullptr, "not-a-timestamp", false, "timestamp_garbage"},
66 1 : {"DATE", nullptr, "2020-13-45", false, "date_invalid"},
67 1 : {"BOOL", nullptr, "yes", false, "bool_garbage"},
68 1 : {"BYTES", nullptr, "\"!!!\"", false, "bytes_invalid_base64"},
69 1 : };
70 1 : }
71 :
72 : class ParameterMatrixTest : public testing::TestWithParam<ParameterMatrixCase> {
73 : };
74 :
75 22 : TEST_P(ParameterMatrixTest, ParseParameterValue) {
76 22 : const ParameterMatrixCase& c = GetParam();
77 22 : absl::string_view type_json = c.type_json != nullptr ? c.type_json : "";
78 22 : auto result = ParseParameterValue(c.value_json, c.type_kind, type_json);
79 22 : if (c.should_succeed) {
80 36 : ASSERT_TRUE(result.ok()) << c.label << ": " << result.status();
81 18 : } else {
82 8 : ASSERT_FALSE(result.ok()) << c.label;
83 4 : }
84 22 : }
85 :
86 : INSTANTIATE_TEST_SUITE_P(
87 : Accepted,
88 : ParameterMatrixTest,
89 : testing::ValuesIn(AcceptedMatrixCases()),
90 : [](const testing::TestParamInfo<ParameterMatrixCase>& info) {
91 : return info.param.label;
92 : });
93 :
94 : INSTANTIATE_TEST_SUITE_P(
95 : Rejected,
96 : ParameterMatrixTest,
97 : testing::ValuesIn(RejectedMatrixCases()),
98 : [](const testing::TestParamInfo<ParameterMatrixCase>& info) {
99 : return info.param.label;
100 : });
101 :
102 1 : TEST(ValueParametersTest, TimestampNaiveIsoDefaultsToUtc) {
103 1 : auto v = ParseParameterValue("2026-06-22T10:00:00", "TIMESTAMP");
104 2 : ASSERT_TRUE(v.ok()) << v.status();
105 1 : EXPECT_EQ(absl::ToUnixSeconds(v->ToTime()), 1782122400);
106 1 : }
107 :
108 1 : TEST(ValueParametersTest, DateRoundTrips) {
109 1 : auto v = ParseParameterValue("2020-06-15", "DATE");
110 2 : ASSERT_TRUE(v.ok()) << v.status();
111 1 : EXPECT_EQ(v->type_kind(), ::googlesql::TYPE_DATE);
112 1 : EXPECT_FALSE(v->is_null());
113 1 : }
114 :
115 1 : TEST(ValueParametersTest, DatetimeSpaceForm) {
116 1 : auto v = ParseParameterValue("2020-06-15 12:30:45", "DATETIME");
117 2 : ASSERT_TRUE(v.ok()) << v.status();
118 1 : EXPECT_EQ(v->type_kind(), ::googlesql::TYPE_DATETIME);
119 1 : }
120 :
121 1 : TEST(ValueParametersTest, TimeFractionalSeconds) {
122 1 : auto v = ParseParameterValue("12:30:45.123456", "TIME");
123 2 : ASSERT_TRUE(v.ok()) << v.status();
124 1 : EXPECT_EQ(v->type_kind(), ::googlesql::TYPE_TIME);
125 1 : }
126 :
127 1 : TEST(ValueParametersTest, BytesBase64RoundTrips) {
128 1 : auto v = ParseParameterValue("\"SGVsbG8=\"", "BYTES");
129 2 : ASSERT_TRUE(v.ok()) << v.status();
130 1 : EXPECT_EQ(v->bytes_value(), "Hello");
131 1 : }
132 :
133 1 : TEST(ValueParametersTest, NumericAndBigNumeric) {
134 1 : auto n = ParseParameterValue("1.5", "NUMERIC");
135 2 : ASSERT_TRUE(n.ok()) << n.status();
136 1 : auto bn = ParseParameterValue("99999999999999999999999999999.999999999",
137 1 : "BIGNUMERIC");
138 2 : ASSERT_TRUE(bn.ok()) << bn.status();
139 1 : }
140 :
141 : } // namespace
142 : } // namespace semantic
143 : } // namespace engine
144 : } // namespace backend
145 : } // namespace bigquery_emulator
|