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