odatav4_parser/renderers/
postgresql.rs1use crate::ast::QueryOptions;
2use crate::renderers::{filter::FilterRenderer, SqlRenderer};
3
4pub struct PostgresqlRenderer;
6
7impl PostgresqlRenderer {
8 pub fn new() -> Self {
9 Self
10 }
11}
12
13impl Default for PostgresqlRenderer {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl FilterRenderer for PostgresqlRenderer {
20 fn quote_identifier(&self, ident: &str) -> String {
21 format!("\"{}\"", ident)
22 }
23}
24
25impl SqlRenderer for PostgresqlRenderer {
26 fn render(&self, table_name: &str, options: &QueryOptions) -> String {
27 let mut parts = Vec::new();
28
29 parts.push("SELECT".to_string());
31
32 if let Some(ref fields) = options.select {
34 let field_list = fields
35 .iter()
36 .map(|f| self.quote_identifier(f))
37 .collect::<Vec<_>>()
38 .join(", ");
39 parts.push(field_list);
40 } else {
41 parts.push("*".to_string());
42 }
43
44 parts.push("FROM".to_string());
46 parts.push(self.quote_identifier(table_name));
47
48 if let Some(ref expand_fields) = options.expand {
50 let expand_list = expand_fields
51 .iter()
52 .map(|item| item.field.clone())
53 .collect::<Vec<_>>()
54 .join(", ");
55 parts.push(format!(
56 "/* TODO: JOIN {} */",
57 expand_list
58 ));
59 }
60
61 if let Some(ref filter) = options.filter {
63 parts.push("WHERE".to_string());
64 parts.push(self.render_filter(filter));
65 }
66
67 if let Some(ref groupby_fields) = options.groupby {
69 let groupby_list = groupby_fields
70 .iter()
71 .map(|f| self.quote_identifier(f))
72 .collect::<Vec<_>>()
73 .join(", ");
74 parts.push(format!("GROUP BY {}", groupby_list));
75 }
76
77 if let Some(ref orderby_items) = options.orderby {
79 let orderby_list = orderby_items
80 .iter()
81 .map(|item| {
82 let dir = match item.direction {
83 crate::ast::SortDirection::Asc => "ASC",
84 crate::ast::SortDirection::Desc => "DESC",
85 };
86 format!("{} {}", self.quote_identifier(&item.field), dir)
87 })
88 .collect::<Vec<_>>()
89 .join(", ");
90 parts.push(format!("ORDER BY {}", orderby_list));
91 }
92
93 if let Some(top) = options.top {
95 parts.push(format!("LIMIT {}", top));
96 }
97
98 if let Some(skip) = options.skip {
100 parts.push(format!("OFFSET {}", skip));
101 }
102
103 parts.join(" ")
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_postgresql_select_only() {
113 let renderer = PostgresqlRenderer::new();
114 let mut options = QueryOptions::new();
115 options.select = Some(vec!["id".to_string(), "name".to_string()]);
116
117 let sql = renderer.render("users", &options);
118 assert_eq!(sql, "SELECT \"id\", \"name\" FROM \"users\"");
119 }
120
121 #[test]
122 fn test_postgresql_limit() {
123 let renderer = PostgresqlRenderer::new();
124 let mut options = QueryOptions::new();
125 options.top = Some(10);
126
127 let sql = renderer.render("users", &options);
128 assert_eq!(sql, "SELECT * FROM \"users\" LIMIT 10");
129 }
130
131 #[test]
132 fn test_postgresql_offset() {
133 let renderer = PostgresqlRenderer::new();
134 let mut options = QueryOptions::new();
135 options.skip = Some(20);
136
137 let sql = renderer.render("users", &options);
138 assert_eq!(sql, "SELECT * FROM \"users\" OFFSET 20");
139 }
140
141 #[test]
142 fn test_postgresql_combined() {
143 let renderer = PostgresqlRenderer::new();
144 let mut options = QueryOptions::new();
145 options.select = Some(vec!["id".to_string(), "name".to_string()]);
146 options.top = Some(10);
147 options.skip = Some(20);
148
149 let sql = renderer.render("users", &options);
150 assert_eq!(sql, "SELECT \"id\", \"name\" FROM \"users\" LIMIT 10 OFFSET 20");
151 }
152}