1use crate::ast::{
10 AlterRoutineKind, AlterTableAction, AlterViewKind, DiscardTarget, DropKind, TransactionStmt,
11};
12use crate::plan::{CommandPlan, UnifiedPlan};
13use crate::SQLResult;
14
15pub fn set_command_completion(
16 plan: &UnifiedPlan,
17 result: &mut SQLResult,
18 transaction_failed: bool,
19) {
20 let command = match plan {
21 UnifiedPlan::Query(_) => {
22 result.command_tag = Some(format!("SELECT {}", result.rows.len()));
23 return;
24 }
25 UnifiedPlan::Command(command) => command.as_ref(),
26 };
27 if matches!(
28 command,
29 CommandPlan::Execute { .. }
30 | CommandPlan::CreateTableAs { .. }
31 | CommandPlan::CreateMaterializedView { .. }
32 ) {
33 return;
35 }
36 result.command_tag = Some(command_completion(command, result, transaction_failed));
37}
38
39#[expect(
40 clippy::too_many_lines,
41 reason = "exhaustive SQL command completion mapping"
42)]
43fn command_completion(
44 command: &CommandPlan,
45 result: &SQLResult,
46 transaction_failed: bool,
47) -> String {
48 let tag = match command {
49 CommandPlan::Insert(_) => return format!("INSERT 0 {}", result.affected_rows),
50 CommandPlan::Update(_) => return format!("UPDATE {}", result.affected_rows),
51 CommandPlan::Delete(_) => return format!("DELETE {}", result.affected_rows),
52 CommandPlan::Merge(_) => return format!("MERGE {}", result.affected_rows),
53 CommandPlan::CreateTableAs { .. } | CommandPlan::CreateMaterializedView { .. } => {
54 unreachable!("population execution owns completion")
55 }
56 CommandPlan::FetchCursor(fetch) => {
57 return if fetch.move_only {
58 format!("MOVE {}", result.affected_rows)
59 } else {
60 format!("FETCH {}", result.rows.len())
61 };
62 }
63 CommandPlan::CreateTable(_) | CommandPlan::CreateTableIfNotExists(_) => "CREATE TABLE",
64 CommandPlan::CreateIndex(_) => "CREATE INDEX",
65 CommandPlan::RenameIndex(_) => "ALTER INDEX",
66 CommandPlan::Drop(statement) => match statement.kind {
67 DropKind::Table => "DROP TABLE",
68 DropKind::ForeignTable => "DROP FOREIGN TABLE",
69 DropKind::Index => "DROP INDEX",
70 DropKind::View => "DROP VIEW",
71 DropKind::MaterializedView => "DROP MATERIALIZED VIEW",
72 DropKind::Schema => "DROP SCHEMA",
73 DropKind::Sequence => "DROP SEQUENCE",
74 DropKind::Domain => "DROP DOMAIN",
75 },
76 CommandPlan::AlterTable(statement) => match statement.actions.as_slice() {
77 [AlterTableAction::RenameTrigger { .. }] => "ALTER TRIGGER",
78 [AlterTableAction::RenameRule { .. }] => "ALTER RULE",
79 _ => "ALTER TABLE",
80 },
81 CommandPlan::AlterForeignTable(_) => "ALTER FOREIGN TABLE",
82 CommandPlan::AlterView(statement) => match statement.kind {
83 AlterViewKind::View => "ALTER VIEW",
84 AlterViewKind::MaterializedView => "ALTER MATERIALIZED VIEW",
85 },
86 CommandPlan::CreateView { .. } => "CREATE VIEW",
87 CommandPlan::RefreshMaterializedView { .. } => "REFRESH MATERIALIZED VIEW",
88 CommandPlan::CreateSchema { .. } => "CREATE SCHEMA",
89 CommandPlan::AlterSchemaOwner { .. } => "ALTER SCHEMA",
90 CommandPlan::Notify { .. } => "NOTIFY",
91 CommandPlan::Listen { .. } => "LISTEN",
92 CommandPlan::Unlisten { .. } => "UNLISTEN",
93 CommandPlan::SetVariable { .. } => "SET",
94 CommandPlan::ResetVariable { .. } | CommandPlan::ResetAllVariables => "RESET",
95 CommandPlan::SetConstraints { .. } => "SET CONSTRAINTS",
96 CommandPlan::ShowVariable { .. } => "SHOW",
97 CommandPlan::Discard { target } => match target {
98 DiscardTarget::All => "DISCARD ALL",
99 DiscardTarget::Plans => "DISCARD PLANS",
100 DiscardTarget::Sequences => "DISCARD SEQUENCES",
101 DiscardTarget::Temp => "DISCARD TEMP",
102 },
103 CommandPlan::Load { .. } => "LOAD",
104 CommandPlan::Explain { .. } => "EXPLAIN",
105 CommandPlan::Analyze { .. } => "ANALYZE",
106 CommandPlan::LockTable(_) => "LOCK TABLE",
107 CommandPlan::Vacuum(_) => "VACUUM",
108 CommandPlan::Truncate { .. } => "TRUNCATE TABLE",
109 CommandPlan::Transaction(statement) => {
110 transaction_completion(statement, transaction_failed)
111 }
112 CommandPlan::DeclareCursor { .. } => "DECLARE CURSOR",
113 CommandPlan::CloseCursor { name } => {
114 if name.is_some() {
115 "CLOSE CURSOR"
116 } else {
117 "CLOSE CURSOR ALL"
118 }
119 }
120 CommandPlan::CreateSequence(_) => "CREATE SEQUENCE",
121 CommandPlan::CreateDomain(_) => "CREATE DOMAIN",
122 CommandPlan::AlterSequence(_) => "ALTER SEQUENCE",
123 CommandPlan::Prepare { .. } => "PREPARE",
124 CommandPlan::Execute { .. } => {
125 unreachable!("EXECUTE preserves delegated command completion")
126 }
127 CommandPlan::Deallocate { name } => {
128 if name.is_some() {
129 "DEALLOCATE"
130 } else {
131 "DEALLOCATE ALL"
132 }
133 }
134 CommandPlan::CreateForeignServer(_) => "CREATE SERVER",
135 CommandPlan::CreateForeignTable(_) | CommandPlan::CreateForeignTableIfNotExists(_) => {
136 "CREATE FOREIGN TABLE"
137 }
138 CommandPlan::CreateFunction(statement) => {
139 if statement.is_procedure {
140 "CREATE PROCEDURE"
141 } else {
142 "CREATE FUNCTION"
143 }
144 }
145 CommandPlan::DropFunction(statement) => {
146 if statement.is_procedure {
147 "DROP PROCEDURE"
148 } else {
149 "DROP FUNCTION"
150 }
151 }
152 CommandPlan::AlterRoutine(statement) => alter_routine_completion(statement.kind),
153 CommandPlan::AlterRoutineOwner(statement) => alter_routine_completion(statement.kind),
154 CommandPlan::RenameRoutine(statement) => alter_routine_completion(statement.kind),
155 CommandPlan::GrantRoutine(statement) => grant_completion(statement.is_grant),
156 CommandPlan::GrantTable(statement) => grant_completion(statement.is_grant),
157 CommandPlan::GrantSequence(statement) => grant_completion(statement.is_grant),
158 CommandPlan::GrantDatabase(statement) => grant_completion(statement.is_grant),
159 CommandPlan::GrantSchema(statement) => grant_completion(statement.is_grant),
160 CommandPlan::GrantRole(statement) => {
161 if statement.is_grant {
162 "GRANT ROLE"
163 } else {
164 "REVOKE ROLE"
165 }
166 }
167 CommandPlan::CreateRole(_) => "CREATE ROLE",
168 CommandPlan::AlterRole(_) | CommandPlan::RenameRole(_) => "ALTER ROLE",
169 CommandPlan::DropRole(_) => "DROP ROLE",
170 CommandPlan::CreateTrigger(_) => "CREATE TRIGGER",
171 CommandPlan::DropTrigger(_) => "DROP TRIGGER",
172 CommandPlan::CreateRule(_) => "CREATE RULE",
173 CommandPlan::DropRule(_) => "DROP RULE",
174 CommandPlan::DoBlock { .. } => "DO",
175 CommandPlan::Call { .. } => "CALL",
176 };
177 tag.to_string()
178}
179
180pub const fn transaction_completion(statement: &TransactionStmt, failed: bool) -> &'static str {
181 match statement {
182 TransactionStmt::Begin | TransactionStmt::BeginWithCharacteristics(_) => "BEGIN",
183 TransactionStmt::Commit | TransactionStmt::CommitAndChain => {
184 if failed {
185 "ROLLBACK"
186 } else {
187 "COMMIT"
188 }
189 }
190 TransactionStmt::Rollback
191 | TransactionStmt::RollbackAndChain
192 | TransactionStmt::RollbackToSavepoint(_) => "ROLLBACK",
193 TransactionStmt::Savepoint(_) => "SAVEPOINT",
194 TransactionStmt::ReleaseSavepoint(_) => "RELEASE",
195 TransactionStmt::SetCharacteristics(_)
196 | TransactionStmt::SetSessionCharacteristics(_)
197 | TransactionStmt::SetSnapshot(_) => "SET",
198 }
199}
200
201const fn alter_routine_completion(kind: AlterRoutineKind) -> &'static str {
202 match kind {
203 AlterRoutineKind::Function => "ALTER FUNCTION",
204 AlterRoutineKind::Procedure => "ALTER PROCEDURE",
205 AlterRoutineKind::Routine => "ALTER ROUTINE",
206 }
207}
208
209const fn grant_completion(is_grant: bool) -> &'static str {
210 if is_grant {
211 "GRANT"
212 } else {
213 "REVOKE"
214 }
215}