(
&mut self,
stmt: Arc<Statement<Raw>>,
params: Params,
mut ctx: ExecuteContext,
)
| 1167 | |
| 1168 | #[instrument(name = "coord::handle_execute_inner", fields(stmt = stmt.to_ast_string_redacted()))] |
| 1169 | pub(crate) async fn handle_execute_inner( |
| 1170 | &mut self, |
| 1171 | stmt: Arc<Statement<Raw>>, |
| 1172 | params: Params, |
| 1173 | mut ctx: ExecuteContext, |
| 1174 | ) { |
| 1175 | // This comment describes the various ways DDL can execute (the ordered operations: name |
| 1176 | // resolve, purify, plan, sequence), all of which are managed by this function. DDL has |
| 1177 | // three notable properties that all partially interact. |
| 1178 | // |
| 1179 | // 1. Most DDL statements (and a few others) support single-statement transaction delayed |
| 1180 | // execution. This occurs when a session executes `BEGIN`, a single DDL, then `COMMIT`. |
| 1181 | // We announce success of the single DDL when it is executed, but do not attempt to plan |
| 1182 | // or sequence it until `COMMIT`, which is able to error if needed while sequencing the |
| 1183 | // DDL (this behavior is Postgres-compatible). The purpose of this is because some |
| 1184 | // drivers or tools wrap all statements in `BEGIN` and `COMMIT` and we would like them to |
| 1185 | // work. When the single DDL is announced as successful we also put the session's |
| 1186 | // transaction ops into `SingleStatement` which will produce an error if any other |
| 1187 | // statement is run in the transaction except `COMMIT`. Additionally, this will cause |
| 1188 | // `handle_execute_inner` to stop further processing (no planning, etc.) of the |
| 1189 | // statement. |
| 1190 | // 2. A few other DDL statements (`ALTER .. RENAME/SWAP`) enter the `DDL` ops which allows |
| 1191 | // any number of only these DDL statements to be executed in a transaction. During |
| 1192 | // sequencing we run an incremental catalog dry run against in-memory transaction state |
| 1193 | // and store the resulting `CatalogState` in `TransactionOps::DDL`, but nothing is yet |
| 1194 | // committed to the durable catalog. At `COMMIT`, all accumulated ops are applied in one |
| 1195 | // catalog transaction. The purpose of this is to allow multiple, atomic renames in the |
| 1196 | // same transaction. |
| 1197 | // 3. Some DDLs do off-thread work during purification or sequencing that is expensive or |
| 1198 | // makes network calls (interfacing with secrets, optimization of views/indexes, source |
| 1199 | // purification). These must guarantee correctness when they return to the main |
| 1200 | // coordinator thread because the catalog state could have changed while they were doing |
| 1201 | // the off-thread work. Previously we would use `PlanValidity::Checks` to specify a bunch |
| 1202 | // of IDs that we needed to exist. We discovered the way we were doing that was not |
| 1203 | // always correct. Instead of attempting to get that completely right, we have opted to |
| 1204 | // serialize DDL. Getting this right is difficult because catalog changes can affect name |
| 1205 | // resolution, planning, sequencing, and optimization. Correctly writing logic that is |
| 1206 | // aware of all possible catalog changes that would affect any of those parts is not |
| 1207 | // something our current code has been designed to be helpful at. Even if a DDL statement |
| 1208 | // is doing off-thread work, another DDL must not yet execute at all. Executing these |
| 1209 | // serially will guarantee that no off-thread work has affected the state of the catalog. |
| 1210 | // This is done by adding a VecDeque of deferred statements and a lock to the |
| 1211 | // Coordinator. When a DDL is run in `handle_execute_inner` (after applying whatever |
| 1212 | // transaction ops are needed to the session as described above), it attempts to own the |
| 1213 | // lock (a tokio Mutex). If acquired, it stashes the lock in the connection`s `ConnMeta` |
| 1214 | // struct in `active_conns` and proceeds. The lock is dropped at transaction end in |
| 1215 | // `clear_transaction` and a message sent to the Coordinator to execute the next queued |
| 1216 | // DDL. If the lock could not be acquired, the DDL is put into the VecDeque where it |
| 1217 | // awaits dequeuing caused by the lock being released. |
| 1218 | |
| 1219 | // Verify that this statement type can be executed in the current |
| 1220 | // transaction state. |
| 1221 | match ctx.session().transaction() { |
| 1222 | // By this point we should be in a running transaction. |
| 1223 | TransactionStatus::Default => unreachable!(), |
| 1224 | |
| 1225 | // Failed transactions have already been checked in pgwire for a safe statement |
| 1226 | // (COMMIT, ROLLBACK, etc.) and can proceed. |
no test coverage detected