keelson_core/clause/
select.rs1use crate::expr::{Expr, IntoExpr, IntoExprList};
2use crate::writer::{Expression, SqlWriter};
3
4#[derive(Debug, Clone, Default)]
16pub struct SelectList {
17 pub columns: Vec<Expr>,
19 pub preload_columns: Vec<Expr>,
21}
22
23impl SelectList {
24 pub fn count_select_cols(&self) -> usize {
26 self.columns.len()
27 }
28
29 pub fn set_select(&mut self, columns: impl IntoExprList) {
31 self.columns = columns.into_expr_list();
32 }
33
34 pub fn append_select(&mut self, columns: impl IntoExprList) {
36 self.columns.extend(columns.into_expr_list());
37 }
38
39 pub fn set_preload_select(&mut self, columns: impl IntoExprList) {
41 self.preload_columns = columns.into_expr_list();
42 }
43
44 pub fn append_preload_select(&mut self, columns: impl IntoExprList) {
46 self.preload_columns.extend(columns.into_expr_list());
47 }
48
49 pub fn append_column(&mut self, column: impl IntoExpr) {
51 self.columns.push(column.into_expr());
52 }
53
54 pub fn is_empty(&self) -> bool {
56 self.columns.is_empty() && self.preload_columns.is_empty()
57 }
58}
59
60impl Expression for SelectList {
61 fn write_sql(&self, w: &mut SqlWriter<'_>) {
62 if self.is_empty() {
63 w.push_str("*");
64 return;
65 }
66 w.write_iter(
67 self.columns.iter().chain(self.preload_columns.iter()),
68 "",
69 ", ",
70 "",
71 );
72 }
73}
74
75pub trait HasSelectList {
77 fn select_list_mut(&mut self) -> &mut SelectList;
79}
80
81impl HasSelectList for SelectList {
82 fn select_list_mut(&mut self) -> &mut SelectList {
83 self
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use keelson_sqlcheck::testing::assert_frag_sql;
90
91 use super::*;
92 use crate::dialect::testing::Numbered;
93 use crate::expr::{Chain, quote};
94 use crate::writer::build;
95
96 fn sql(list: &SelectList) -> String {
97 build(&Numbered, list).expect("render").0
98 }
99
100 #[test]
101 fn an_empty_projection_is_a_star() {
102 assert_frag_sql("SELECT {} FROM users", &sql(&SelectList::default()), "*");
103 assert!(SelectList::default().is_empty());
104 }
105
106 #[test]
107 fn preloads_render_after_the_selected_columns() {
108 let mut list = SelectList::default();
109 list.append_select((quote("age"), quote("name")));
110 list.append_preload_select(quote(("posts", "id")));
111
112 assert_frag_sql(
115 "SELECT {} FROM users, posts",
116 &sql(&list),
117 r#""age", "name", "posts"."id""#,
118 );
119 assert_eq!(
120 list.count_select_cols(),
121 2,
122 "a preload column is not part of the root projection"
123 );
124 }
125
126 #[test]
127 fn preloads_alone_still_suppress_the_star() {
128 let mut list = SelectList::default();
129 list.set_preload_select(quote(("posts", "id")));
130 assert_frag_sql("SELECT {} FROM posts", &sql(&list), r#""posts"."id""#);
131 assert!(!list.is_empty());
132 }
133
134 #[test]
135 fn a_column_can_be_any_expression() {
136 let mut list = SelectList::default();
137 list.append_column(quote("id"));
138 list.append_column(Expr::func("count", "*").as_("n"));
139 list.append_column("1 + 1");
140 assert_frag_sql(
143 r#"SELECT {} FROM users GROUP BY "id""#,
144 &sql(&list),
145 r#""id", count(*) AS "n", 1 + 1"#,
146 );
147 }
148}