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::Drop(statement) => match statement.kind {
66 DropKind::Table => "DROP TABLE",
67 DropKind::ForeignTable => "DROP FOREIGN TABLE",
68 DropKind::Index => "DROP INDEX",
69 DropKind::View => "DROP VIEW",
70 DropKind::MaterializedView => "DROP MATERIALIZED VIEW",
71 DropKind::Schema => "DROP SCHEMA",
72 DropKind::Sequence => "DROP SEQUENCE",
73 DropKind::Domain => "DROP DOMAIN",
74 },
75 CommandPlan::AlterTable(statement) => match statement.actions.as_slice() {
76 [AlterTableAction::RenameTrigger { .. }] => "ALTER TRIGGER",
77 [AlterTableAction::RenameRule { .. }] => "ALTER RULE",
78 _ => "ALTER TABLE",
79 },
80 CommandPlan::AlterForeignTable(_) => "ALTER FOREIGN TABLE",
81 CommandPlan::AlterView(statement) => match statement.kind {
82 AlterViewKind::View => "ALTER VIEW",
83 AlterViewKind::MaterializedView => "ALTER MATERIALIZED VIEW",
84 },
85 CommandPlan::CreateView { .. } => "CREATE VIEW",
86 CommandPlan::RefreshMaterializedView { .. } => "REFRESH MATERIALIZED VIEW",
87 CommandPlan::CreateSchema { .. } => "CREATE SCHEMA",
88 CommandPlan::AlterSchemaOwner { .. } => "ALTER SCHEMA",
89 CommandPlan::Notify { .. } => "NOTIFY",
90 CommandPlan::Listen { .. } => "LISTEN",
91 CommandPlan::Unlisten { .. } => "UNLISTEN",
92 CommandPlan::SetVariable { .. } => "SET",
93 CommandPlan::ResetVariable { .. } | CommandPlan::ResetAllVariables => "RESET",
94 CommandPlan::SetConstraints { .. } => "SET CONSTRAINTS",
95 CommandPlan::ShowVariable { .. } => "SHOW",
96 CommandPlan::Discard { target } => match target {
97 DiscardTarget::All => "DISCARD ALL",
98 DiscardTarget::Plans => "DISCARD PLANS",
99 DiscardTarget::Sequences => "DISCARD SEQUENCES",
100 DiscardTarget::Temp => "DISCARD TEMP",
101 },
102 CommandPlan::Load { .. } => "LOAD",
103 CommandPlan::Explain { .. } => "EXPLAIN",
104 CommandPlan::Analyze { .. } => "ANALYZE",
105 CommandPlan::Vacuum(_) => "VACUUM",
106 CommandPlan::Truncate { .. } => "TRUNCATE TABLE",
107 CommandPlan::Transaction(statement) => {
108 transaction_completion(statement, transaction_failed)
109 }
110 CommandPlan::DeclareCursor { .. } => "DECLARE CURSOR",
111 CommandPlan::CloseCursor { name } => {
112 if name.is_some() {
113 "CLOSE CURSOR"
114 } else {
115 "CLOSE CURSOR ALL"
116 }
117 }
118 CommandPlan::CreateSequence(_) => "CREATE SEQUENCE",
119 CommandPlan::CreateDomain(_) => "CREATE DOMAIN",
120 CommandPlan::AlterSequence(_) => "ALTER SEQUENCE",
121 CommandPlan::Prepare { .. } => "PREPARE",
122 CommandPlan::Execute { .. } => {
123 unreachable!("EXECUTE preserves delegated command completion")
124 }
125 CommandPlan::Deallocate { name } => {
126 if name.is_some() {
127 "DEALLOCATE"
128 } else {
129 "DEALLOCATE ALL"
130 }
131 }
132 CommandPlan::CreateForeignServer(_) => "CREATE SERVER",
133 CommandPlan::CreateForeignTable(_) | CommandPlan::CreateForeignTableIfNotExists(_) => {
134 "CREATE FOREIGN TABLE"
135 }
136 CommandPlan::CreateFunction(statement) => {
137 if statement.is_procedure {
138 "CREATE PROCEDURE"
139 } else {
140 "CREATE FUNCTION"
141 }
142 }
143 CommandPlan::DropFunction(statement) => {
144 if statement.is_procedure {
145 "DROP PROCEDURE"
146 } else {
147 "DROP FUNCTION"
148 }
149 }
150 CommandPlan::AlterRoutine(statement) => alter_routine_completion(statement.kind),
151 CommandPlan::AlterRoutineOwner(statement) => alter_routine_completion(statement.kind),
152 CommandPlan::RenameRoutine(statement) => alter_routine_completion(statement.kind),
153 CommandPlan::GrantRoutine(statement) => grant_completion(statement.is_grant),
154 CommandPlan::GrantTable(statement) => grant_completion(statement.is_grant),
155 CommandPlan::GrantSequence(statement) => grant_completion(statement.is_grant),
156 CommandPlan::GrantDatabase(statement) => grant_completion(statement.is_grant),
157 CommandPlan::GrantSchema(statement) => grant_completion(statement.is_grant),
158 CommandPlan::GrantRole(statement) => {
159 if statement.is_grant {
160 "GRANT ROLE"
161 } else {
162 "REVOKE ROLE"
163 }
164 }
165 CommandPlan::CreateRole(_) => "CREATE ROLE",
166 CommandPlan::AlterRole(_) => "ALTER ROLE",
167 CommandPlan::DropRole(_) => "DROP ROLE",
168 CommandPlan::CreateTrigger(_) => "CREATE TRIGGER",
169 CommandPlan::DropTrigger(_) => "DROP TRIGGER",
170 CommandPlan::CreateRule(_) => "CREATE RULE",
171 CommandPlan::DropRule(_) => "DROP RULE",
172 CommandPlan::DoBlock { .. } => "DO",
173 CommandPlan::Call { .. } => "CALL",
174 };
175 tag.to_string()
176}
177
178pub const fn transaction_completion(statement: &TransactionStmt, failed: bool) -> &'static str {
179 match statement {
180 TransactionStmt::Begin | TransactionStmt::BeginWithCharacteristics(_) => "BEGIN",
181 TransactionStmt::Commit | TransactionStmt::CommitAndChain => {
182 if failed {
183 "ROLLBACK"
184 } else {
185 "COMMIT"
186 }
187 }
188 TransactionStmt::Rollback
189 | TransactionStmt::RollbackAndChain
190 | TransactionStmt::RollbackToSavepoint(_) => "ROLLBACK",
191 TransactionStmt::Savepoint(_) => "SAVEPOINT",
192 TransactionStmt::ReleaseSavepoint(_) => "RELEASE",
193 TransactionStmt::SetCharacteristics(_)
194 | TransactionStmt::SetSessionCharacteristics(_)
195 | TransactionStmt::SetSnapshot(_) => "SET",
196 }
197}
198
199const fn alter_routine_completion(kind: AlterRoutineKind) -> &'static str {
200 match kind {
201 AlterRoutineKind::Function => "ALTER FUNCTION",
202 AlterRoutineKind::Procedure => "ALTER PROCEDURE",
203 AlterRoutineKind::Routine => "ALTER ROUTINE",
204 }
205}
206
207const fn grant_completion(is_grant: bool) -> &'static str {
208 if is_grant {
209 "GRANT"
210 } else {
211 "REVOKE"
212 }
213}