use super::administrative::{
compile_analyze, compile_explain, compile_set_constraints, compile_transaction,
compile_truncate, compile_variable_set, discard_target,
};
use super::cursors::{compile_close_cursor, compile_declare_cursor, compile_fetch_cursor};
use super::dml::{compile_delete, compile_update};
use super::drop_alter::{
compile_alter_object_schema, compile_alter_table, compile_drop, compile_rename,
};
use super::events::{compile_create_rule, compile_create_trigger};
use super::merge::compile_merge;
use super::relations::{
compile_create_foreign_server, compile_create_foreign_table, compile_create_schema,
compile_create_table_as, compile_create_view, compile_deallocate, compile_execute,
compile_prepare, compile_refresh_materialized_view, compile_top_level_select,
defer_create_foreign_table, defer_create_table,
};
use super::routines::{
compile_alter_role, compile_alter_routine, compile_alter_routine_owner, compile_call,
compile_create_function, compile_create_role, compile_do, compile_drop_role, compile_grant,
compile_grant_role,
};
use super::sequences::{compile_alter_sequence, compile_create_sequence};
use super::{
compile_create_index, compile_create_table, compile_insert, compile_values_lists, Node,
NodeEnum, Result, SQLError, Statement,
};
pub fn compile(sql: &str) -> Result<Vec<Statement>> {
let parsed = pg_query::parse(sql)?;
let mut out = Vec::with_capacity(parsed.protobuf.stmts.len());
for raw in parsed.protobuf.stmts {
let node = raw
.stmt
.ok_or_else(|| SQLError::Internal("parser returned an empty statement".into()))?;
out.push(compile_stmt(&node)?);
}
Ok(out)
}
pub fn resolve_deferred_create_table(
deferred: &crate::ast::DeferredCreateTable,
) -> Result<crate::ast::CreateTable> {
let parsed = pg_query::parse(&deferred.definition_sql)?;
let [raw] = parsed.protobuf.stmts.as_slice() else {
return Err(SQLError::Internal(
"deferred CREATE TABLE did not contain exactly one statement".into(),
));
};
let node = raw
.stmt
.as_deref()
.and_then(|node| node.node.as_ref())
.ok_or_else(|| SQLError::Internal("deferred CREATE TABLE is empty".into()))?;
let NodeEnum::CreateStmt(stmt) = node else {
return Err(SQLError::Internal(
"deferred CREATE TABLE changed statement kind".into(),
));
};
let table = compile_create_table(stmt)?;
if !table.if_not_exists
|| table.name != deferred.name
|| table.persistence != deferred.persistence
{
return Err(SQLError::Internal(
"deferred CREATE TABLE changed target identity".into(),
));
}
Ok(table)
}
pub fn resolve_deferred_create_foreign_table(
deferred: &crate::ast::DeferredCreateForeignTable,
) -> Result<crate::ast::CreateForeignTable> {
let parsed = pg_query::parse(&deferred.definition_sql)?;
let [raw] = parsed.protobuf.stmts.as_slice() else {
return Err(SQLError::Internal(
"deferred CREATE FOREIGN TABLE did not contain exactly one statement".into(),
));
};
let node = raw
.stmt
.as_deref()
.and_then(|node| node.node.as_ref())
.ok_or_else(|| SQLError::Internal("deferred CREATE FOREIGN TABLE is empty".into()))?;
let NodeEnum::CreateForeignTableStmt(stmt) = node else {
return Err(SQLError::Internal(
"deferred CREATE FOREIGN TABLE changed statement kind".into(),
));
};
let table = compile_create_foreign_table(stmt)?;
if !table.if_not_exists
|| table.name != deferred.name
|| table.server_name != deferred.server_name
{
return Err(SQLError::Internal(
"deferred CREATE FOREIGN TABLE changed target identity".into(),
));
}
Ok(table)
}
fn compile_create_table_statement(statement: &pg_query::protobuf::CreateStmt) -> Result<Statement> {
if statement.if_not_exists {
defer_create_table(statement).map(Statement::CreateTableIfNotExists)
} else {
compile_create_table(statement).map(Statement::CreateTable)
}
}
fn compile_create_foreign_table_statement(
statement: &pg_query::protobuf::CreateForeignTableStmt,
) -> Result<Statement> {
if statement
.base_stmt
.as_ref()
.is_some_and(|base| base.if_not_exists)
{
defer_create_foreign_table(statement).map(Statement::CreateForeignTableIfNotExists)
} else {
compile_create_foreign_table(statement).map(Statement::CreateForeignTable)
}
}
pub(super) fn compile_stmt(node: &Node) -> Result<Statement> {
let Some(inner) = node.node.as_ref() else {
return Err(SQLError::Unsupported("empty statement".into()));
};
match inner {
NodeEnum::CreateStmt(stmt) => compile_create_table_statement(stmt),
NodeEnum::IndexStmt(stmt) => compile_create_index(stmt).map(Statement::CreateIndex),
NodeEnum::InsertStmt(stmt) => compile_insert(stmt).map(Statement::Insert),
NodeEnum::SelectStmt(stmt) => {
if stmt.target_list.is_empty()
&& !stmt.values_lists.is_empty()
&& stmt.locking_clause.is_empty()
&& stmt.sort_clause.is_empty()
&& stmt.limit_count.is_none()
&& stmt.limit_offset.is_none()
{
let rows = compile_values_lists(&stmt.values_lists)?;
return Ok(Statement::Values { rows });
}
compile_top_level_select(stmt)
}
NodeEnum::UpdateStmt(stmt) => compile_update(stmt).map(Statement::Update),
NodeEnum::DeleteStmt(stmt) => compile_delete(stmt).map(Statement::Delete),
NodeEnum::DropStmt(stmt) => compile_drop(stmt),
NodeEnum::CreateTrigStmt(stmt) => {
compile_create_trigger(stmt).map(Statement::CreateTrigger)
}
NodeEnum::RuleStmt(stmt) => compile_create_rule(stmt).map(Statement::CreateRule),
NodeEnum::AlterTableStmt(stmt) => compile_alter_table(stmt),
NodeEnum::RenameStmt(stmt) => compile_rename(stmt),
NodeEnum::AlterObjectSchemaStmt(stmt) => compile_alter_object_schema(stmt),
NodeEnum::ViewStmt(stmt) => compile_create_view(stmt),
NodeEnum::CreateSchemaStmt(stmt) => compile_create_schema(stmt),
NodeEnum::NotifyStmt(stmt) => Ok(Statement::Notify {
channel: stmt.conditionname.clone(),
payload: stmt.payload.clone(),
}),
NodeEnum::ListenStmt(stmt) => Ok(Statement::Listen {
channel: stmt.conditionname.clone(),
}),
NodeEnum::UnlistenStmt(stmt) => Ok(Statement::Unlisten {
channel: (!stmt.conditionname.is_empty()).then(|| stmt.conditionname.clone()),
}),
NodeEnum::ExplainStmt(stmt) => compile_explain(stmt),
NodeEnum::VacuumStmt(stmt) => compile_analyze(stmt),
NodeEnum::TruncateStmt(stmt) => compile_truncate(stmt),
NodeEnum::TransactionStmt(stmt) => compile_transaction(stmt),
NodeEnum::DeclareCursorStmt(stmt) => compile_declare_cursor(stmt),
NodeEnum::FetchStmt(stmt) => compile_fetch_cursor(stmt),
NodeEnum::ClosePortalStmt(stmt) => Ok(compile_close_cursor(stmt)),
NodeEnum::CreateSeqStmt(stmt) => {
compile_create_sequence(stmt).map(Statement::CreateSequence)
}
NodeEnum::AlterSeqStmt(stmt) => compile_alter_sequence(stmt).map(Statement::AlterSequence),
NodeEnum::CreateTableAsStmt(stmt) => compile_create_table_as(stmt),
NodeEnum::RefreshMatViewStmt(stmt) => compile_refresh_materialized_view(stmt),
NodeEnum::PrepareStmt(stmt) => compile_prepare(stmt),
NodeEnum::ExecuteStmt(stmt) => compile_execute(stmt),
NodeEnum::DeallocateStmt(stmt) => compile_deallocate(stmt),
NodeEnum::CreateForeignServerStmt(stmt) => {
compile_create_foreign_server(stmt).map(Statement::CreateForeignServer)
}
NodeEnum::CreateForeignTableStmt(stmt) => compile_create_foreign_table_statement(stmt),
NodeEnum::MergeStmt(stmt) => compile_merge(stmt).map(Statement::Merge),
NodeEnum::CreateFunctionStmt(stmt) => {
compile_create_function(stmt).map(|f| Statement::CreateFunction(Box::new(f)))
}
NodeEnum::DoStmt(stmt) => compile_do(stmt),
NodeEnum::CallStmt(stmt) => compile_call(stmt),
NodeEnum::AlterFunctionStmt(stmt) => {
compile_alter_routine(stmt).map(Statement::AlterRoutine)
}
NodeEnum::AlterOwnerStmt(stmt) => compile_alter_routine_owner(stmt),
NodeEnum::GrantStmt(stmt) => compile_grant(stmt),
NodeEnum::GrantRoleStmt(stmt) => compile_grant_role(stmt),
NodeEnum::CreateRoleStmt(stmt) => compile_create_role(stmt),
NodeEnum::AlterRoleStmt(stmt) => compile_alter_role(stmt),
NodeEnum::DropRoleStmt(stmt) => compile_drop_role(stmt),
NodeEnum::VariableSetStmt(stmt) => compile_variable_set(stmt),
NodeEnum::ConstraintsSetStmt(stmt) => compile_set_constraints(stmt),
NodeEnum::VariableShowStmt(stmt) => Ok(Statement::ShowVariable {
name: stmt.name.clone(),
}),
NodeEnum::DiscardStmt(stmt) => Ok(Statement::Discard {
target: discard_target(stmt.target)?,
}),
NodeEnum::LoadStmt(stmt) => Ok(Statement::Load {
library: stmt.filename.clone(),
}),
other => Err(SQLError::Unsupported(format!(
"{}",
other_node_label(other)
))),
}
}
pub(super) fn other_node_label(node: &NodeEnum) -> &'static str {
match node {
NodeEnum::ExplainStmt(_) => "EXPLAIN",
NodeEnum::ViewStmt(_) => "CREATE VIEW",
NodeEnum::TransactionStmt(_) => "BEGIN/COMMIT/ROLLBACK",
NodeEnum::DeclareCursorStmt(_) => "DECLARE CURSOR",
NodeEnum::FetchStmt(_) => "FETCH/MOVE",
NodeEnum::ClosePortalStmt(_) => "CLOSE CURSOR",
NodeEnum::PrepareStmt(_) | NodeEnum::ExecuteStmt(_) => "PREPARE/EXECUTE",
_ => "unknown statement",
}
}
pub fn plan_only_for_test(sql: &str) -> Result<Vec<Statement>> {
compile(sql)
}