Skip to content

Commit 62d49e8

Browse files
authored
Merge pull request #1479 from fnc12/feature/window-functions
Add SQLite window functions support
2 parents c461f77 + 4335a5a commit 62d49e8

14 files changed

Lines changed: 2945 additions & 181 deletions

File tree

TODO.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
* blob incremental I/O https://sqlite.org/c3ref/blob_open.html
99
* CREATE VIEW and other view operations https://sqlite.org/lang_createview.html
1010
* query static check for correct order (e.g. `GROUP BY` after `WHERE`)
11-
* `WINDOW`
1211
* `SAVEPOINT` https://www.sqlite.org/lang_savepoint.html
1312
* add `static_assert` in crud `get*` functions in case user passes `where_t` instead of id to make compilation error more clear (example https://github.com/fnc12/sqlite_orm/issues/485)
1413
* named constraints: constraint can have name `CREATE TABLE heroes(id INTEGER CONSTRAINT pk PRIMARY KEY)`

dev/ast/rank.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
#pragma once
22

3+
#ifndef SQLITE_ORM_IMPORT_STD_MODULE
4+
#include <utility> // std::forward
5+
#endif
6+
7+
#include "window.h"
8+
39
namespace sqlite_orm::internal {
4-
struct rank_t {};
10+
struct rank_t {
11+
template<class... OverArgs>
12+
over_t<rank_t, OverArgs...> over(OverArgs... overArgs) {
13+
return {*this, {std::forward<OverArgs>(overArgs)...}};
14+
}
15+
};
516
}
617

718
SQLITE_ORM_EXPORT namespace sqlite_orm {
8-
/**
9-
* [Deprecation notice] This expression factory function is deprecated and will be removed in v1.11.
19+
/**
20+
* RANK() window function / FTS5 rank keyword.
21+
* https://sqlite.org/windowfunctions.html#built-in_window_functions
1022
*/
11-
[[deprecated("Use the hidden FTS5 rank column instead")]]
1223
inline internal::rank_t rank() {
1324
return {};
1425
}

dev/ast/window.h

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#pragma once
2+
3+
#ifndef SQLITE_ORM_IMPORT_STD_MODULE
4+
#include <string> // std::string
5+
#include <tuple> // std::tuple
6+
#include <type_traits> // std::forward, std::move
7+
#include <utility> // std::forward, std::move
8+
#endif
9+
10+
#include "../functional/cxx_type_traits_polyfill.h"
11+
12+
namespace sqlite_orm::internal {
13+
14+
struct unbounded_preceding_t {};
15+
16+
template<class E>
17+
struct preceding_t {
18+
E expression;
19+
};
20+
21+
struct current_row_t {};
22+
23+
template<class E>
24+
struct following_t {
25+
E expression;
26+
};
27+
28+
struct unbounded_following_t {};
29+
30+
enum class frame_type_t { rows, range, groups };
31+
enum class frame_exclude_t { no_others, current_row, group, ties };
32+
33+
template<class Start, class End>
34+
struct frame_spec_t {
35+
frame_type_t type;
36+
Start start;
37+
End end;
38+
frame_exclude_t exclude = frame_exclude_t::no_others;
39+
40+
frame_spec_t exclude_current_row() const {
41+
auto res = *this;
42+
res.exclude = frame_exclude_t::current_row;
43+
return res;
44+
}
45+
46+
frame_spec_t exclude_group() const {
47+
auto res = *this;
48+
res.exclude = frame_exclude_t::group;
49+
return res;
50+
}
51+
52+
frame_spec_t exclude_ties() const {
53+
auto res = *this;
54+
res.exclude = frame_exclude_t::ties;
55+
return res;
56+
}
57+
58+
frame_spec_t exclude_no_others() const {
59+
auto res = *this;
60+
res.exclude = frame_exclude_t::no_others;
61+
return res;
62+
}
63+
};
64+
65+
template<class... Args>
66+
struct partition_by_t {
67+
using arguments_type = std::tuple<Args...>;
68+
arguments_type arguments;
69+
};
70+
71+
template<class T>
72+
inline constexpr bool is_partition_by_v = polyfill::is_specialization_of_v<T, partition_by_t>;
73+
74+
template<class T>
75+
using is_partition_by = polyfill::bool_constant<is_partition_by_v<T>>;
76+
77+
struct window_ref_t {
78+
std::string name;
79+
};
80+
81+
template<class F, class... Args>
82+
struct over_t {
83+
using function_type = F;
84+
using arguments_type = std::tuple<Args...>;
85+
86+
function_type function;
87+
arguments_type arguments;
88+
};
89+
90+
template<class T>
91+
inline constexpr bool is_over_v = polyfill::is_specialization_of_v<T, over_t>;
92+
93+
template<class T>
94+
using is_over = polyfill::bool_constant<is_over_v<T>>;
95+
96+
template<class... Args>
97+
struct window_defn_t {
98+
std::string name;
99+
using arguments_type = std::tuple<Args...>;
100+
arguments_type arguments;
101+
};
102+
103+
template<class T>
104+
inline constexpr bool is_window_defn_v = polyfill::is_specialization_of_v<T, window_defn_t>;
105+
106+
template<class T>
107+
using is_window_defn = polyfill::bool_constant<is_window_defn_v<T>>;
108+
}
109+
110+
SQLITE_ORM_EXPORT namespace sqlite_orm {
111+
112+
/**
113+
* UNBOUNDED PRECEDING frame boundary.
114+
* https://sqlite.org/windowfunctions.html
115+
*/
116+
inline internal::unbounded_preceding_t unbounded_preceding() {
117+
return {};
118+
}
119+
120+
/**
121+
* expr PRECEDING frame boundary.
122+
* https://sqlite.org/windowfunctions.html
123+
*/
124+
template<class E>
125+
internal::preceding_t<E> preceding(E expression) {
126+
return {std::move(expression)};
127+
}
128+
129+
/**
130+
* CURRENT ROW frame boundary.
131+
* https://sqlite.org/windowfunctions.html
132+
*/
133+
inline internal::current_row_t current_row() {
134+
return {};
135+
}
136+
137+
/**
138+
* expr FOLLOWING frame boundary.
139+
* https://sqlite.org/windowfunctions.html
140+
*/
141+
template<class E>
142+
internal::following_t<E> following(E expression) {
143+
return {std::move(expression)};
144+
}
145+
146+
/**
147+
* UNBOUNDED FOLLOWING frame boundary.
148+
* https://sqlite.org/windowfunctions.html
149+
*/
150+
inline internal::unbounded_following_t unbounded_following() {
151+
return {};
152+
}
153+
154+
/**
155+
* ROWS BETWEEN start AND end frame specification.
156+
* Example: rows(unbounded_preceding(), current_row())
157+
*/
158+
template<class Start, class End>
159+
internal::frame_spec_t<Start, End> rows(Start start, End end) {
160+
return {internal::frame_type_t::rows, std::move(start), std::move(end)};
161+
}
162+
163+
/**
164+
* RANGE BETWEEN start AND end frame specification.
165+
* Example: range(current_row(), unbounded_following())
166+
*/
167+
template<class Start, class End>
168+
internal::frame_spec_t<Start, End> range(Start start, End end) {
169+
return {internal::frame_type_t::range, std::move(start), std::move(end)};
170+
}
171+
172+
/**
173+
* GROUPS BETWEEN start AND end frame specification.
174+
* Example: groups(unbounded_preceding(), current_row())
175+
*/
176+
template<class Start, class End>
177+
internal::frame_spec_t<Start, End> groups(Start start, End end) {
178+
return {internal::frame_type_t::groups, std::move(start), std::move(end)};
179+
}
180+
181+
/**
182+
* PARTITION BY expression list for window functions.
183+
* Example: partition_by(&Employee::departmentId)
184+
*/
185+
template<class... Args>
186+
internal::partition_by_t<Args...> partition_by(Args... args) {
187+
return {{std::forward<Args>(args)...}};
188+
}
189+
190+
/**
191+
* Reference to a named window definition (OVER window_name).
192+
* Example: row_number().over(window_ref("win"))
193+
*/
194+
inline internal::window_ref_t window_ref(std::string name) {
195+
return {std::move(name)};
196+
}
197+
198+
/**
199+
* Named window definition (WINDOW name AS (...)).
200+
* Passed as a condition to select().
201+
* Example: window("win", order_by(&Employee::salary))
202+
*/
203+
template<class... Args>
204+
internal::window_defn_t<Args...> window(std::string name, Args... args) {
205+
return {std::move(name), {std::forward<Args>(args)...}};
206+
}
207+
}

dev/ast_iterator.h

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "ast/between.h"
3232
#include "ast/is_null.h"
3333
#include "ast/is_not_null.h"
34+
#include "ast/window.h"
35+
#include "window_functions.h"
3436

3537
namespace sqlite_orm::internal {
3638
/**
@@ -768,4 +770,126 @@ namespace sqlite_orm::internal {
768770
iterate_ast(node.expression, lambda);
769771
}
770772
};
773+
774+
template<class E>
775+
struct ast_iterator<preceding_t<E>, void> {
776+
using node_type = preceding_t<E>;
777+
778+
template<class L>
779+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
780+
iterate_ast(node.expression, lambda);
781+
}
782+
};
783+
784+
template<class E>
785+
struct ast_iterator<following_t<E>, void> {
786+
using node_type = following_t<E>;
787+
788+
template<class L>
789+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
790+
iterate_ast(node.expression, lambda);
791+
}
792+
};
793+
794+
template<class Start, class End>
795+
struct ast_iterator<frame_spec_t<Start, End>, void> {
796+
using node_type = frame_spec_t<Start, End>;
797+
798+
template<class L>
799+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
800+
iterate_ast(node.start, lambda);
801+
iterate_ast(node.end, lambda);
802+
}
803+
};
804+
805+
template<class... Args>
806+
struct ast_iterator<partition_by_t<Args...>, void> {
807+
using node_type = partition_by_t<Args...>;
808+
809+
template<class L>
810+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
811+
iterate_ast(node.arguments, lambda);
812+
}
813+
};
814+
815+
template<class F, class... Args>
816+
struct ast_iterator<over_t<F, Args...>, void> {
817+
using node_type = over_t<F, Args...>;
818+
819+
template<class L>
820+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
821+
iterate_ast(node.function, lambda);
822+
iterate_ast(node.arguments, lambda);
823+
}
824+
};
825+
826+
template<class... Args>
827+
struct ast_iterator<window_defn_t<Args...>, void> {
828+
using node_type = window_defn_t<Args...>;
829+
830+
template<class L>
831+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
832+
iterate_ast(node.arguments, lambda);
833+
}
834+
};
835+
836+
template<class... Args>
837+
struct ast_iterator<ntile_t<Args...>, void> {
838+
using node_type = ntile_t<Args...>;
839+
840+
template<class L>
841+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
842+
iterate_ast(node.args, lambda);
843+
}
844+
};
845+
846+
template<class... Args>
847+
struct ast_iterator<lag_t<Args...>, void> {
848+
using node_type = lag_t<Args...>;
849+
850+
template<class L>
851+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
852+
iterate_ast(node.args, lambda);
853+
}
854+
};
855+
856+
template<class... Args>
857+
struct ast_iterator<lead_t<Args...>, void> {
858+
using node_type = lead_t<Args...>;
859+
860+
template<class L>
861+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
862+
iterate_ast(node.args, lambda);
863+
}
864+
};
865+
866+
template<class... Args>
867+
struct ast_iterator<first_value_t<Args...>, void> {
868+
using node_type = first_value_t<Args...>;
869+
870+
template<class L>
871+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
872+
iterate_ast(node.args, lambda);
873+
}
874+
};
875+
876+
template<class... Args>
877+
struct ast_iterator<last_value_t<Args...>, void> {
878+
using node_type = last_value_t<Args...>;
879+
880+
template<class L>
881+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
882+
iterate_ast(node.args, lambda);
883+
}
884+
};
885+
886+
template<class... Args>
887+
struct ast_iterator<nth_value_t<Args...>, void> {
888+
using node_type = nth_value_t<Args...>;
889+
890+
template<class L>
891+
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& node, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
892+
iterate_ast(node.args, lambda);
893+
}
894+
};
771895
}

0 commit comments

Comments
 (0)