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