Skip to main content

qail_core/ast/cmd/
constructors.rs

1//! Static constructor methods for Qail.
2//!
3//! Methods like get(), set(), add(), del(), make(), etc.
4
5use crate::ast::{Action, Qail};
6
7impl Qail {
8    /// SELECT — query rows.
9    pub fn get(table: impl Into<String>) -> Self {
10        Self {
11            action: Action::Get,
12            table: table.into(),
13            ..Default::default()
14        }
15    }
16
17    /// UPDATE — modify rows.
18    pub fn set(table: impl Into<String>) -> Self {
19        Self {
20            action: Action::Set,
21            table: table.into(),
22            ..Default::default()
23        }
24    }
25
26    /// DELETE — remove rows.
27    pub fn del(table: impl Into<String>) -> Self {
28        Self {
29            action: Action::Del,
30            table: table.into(),
31            ..Default::default()
32        }
33    }
34
35    /// INSERT — add rows.
36    pub fn add(table: impl Into<String>) -> Self {
37        Self {
38            action: Action::Add,
39            table: table.into(),
40            ..Default::default()
41        }
42    }
43
44    /// UPSERT — insert or update.
45    pub fn put(table: impl Into<String>) -> Self {
46        Self {
47            action: Action::Put,
48            table: table.into(),
49            ..Default::default()
50        }
51    }
52
53    /// MERGE — conditionally insert, update, delete, or do nothing.
54    pub fn merge_into(table: impl Into<String>) -> Self {
55        Self {
56            action: Action::Merge,
57            table: table.into(),
58            ..Default::default()
59        }
60    }
61
62    /// COPY … TO — export data.
63    pub fn export(table: impl Into<String>) -> Self {
64        Self {
65            action: Action::Export,
66            table: table.into(),
67            ..Default::default()
68        }
69    }
70
71    /// CREATE TABLE.
72    pub fn make(table: impl Into<String>) -> Self {
73        Self {
74            action: Action::Make,
75            table: table.into(),
76            ..Default::default()
77        }
78    }
79
80    /// TRUNCATE — empty a table.
81    pub fn truncate(table: impl Into<String>) -> Self {
82        Self {
83            action: Action::Truncate,
84            table: table.into(),
85            ..Default::default()
86        }
87    }
88
89    /// EXPLAIN — show query plan.
90    pub fn explain(table: impl Into<String>) -> Self {
91        Self {
92            action: Action::Explain,
93            table: table.into(),
94            ..Default::default()
95        }
96    }
97
98    /// EXPLAIN ANALYZE — show query plan with execution stats.
99    pub fn explain_analyze(table: impl Into<String>) -> Self {
100        Self {
101            action: Action::ExplainAnalyze,
102            table: table.into(),
103            ..Default::default()
104        }
105    }
106
107    /// LOCK TABLE.
108    pub fn lock(table: impl Into<String>) -> Self {
109        Self {
110            action: Action::Lock,
111            table: table.into(),
112            ..Default::default()
113        }
114    }
115
116    /// CREATE MATERIALIZED VIEW.
117    pub fn create_materialized_view(name: impl Into<String>, query: Qail) -> Self {
118        Self {
119            action: Action::CreateMaterializedView,
120            table: name.into(),
121            source_query: Some(Box::new(query)),
122            ..Default::default()
123        }
124    }
125
126    /// REFRESH MATERIALIZED VIEW.
127    pub fn refresh_materialized_view(name: impl Into<String>) -> Self {
128        Self {
129            action: Action::RefreshMaterializedView,
130            table: name.into(),
131            ..Default::default()
132        }
133    }
134
135    /// DROP MATERIALIZED VIEW.
136    pub fn drop_materialized_view(name: impl Into<String>) -> Self {
137        Self {
138            action: Action::DropMaterializedView,
139            table: name.into(),
140            ..Default::default()
141        }
142    }
143
144    // PostgreSQL Pub/Sub (LISTEN/NOTIFY)
145
146    /// Create a LISTEN command to subscribe to a channel.
147    ///
148    /// # Example
149    /// ```ignore
150    /// let cmd = Qail::listen("orders");
151    /// // Generates: LISTEN orders
152    /// ```
153    pub fn listen(channel: impl Into<String>) -> Self {
154        Self {
155            action: Action::Listen,
156            channel: Some(channel.into()),
157            ..Default::default()
158        }
159    }
160
161    /// Create an UNLISTEN command to unsubscribe from a channel.
162    ///
163    /// # Example
164    /// ```ignore
165    /// let cmd = Qail::unlisten("orders");
166    /// // Generates: UNLISTEN orders
167    /// ```
168    pub fn unlisten(channel: impl Into<String>) -> Self {
169        Self {
170            action: Action::Unlisten,
171            channel: Some(channel.into()),
172            ..Default::default()
173        }
174    }
175
176    /// Create a NOTIFY command to send a message to a channel.
177    ///
178    /// # Example
179    /// ```ignore
180    /// let cmd = Qail::notify("orders", "new_order:123");
181    /// // Generates: NOTIFY orders, 'new_order:123'
182    /// ```
183    pub fn notify(channel: impl Into<String>, payload: impl Into<String>) -> Self {
184        Self {
185            action: Action::Notify,
186            channel: Some(channel.into()),
187            payload: Some(payload.into()),
188            ..Default::default()
189        }
190    }
191
192    /// Create a NOTIFY on the channel a gateway `subscribe(fragment)` under
193    /// `ctx` actually listens on.
194    ///
195    /// The gateway derives its real PostgreSQL channel from
196    /// `(scope, fragment)` via [`crate::rls::channel::scoped_channel_for`];
197    /// this is the producer-side half of that contract, so application code
198    /// never re-implements the naming convention.
199    ///
200    /// # Example
201    /// ```ignore
202    /// let ctx = RlsContext::tenant("acme");
203    /// let cmd = Qail::notify_scoped(&ctx, "chat_42", r#"{"id":7}"#)?;
204    /// // Generates: NOTIFY "t_4_acme_chat_42", '{"id":7}'
205    /// ```
206    pub fn notify_scoped(
207        ctx: &crate::rls::RlsContext,
208        fragment: &str,
209        payload: impl Into<String>,
210    ) -> Result<Self, String> {
211        let channel = crate::rls::channel::scoped_channel_for(ctx, fragment)?;
212        Ok(Self::notify(channel, payload))
213    }
214
215    // PostgreSQL Procedural Commands
216
217    /// Create a CALL command to invoke a stored procedure.
218    ///
219    /// # Example
220    /// ```ignore
221    /// let cmd = Qail::call("refresh_materialized_views()");
222    /// // Generates: CALL refresh_materialized_views()
223    /// ```
224    pub fn call(procedure: impl Into<String>) -> Self {
225        Self {
226            action: Action::Call,
227            table: procedure.into(),
228            ..Default::default()
229        }
230    }
231
232    /// Create a DO command to execute an anonymous code block.
233    ///
234    /// # Example
235    /// ```ignore
236    /// let cmd = Qail::do_block("BEGIN RAISE NOTICE 'hello'; END;", "plpgsql");
237    /// // Generates: DO $$ BEGIN RAISE NOTICE 'hello'; END; $$ LANGUAGE plpgsql
238    /// ```
239    pub fn do_block(body: impl Into<String>, language: impl Into<String>) -> Self {
240        Self {
241            action: Action::Do,
242            payload: Some(body.into()),
243            table: language.into(),
244            ..Default::default()
245        }
246    }
247
248    // PostgreSQL Session Commands
249
250    /// Create a SET command for session variables.
251    ///
252    /// # Example
253    /// ```ignore
254    /// let cmd = Qail::session_set("statement_timeout", "5000");
255    /// // Generates: SET statement_timeout = '5000'
256    /// ```
257    pub fn session_set(key: impl Into<String>, value: impl Into<String>) -> Self {
258        Self {
259            action: Action::SessionSet,
260            table: key.into(),
261            payload: Some(value.into()),
262            ..Default::default()
263        }
264    }
265
266    /// Create a SHOW command to inspect a session variable.
267    ///
268    /// # Example
269    /// ```ignore
270    /// let cmd = Qail::session_show("statement_timeout");
271    /// // Generates: SHOW statement_timeout
272    /// ```
273    pub fn session_show(key: impl Into<String>) -> Self {
274        Self {
275            action: Action::SessionShow,
276            table: key.into(),
277            ..Default::default()
278        }
279    }
280
281    /// Create a RESET command to restore a session variable to default.
282    ///
283    /// # Example
284    /// ```ignore
285    /// let cmd = Qail::session_reset("statement_timeout");
286    /// // Generates: RESET statement_timeout
287    /// ```
288    pub fn session_reset(key: impl Into<String>) -> Self {
289        Self {
290            action: Action::SessionReset,
291            table: key.into(),
292            ..Default::default()
293        }
294    }
295
296    /// Create a CREATE DATABASE command.
297    pub fn create_database(name: impl Into<String>) -> Self {
298        Self {
299            action: Action::CreateDatabase,
300            table: name.into(),
301            ..Default::default()
302        }
303    }
304
305    /// Create a DROP DATABASE command.
306    pub fn drop_database(name: impl Into<String>) -> Self {
307        Self {
308            action: Action::DropDatabase,
309            table: name.into(),
310            ..Default::default()
311        }
312    }
313}