1use std::fmt::Display;
2
3use chrono::{DateTime, Utc};
4use sqlx::{postgres::PgArguments, Database, Encode, Executor, Postgres, Type};
5use uuid::Uuid;
6
7pub struct Driver {
8 prql: String,
9 arguments: PgArguments,
10}
11
12impl Driver {
13 pub fn new() -> Self {
14 Driver {
15 prql: String::new(),
16 arguments: PgArguments::default(),
17 }
18 }
19
20 pub fn prql(&self) -> &str {
21 &self.prql
22 }
23
24 pub fn sql(&self) -> String {
25 use prqlc::{sql::Dialect, Options, Target};
26
27 let opts = &Options {
28 format: false,
29 signature_comment: false,
30 target: Target::Sql(Some(Dialect::Postgres)),
31 ..Default::default()
32 };
33
34 match prqlc::compile(&self.prql, opts) {
35 Ok(sql) => {
36 tracing::debug!("compiling prql:\n{}\ninto sql:\n{}", &self.prql, &sql);
37 sql
38 }
39 Err(e) => {
40 tracing::error!("bad prql:\n{}", &self.prql);
41 panic!("must compile prql: {}", e)
42 }
43 }
44 }
45
46 pub fn is_empty(&self) -> bool {
47 self.prql.is_empty()
48 }
49
50 pub fn push(&mut self, prql: impl Display) {
51 use std::fmt::Write as _;
52
53 write!(&mut self.prql, "{}", prql).expect("must write pqrl");
54 }
55
56 pub fn push_bind<T>(&mut self, value: T)
57 where
58 for<'q> T: Encode<'q, Postgres> + Send + Type<Postgres>,
59 {
60 use sqlx::Arguments as _;
61
62 self.arguments.add(value);
63 self.arguments
64 .format_placeholder(&mut self.prql)
65 .expect("must format placeholder");
66 }
67
68 pub async fn execute_without_compilation<'c>(
69 self,
70 executor: impl Executor<'c, Database = Postgres>,
71 ) -> sqlx::Result<<Postgres as Database>::QueryResult> {
72 use sqlx::QueryBuilder;
73
74 QueryBuilder::with_arguments(self.prql, self.arguments)
75 .build()
76 .execute(executor)
77 .await
78 }
79
80 pub async fn fetch_all(
81 self,
82 executor: impl Executor<'_, Database = Postgres>,
83 ) -> sqlx::Result<Vec<<Postgres as Database>::Row>> {
84 use sqlx::QueryBuilder;
85
86 QueryBuilder::with_arguments(self.sql(), self.arguments)
87 .build()
88 .fetch_all(executor)
89 .await
90 }
91
92 pub async fn fetch_one(
93 self,
94 executor: impl Executor<'_, Database = Postgres>,
95 ) -> sqlx::Result<<Postgres as Database>::Row> {
96 use sqlx::QueryBuilder;
97
98 QueryBuilder::with_arguments(self.sql(), self.arguments)
99 .build()
100 .fetch_one(executor)
101 .await
102 }
103
104 pub async fn fetch_optional(
105 self,
106 executor: impl Executor<'_, Database = Postgres>,
107 ) -> sqlx::Result<Option<<Postgres as Database>::Row>> {
108 use sqlx::QueryBuilder;
109
110 QueryBuilder::with_arguments(self.sql(), self.arguments)
111 .build()
112 .fetch_optional(executor)
113 .await
114 }
115}
116
117impl Default for Driver {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123pub trait PushPrql {
124 fn push_to_driver(&self, driver: &mut Driver);
125}
126
127impl PushPrql for String {
128 fn push_to_driver(&self, driver: &mut Driver) {
129 driver.push_bind(self);
130 }
131}
132
133impl PushPrql for &str {
134 fn push_to_driver(&self, driver: &mut Driver) {
135 driver.push_bind(self);
136 }
137}
138
139impl PushPrql for i32 {
140 fn push_to_driver(&self, driver: &mut Driver) {
141 driver.push_bind(self);
142 }
143}
144
145impl PushPrql for i64 {
146 fn push_to_driver(&self, driver: &mut Driver) {
147 driver.push_bind(self);
148 }
149}
150
151impl PushPrql for u32 {
152 fn push_to_driver(&self, driver: &mut Driver) {
153 driver.push_bind(*self as i32);
154 }
155}
156
157impl PushPrql for u64 {
158 fn push_to_driver(&self, driver: &mut Driver) {
159 driver.push_bind(*self as i64);
160 }
161}
162
163impl PushPrql for bool {
164 fn push_to_driver(&self, driver: &mut Driver) {
165 driver.push_bind(self);
166 }
167}
168
169impl PushPrql for Uuid {
170 fn push_to_driver(&self, driver: &mut Driver) {
171 driver.push_bind(self);
172 }
173}
174
175impl PushPrql for DateTime<Utc> {
176 fn push_to_driver(&self, driver: &mut Driver) {
177 driver.push_bind(self);
178 }
179}
180
181impl<T> PushPrql for Option<T>
182where
183 for<'q> T: 'q + Encode<'q, Postgres> + Sync + Type<Postgres>,
184{
185 fn push_to_driver(&self, driver: &mut Driver) {
186 driver.push_bind(self);
187 }
188}
189
190impl<T> PushPrql for Vec<T>
191where
192 T: PushPrql,
193{
194 fn push_to_driver(&self, driver: &mut Driver) {
195 for (i, value) in self.iter().enumerate() {
196 if i > 0 {
197 driver.push(", ");
198 }
199 value.push_to_driver(driver);
200 }
201 }
202}
203
204impl<T> PushPrql for &T
205where
206 T: PushPrql,
207{
208 fn push_to_driver(&self, driver: &mut Driver) {
209 (*self).push_to_driver(driver);
210 }
211}
212
213impl PushPrql for &dyn PushPrql {
214 fn push_to_driver(&self, driver: &mut Driver) {
215 (*self).push_to_driver(driver)
216 }
217}
218
219pub fn sql(sql: &'static str) -> SQL {
220 SQL { sql }
221}
222
223pub struct SQL {
224 pub sql: &'static str,
225}
226
227impl PushPrql for SQL {
228 fn push_to_driver(&self, driver: &mut crate::driver::Driver) {
229 driver.push("s\"");
230 driver.push(self.sql);
231 driver.push('\"');
232 }
233}