SQLon test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 66 / 0 / 66
Functions: 100.0% 22 / 0 / 22
Branches: 97.4% 37 / 0 / 38

src/window.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/window.h>
19
20 #include <sqlon/error.h>
21
22 #include <optional>
23
24 namespace sqlon {
25
26 4 window_frame_bound unbounded_preceding()
27 {
28 4 return detail::window_access::make_bound({detail::window_frame_bound_kind::unbounded_preceding, 0});
29 }
30
31 7 window_frame_bound preceding(std::uint64_t offset)
32 {
33 7 return detail::window_access::make_bound({detail::window_frame_bound_kind::preceding, offset});
34 }
35
36 8 window_frame_bound current_row()
37 {
38 8 return detail::window_access::make_bound({detail::window_frame_bound_kind::current_row, 0});
39 }
40
41 6 window_frame_bound following(std::uint64_t offset)
42 {
43 6 return detail::window_access::make_bound({detail::window_frame_bound_kind::following, offset});
44 }
45
46 3 window_frame_bound unbounded_following()
47 {
48 3 return detail::window_access::make_bound({detail::window_frame_bound_kind::unbounded_following, 0});
49 }
50
51 18 window_frame_builder::window_frame_builder(detail::window_frame_mode mode)
52 18 : m_mode(mode)
53 18 {}
54
55 10 window_frame window_frame_builder::between(window_frame_bound start, window_frame_bound end) const
56 {
57 10 const detail::window_frame_bound_node& startNode = detail::window_access::node(start);
58 10 const detail::window_frame_bound_node& endNode = detail::window_access::node(end);
59
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 9 times.
10 if (startNode.kind == detail::window_frame_bound_kind::unbounded_following)
60
1/1
✓ Branch 2 taken 1 time.
1 throw invalid_query{"a window frame cannot start with UNBOUNDED FOLLOWING"};
61
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
9 if (endNode.kind == detail::window_frame_bound_kind::unbounded_preceding)
62
1/1
✓ Branch 2 taken 1 time.
1 throw invalid_query{"a window frame cannot end with UNBOUNDED PRECEDING"};
63
2/2
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 7 times.
8 if (bound_rank(endNode.kind) < bound_rank(startNode.kind))
64
1/1
✓ Branch 2 taken 1 time.
1 throw invalid_query{"a window frame cannot end before it starts"};
65
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if (startNode.kind == detail::window_frame_bound_kind::preceding &&
66
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
3 endNode.kind == detail::window_frame_bound_kind::preceding && endNode.offset > startNode.offset) {
67
1/1
✓ Branch 2 taken 1 time.
1 throw invalid_query{"a PRECEDING window frame cannot end before it starts"};
68 }
69
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (startNode.kind == detail::window_frame_bound_kind::following &&
70
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
2 endNode.kind == detail::window_frame_bound_kind::following && endNode.offset < startNode.offset) {
71
1/1
✓ Branch 2 taken 1 time.
1 throw invalid_query{"a FOLLOWING window frame cannot end before it starts"};
72 }
73 5 return detail::window_access::make_frame({m_mode, startNode, endNode});
74 }
75
76 8 window_frame window_frame_builder::bound(window_frame_bound value) const
77 {
78 8 const detail::window_frame_bound_node& node = detail::window_access::node(value);
79
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 7 times.
8 if (node.kind == detail::window_frame_bound_kind::unbounded_following)
80
1/1
✓ Branch 2 taken 1 time.
1 throw invalid_query{"a window frame cannot start with UNBOUNDED FOLLOWING"};
81
2/2
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 6 times.
7 if (bound_rank(node.kind) > bound_rank(detail::window_frame_bound_kind::current_row))
82
1/1
✓ Branch 2 taken 1 time.
1 throw invalid_query{"a single-bound window frame cannot start after CURRENT ROW"};
83 6 return detail::window_access::make_frame({m_mode, node, std::nullopt});
84 }
85
86 1 window_frame window_frame_builder::unbounded_preceding() const
87 {
88 1 return bound(sqlon::unbounded_preceding());
89 }
90
91 1 window_frame window_frame_builder::preceding(std::uint64_t offset) const
92 {
93 1 return bound(sqlon::preceding(offset));
94 }
95
96 4 window_frame window_frame_builder::current_row() const
97 {
98 4 return bound(sqlon::current_row());
99 }
100
101 1 window_frame window_frame_builder::following(std::uint64_t offset) const
102 {
103 1 return bound(sqlon::following(offset));
104 }
105
106 30 int window_frame_builder::bound_rank(detail::window_frame_bound_kind kind) noexcept
107 {
108 30 return static_cast<int>(kind);
109 }
110
111 15 window_frame_builder rows()
112 {
113 15 return detail::window_access::make_builder(detail::window_frame_mode::rows);
114 }
115
116 1 window_frame_builder range()
117 {
118 1 return detail::window_access::make_builder(detail::window_frame_mode::range);
119 }
120
121 2 window_frame_builder groups()
122 {
123 2 return detail::window_access::make_builder(detail::window_frame_mode::groups);
124 }
125
126 13 window_spec::window_spec() = default;
127
128 9 window_spec window_spec::frame(window_frame value) const
129 {
130
3/3
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 8 times.
✓ Branch 5 taken 1 time.
9 if (m_node.frame) throw invalid_query{"a window frame was already specified"};
131 8 window_spec copy = *this;
132 8 copy.m_node.frame = detail::window_access::node(value);
133 8 return copy;
134 }
135
136 13 window_spec window()
137 {
138 13 return {};
139 }
140
141 2 expression<std::int64_t> row_number()
142 {
143
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
4 return function<std::int64_t>("row_number");
144 }
145
146 7 expression<std::int64_t> rank()
147 {
148
2/2
✓ Branch 1 taken 7 times.
✓ Branch 4 taken 7 times.
14 return function<std::int64_t>("rank");
149 }
150
151 1 expression<std::int64_t> dense_rank()
152 {
153
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
2 return function<std::int64_t>("dense_rank");
154 }
155
156 } // namespace sqlon
157