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}