1use super::administrative::{
10 compile_analyze, compile_explain, compile_set_constraints, compile_transaction,
11 compile_truncate, compile_variable_set, discard_target,
12};
13use super::cursors::{compile_close_cursor, compile_declare_cursor, compile_fetch_cursor};
14use super::dml::{compile_delete, compile_update};
15use super::drop_alter::{
16 compile_alter_object_schema, compile_alter_table, compile_drop, compile_rename,
17};
18use super::events::{compile_create_rule, compile_create_trigger};
19use super::merge::compile_merge;
20use super::relations::{
21 compile_create_foreign_server, compile_create_foreign_table, compile_create_schema,
22 compile_create_table_as, compile_create_view, compile_deallocate, compile_execute,
23 compile_prepare, compile_refresh_materialized_view, compile_top_level_select,
24 defer_create_foreign_table, defer_create_table,
25};
26use super::routines::{
27 compile_alter_role, compile_alter_routine, compile_alter_routine_owner, compile_call,
28 compile_create_function, compile_create_role, compile_do, compile_drop_role, compile_grant,
29 compile_grant_role,
30};
31use super::sequences::{compile_alter_sequence, compile_create_sequence};
32use super::{
33 compile_create_index, compile_create_table, compile_insert, compile_values_lists, Node,
34 NodeEnum, Result, SQLError, Statement,
35};
36
37#[derive(Debug, Clone)]
39pub struct ParsedStatement<'sql> {
40 sql: &'sql str,
41 node: Box<Node>,
42}
43
44impl<'sql> ParsedStatement<'sql> {
45 pub const fn sql(&self) -> &'sql str {
47 self.sql
48 }
49
50 pub fn is_notification_listener_command(&self) -> bool {
52 matches!(
53 self.node.node.as_ref(),
54 Some(NodeEnum::ListenStmt(_) | NodeEnum::UnlistenStmt(_))
55 )
56 }
57
58 pub fn compile(&self) -> Result<Statement> {
60 compile_stmt(&self.node)
61 }
62}
63
64pub fn parse_statements(sql: &str) -> Result<Vec<ParsedStatement<'_>>> {
66 let parsed = pg_query::parse(sql)?;
67 let mut out = Vec::with_capacity(parsed.protobuf.stmts.len());
68 for raw in parsed.protobuf.stmts {
69 let node = raw
70 .stmt
71 .ok_or_else(|| SQLError::Internal("parser returned an empty statement".into()))?;
72 let start = usize::try_from(raw.stmt_location).map_err(|_| {
73 SQLError::Internal("parser returned a negative statement offset".into())
74 })?;
75 let end = if raw.stmt_len == 0 {
76 sql.len()
77 } else {
78 let len = usize::try_from(raw.stmt_len).map_err(|_| {
79 SQLError::Internal("parser returned a negative statement length".into())
80 })?;
81 start
82 .checked_add(len)
83 .ok_or_else(|| SQLError::Internal("parser statement offset overflow".into()))?
84 };
85 let source = sql.get(start..end).ok_or_else(|| {
86 SQLError::Internal("parser statement bounds do not match SQL text".into())
87 })?;
88 out.push(ParsedStatement { sql: source, node });
89 }
90 Ok(out)
91}
92
93pub fn compile(sql: &str) -> Result<Vec<Statement>> {
94 parse_statements(sql)?
95 .iter()
96 .map(ParsedStatement::compile)
97 .collect()
98}
99
100pub fn resolve_deferred_create_table(
101 deferred: &crate::ast::DeferredCreateTable,
102) -> Result<crate::ast::CreateTable> {
103 let parsed = pg_query::parse(&deferred.definition_sql)?;
104 let [raw] = parsed.protobuf.stmts.as_slice() else {
105 return Err(SQLError::Internal(
106 "deferred CREATE TABLE did not contain exactly one statement".into(),
107 ));
108 };
109 let node = raw
110 .stmt
111 .as_deref()
112 .and_then(|node| node.node.as_ref())
113 .ok_or_else(|| SQLError::Internal("deferred CREATE TABLE is empty".into()))?;
114 let NodeEnum::CreateStmt(stmt) = node else {
115 return Err(SQLError::Internal(
116 "deferred CREATE TABLE changed statement kind".into(),
117 ));
118 };
119 let table = compile_create_table(stmt)?;
120 if !table.if_not_exists
121 || table.name != deferred.name
122 || table.persistence != deferred.persistence
123 {
124 return Err(SQLError::Internal(
125 "deferred CREATE TABLE changed target identity".into(),
126 ));
127 }
128 Ok(table)
129}
130
131pub fn resolve_deferred_create_foreign_table(
132 deferred: &crate::ast::DeferredCreateForeignTable,
133) -> Result<crate::ast::CreateForeignTable> {
134 let parsed = pg_query::parse(&deferred.definition_sql)?;
135 let [raw] = parsed.protobuf.stmts.as_slice() else {
136 return Err(SQLError::Internal(
137 "deferred CREATE FOREIGN TABLE did not contain exactly one statement".into(),
138 ));
139 };
140 let node = raw
141 .stmt
142 .as_deref()
143 .and_then(|node| node.node.as_ref())
144 .ok_or_else(|| SQLError::Internal("deferred CREATE FOREIGN TABLE is empty".into()))?;
145 let NodeEnum::CreateForeignTableStmt(stmt) = node else {
146 return Err(SQLError::Internal(
147 "deferred CREATE FOREIGN TABLE changed statement kind".into(),
148 ));
149 };
150 let table = compile_create_foreign_table(stmt)?;
151 if !table.if_not_exists
152 || table.name != deferred.name
153 || table.server_name != deferred.server_name
154 {
155 return Err(SQLError::Internal(
156 "deferred CREATE FOREIGN TABLE changed target identity".into(),
157 ));
158 }
159 Ok(table)
160}
161
162fn compile_create_table_statement(statement: &pg_query::protobuf::CreateStmt) -> Result<Statement> {
163 if statement.if_not_exists {
164 defer_create_table(statement).map(Statement::CreateTableIfNotExists)
165 } else {
166 compile_create_table(statement).map(Statement::CreateTable)
167 }
168}
169
170fn compile_create_foreign_table_statement(
171 statement: &pg_query::protobuf::CreateForeignTableStmt,
172) -> Result<Statement> {
173 if statement
174 .base_stmt
175 .as_ref()
176 .is_some_and(|base| base.if_not_exists)
177 {
178 defer_create_foreign_table(statement).map(Statement::CreateForeignTableIfNotExists)
179 } else {
180 compile_create_foreign_table(statement).map(Statement::CreateForeignTable)
181 }
182}
183
184pub(super) fn compile_stmt(node: &Node) -> Result<Statement> {
185 let Some(inner) = node.node.as_ref() else {
186 return Err(SQLError::Unsupported("empty statement".into()));
187 };
188 match inner {
189 NodeEnum::CreateStmt(stmt) => compile_create_table_statement(stmt),
190 NodeEnum::IndexStmt(stmt) => compile_create_index(stmt).map(Statement::CreateIndex),
191 NodeEnum::InsertStmt(stmt) => compile_insert(stmt).map(Statement::Insert),
192 NodeEnum::SelectStmt(stmt) => {
193 if stmt.target_list.is_empty()
197 && !stmt.values_lists.is_empty()
198 && stmt.locking_clause.is_empty()
199 && stmt.sort_clause.is_empty()
200 && stmt.limit_count.is_none()
201 && stmt.limit_offset.is_none()
202 {
203 let rows = compile_values_lists(&stmt.values_lists)?;
204 return Ok(Statement::Values { rows });
205 }
206 compile_top_level_select(stmt)
207 }
208 NodeEnum::UpdateStmt(stmt) => compile_update(stmt).map(Statement::Update),
209 NodeEnum::DeleteStmt(stmt) => compile_delete(stmt).map(Statement::Delete),
210 NodeEnum::DropStmt(stmt) => compile_drop(stmt),
211 NodeEnum::CreateTrigStmt(stmt) => {
212 compile_create_trigger(stmt).map(Statement::CreateTrigger)
213 }
214 NodeEnum::RuleStmt(stmt) => compile_create_rule(stmt).map(Statement::CreateRule),
215 NodeEnum::AlterTableStmt(stmt) => compile_alter_table(stmt),
216 NodeEnum::RenameStmt(stmt) => compile_rename(stmt),
217 NodeEnum::AlterObjectSchemaStmt(stmt) => compile_alter_object_schema(stmt),
218 NodeEnum::ViewStmt(stmt) => compile_create_view(stmt),
219 NodeEnum::CreateSchemaStmt(stmt) => compile_create_schema(stmt),
220 NodeEnum::NotifyStmt(stmt) => Ok(Statement::Notify {
221 channel: stmt.conditionname.clone(),
222 payload: stmt.payload.clone(),
223 }),
224 NodeEnum::ListenStmt(stmt) => Ok(Statement::Listen {
225 channel: stmt.conditionname.clone(),
226 }),
227 NodeEnum::UnlistenStmt(stmt) => Ok(Statement::Unlisten {
228 channel: (!stmt.conditionname.is_empty()).then(|| stmt.conditionname.clone()),
229 }),
230 NodeEnum::ExplainStmt(stmt) => compile_explain(stmt),
231 NodeEnum::VacuumStmt(stmt) => compile_analyze(stmt),
232 NodeEnum::TruncateStmt(stmt) => compile_truncate(stmt),
233 NodeEnum::LockStmt(stmt) => super::locking::compile_lock_table(stmt),
234 NodeEnum::TransactionStmt(stmt) => compile_transaction(stmt),
235 NodeEnum::DeclareCursorStmt(stmt) => compile_declare_cursor(stmt),
236 NodeEnum::FetchStmt(stmt) => compile_fetch_cursor(stmt),
237 NodeEnum::ClosePortalStmt(stmt) => Ok(compile_close_cursor(stmt)),
238 NodeEnum::CreateSeqStmt(stmt) => {
239 compile_create_sequence(stmt).map(Statement::CreateSequence)
240 }
241 NodeEnum::AlterSeqStmt(stmt) => compile_alter_sequence(stmt).map(Statement::AlterSequence),
242 NodeEnum::CreateTableAsStmt(stmt) => compile_create_table_as(stmt),
243 NodeEnum::RefreshMatViewStmt(stmt) => compile_refresh_materialized_view(stmt),
244 NodeEnum::PrepareStmt(stmt) => compile_prepare(stmt),
245 NodeEnum::ExecuteStmt(stmt) => compile_execute(stmt),
246 NodeEnum::DeallocateStmt(stmt) => compile_deallocate(stmt),
247 NodeEnum::CreateForeignServerStmt(stmt) => {
248 compile_create_foreign_server(stmt).map(Statement::CreateForeignServer)
249 }
250 NodeEnum::CreateForeignTableStmt(stmt) => compile_create_foreign_table_statement(stmt),
251 NodeEnum::MergeStmt(stmt) => compile_merge(stmt).map(Statement::Merge),
252 NodeEnum::CreateDomainStmt(stmt) => {
253 super::domains::compile_create_domain(stmt).map(Statement::CreateDomain)
254 }
255 NodeEnum::CreateFunctionStmt(stmt) => {
256 compile_create_function(stmt).map(|f| Statement::CreateFunction(Box::new(f)))
257 }
258 NodeEnum::DoStmt(stmt) => compile_do(stmt),
259 NodeEnum::CallStmt(stmt) => compile_call(stmt),
260 NodeEnum::AlterFunctionStmt(stmt) => {
261 compile_alter_routine(stmt).map(Statement::AlterRoutine)
262 }
263 NodeEnum::AlterOwnerStmt(stmt) => compile_alter_routine_owner(stmt),
264 NodeEnum::GrantStmt(stmt) => compile_grant(stmt),
265 NodeEnum::GrantRoleStmt(stmt) => compile_grant_role(stmt),
266 NodeEnum::CreateRoleStmt(stmt) => compile_create_role(stmt),
267 NodeEnum::AlterRoleStmt(stmt) => compile_alter_role(stmt),
268 NodeEnum::DropRoleStmt(stmt) => compile_drop_role(stmt),
269 NodeEnum::VariableSetStmt(stmt) => compile_variable_set(stmt),
270 NodeEnum::ConstraintsSetStmt(stmt) => compile_set_constraints(stmt),
271 NodeEnum::VariableShowStmt(stmt) => Ok(Statement::ShowVariable {
272 name: stmt.name.clone(),
273 }),
274 NodeEnum::DiscardStmt(stmt) => Ok(Statement::Discard {
275 target: discard_target(stmt.target)?,
276 }),
277 NodeEnum::LoadStmt(stmt) => Ok(Statement::Load {
278 library: stmt.filename.clone(),
279 }),
280 other => Err(SQLError::Unsupported(format!(
281 "{}",
282 other_node_label(other)
283 ))),
284 }
285}
286
287pub(super) fn other_node_label(node: &NodeEnum) -> &'static str {
290 match node {
291 NodeEnum::ExplainStmt(_) => "EXPLAIN",
292 NodeEnum::ViewStmt(_) => "CREATE VIEW",
293 NodeEnum::TransactionStmt(_) => "BEGIN/COMMIT/ROLLBACK",
294 NodeEnum::DeclareCursorStmt(_) => "DECLARE CURSOR",
295 NodeEnum::FetchStmt(_) => "FETCH/MOVE",
296 NodeEnum::ClosePortalStmt(_) => "CLOSE CURSOR",
297 NodeEnum::PrepareStmt(_) | NodeEnum::ExecuteStmt(_) => "PREPARE/EXECUTE",
298 _ => "unknown statement",
299 }
300}
301
302pub fn plan_only_for_test(sql: &str) -> Result<Vec<Statement>> {
311 compile(sql)
312}