SQLon test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 57 / 0 / 57
Functions: 100.0% 12 / 0 / 12
Branches: 89.6% 69 / 0 / 77

src/expression.cpp
Line Branch Exec Source
1 /******************************************************************************
2 **
3 ** Copyright (C) 2026 Ivan Pinezhaninov <ivan.pinezhaninov@gmail.com>
4 **
5 ** This file is part of the SQLon - which can be found at
6 ** https://github.com/IvanPinezhaninov/sqlon/.
7 **
8 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
12 ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
13 ** OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
14 ** THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 **
16 ******************************************************************************/
17
18 #include <sqlon/expression.h>
19
20 #include <atomic>
21 #include <cctype>
22 #include <memory>
23 #include <utility>
24
25 namespace sqlon {
26 namespace detail {
27
28 219 detail::expression_ptr make_expression_node(detail::expression_kind kind, std::string text,
29 std::vector<detail::expression_ptr> operands)
30 {
31
1/1
✓ Branch 1 taken 219 times.
219 std::shared_ptr<detail::expression_node> node = std::make_shared<detail::expression_node>();
32 219 node->kind = kind;
33 219 node->text = std::move(text);
34 219 node->operands = std::move(operands);
35 438 return node;
36 219 }
37
38 125 detail::expression_ptr make_binary_node(std::string operation, detail::expression_ptr left,
39 detail::expression_ptr right)
40 {
41 375 return make_expression_node(detail::expression_kind::binary, std::move(operation),
42
4/6
✓ Branch 1 taken 125 times.
✓ Branch 5 taken 125 times.
✓ Branch 9 taken 250 times.
✓ Branch 10 taken 125 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
625 {std::move(left), std::move(right)});
43 125 }
44
45 23 detail::expression_ptr make_raw_expression_node(std::string sql, std::vector<detail::expression_ptr> operands)
46 {
47
3/3
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 22 times.
✓ Branch 5 taken 1 time.
23 if (sql.empty()) throw invalid_query{"a raw expression template cannot be empty"};
48
49 22 std::size_t positions = 0;
50
2/2
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 18 times.
186 for (std::size_t index = 0; index < sql.size(); ++index) {
51
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 134 times.
168 if (sql[index] == '{') {
52
6/6
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 31 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 31 times.
34 if (index + 1 >= sql.size() || sql[index + 1] != '}')
53
1/1
✓ Branch 2 taken 3 times.
3 throw invalid_query{"a raw expression template contains malformed syntax"};
54 31 ++positions;
55 31 ++index;
56
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 133 times.
134 } else if (sql[index] == '}') {
57
1/1
✓ Branch 2 taken 1 time.
1 throw invalid_query{"a raw expression template contains malformed syntax"};
58 }
59 }
60
61
6/6
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 15 times.
18 if (positions == 0 || operands.empty())
62
1/1
✓ Branch 2 taken 3 times.
3 throw invalid_query{"a raw expression requires at least one {} position and operand; use raw_sql otherwise"};
63
3/3
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 13 times.
✓ Branch 5 taken 2 times.
15 if (positions != operands.size()) throw invalid_query{"a raw expression requires one operand for every {} position"};
64
65
1/1
✓ Branch 5 taken 13 times.
13 return make_expression_node(detail::expression_kind::raw_expression, std::move(sql), std::move(operands));
66 }
67
68 30 std::size_t next_parameter_identity()
69 {
70 static std::atomic_size_t next_identity{1};
71 30 return next_identity.fetch_add(1, std::memory_order_relaxed);
72 }
73
74 32 bool valid_function_name(std::string_view name) noexcept
75 {
76
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
32 if (name.empty()) return false;
77 32 bool segment_start = true;
78
2/2
✓ Branch 2 taken 191 times.
✓ Branch 3 taken 31 times.
222 for (const char character : name) {
79 191 const unsigned char byte = static_cast<unsigned char>(character);
80
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 157 times.
191 if (segment_start) {
81
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
34 if (std::isalpha(byte) == 0 && character != '_') return false;
82 34 segment_start = false;
83 34 continue;
84 }
85
86
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 155 times.
157 if (character == '.') {
87 2 segment_start = true;
88 2 continue;
89 }
90
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 148 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 6 times.
155 if (std::isalnum(byte) == 0 && character != '_') return false;
91 }
92 31 return !segment_start;
93 }
94
95 } // namespace detail
96
97 14 expression<bool> operator&&(const expression<bool>& left, const expression<bool>& right)
98 {
99 return detail::expression_access::make<bool>(
100
3/3
✓ Branch 5 taken 14 times.
✓ Branch 8 taken 14 times.
✓ Branch 11 taken 14 times.
42 detail::make_binary_node("and", detail::expression_access::node(left), detail::expression_access::node(right)));
101 }
102
103 6 expression<bool> operator||(const expression<bool>& left, const expression<bool>& right)
104 {
105 return detail::expression_access::make<bool>(
106
3/3
✓ Branch 5 taken 6 times.
✓ Branch 8 taken 6 times.
✓ Branch 11 taken 6 times.
18 detail::make_binary_node("or", detail::expression_access::node(left), detail::expression_access::node(right)));
107 }
108
109 2 expression<bool> operator!(const expression<bool>& value)
110 {
111 return detail::expression_access::make<bool>(
112
6/8
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 15 taken 2 times.
✓ Branch 16 taken 2 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
10 detail::make_expression_node(detail::expression_kind::unary, "not", {detail::expression_access::node(value)}));
113 2 }
114
115 1 expression<std::int64_t> count_all()
116 {
117 return detail::expression_access::make<std::int64_t>(
118
3/3
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 8 taken 1 time.
3 detail::make_expression_node(detail::expression_kind::function, "count_all"));
119 }
120
121 2 expression<timestamp> current_timestamp()
122 {
123 return detail::expression_access::make<timestamp>(
124
3/3
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
6 detail::make_expression_node(detail::expression_kind::keyword, "current_timestamp"));
125 }
126
127 3 expression<std::nullptr_t> null()
128 {
129 return detail::expression_access::make<std::nullptr_t>(
130
3/3
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
9 detail::make_expression_node(detail::expression_kind::keyword, "null"));
131 }
132
133 3 expression<std::nullptr_t> default_value()
134 {
135 return detail::expression_access::make<std::nullptr_t>(
136
3/3
✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
9 detail::make_expression_node(detail::expression_kind::keyword, "default"));
137 }
138
139 } // namespace sqlon
140