(ctx: &DispatchCtx<'_>, seq: u64, sql: &str)
| 395 | // ─── Explain ─────────────────────────────────────────────────────── |
| 396 | |
| 397 | async fn handle_explain(ctx: &DispatchCtx<'_>, seq: u64, sql: &str) -> NativeResponse { |
| 398 | let inner_sql = sql.strip_prefix("EXPLAIN ").unwrap_or(sql).trim(); |
| 399 | let inner_upper = inner_sql.to_uppercase(); |
| 400 | |
| 401 | if inner_upper.starts_with("CREATE ") |
| 402 | || inner_upper.starts_with("DROP ") |
| 403 | || inner_upper.starts_with("ALTER ") |
| 404 | || inner_upper.starts_with("SHOW ") |
| 405 | || inner_upper.starts_with("SEARCH ") |
| 406 | { |
| 407 | return NativeResponse { |
| 408 | seq, |
| 409 | status: nodedb_types::protocol::ResponseStatus::Ok, |
| 410 | columns: Some(vec!["plan".into()]), |
| 411 | rows: Some(vec![vec![Value::String(format!("DDL: {inner_sql}"))]]), |
| 412 | rows_affected: None, |
| 413 | watermark_lsn: 0, |
| 414 | error: None, |
| 415 | auth: None, |
| 416 | warnings: Vec::new(), |
| 417 | }; |
| 418 | } |
| 419 | |
| 420 | let perm_cache = ctx.state.permission_cache.read().await; |
| 421 | let sec = crate::control::planner::context::PlanSecurityContext { |
| 422 | identity: ctx.identity, |
| 423 | auth: ctx.auth_context, |
| 424 | rls_store: &ctx.state.rls, |
| 425 | permissions: &ctx.state.permissions, |
| 426 | roles: &ctx.state.roles, |
| 427 | permission_cache: Some(&*perm_cache), |
| 428 | }; |
| 429 | let database_id = ctx |
| 430 | .sessions |
| 431 | .get_current_database(ctx.peer_addr) |
| 432 | .unwrap_or(crate::types::DatabaseId::DEFAULT); |
| 433 | match ctx |
| 434 | .query_ctx |
| 435 | .plan_sql_with_rls(inner_sql, ctx.tenant_id(), database_id, &sec) |
| 436 | .await |
| 437 | { |
| 438 | Ok(tasks) => { |
| 439 | let plan_text = tasks |
| 440 | .iter() |
| 441 | .map(|t| format!("{:?}", t.plan)) |
| 442 | .collect::<Vec<_>>() |
| 443 | .join("\n"); |
| 444 | NativeResponse { |
| 445 | seq, |
| 446 | status: nodedb_types::protocol::ResponseStatus::Ok, |
| 447 | columns: Some(vec!["plan".into()]), |
| 448 | rows: Some(vec![vec![Value::String(plan_text)]]), |
| 449 | rows_affected: None, |
| 450 | watermark_lsn: 0, |
| 451 | error: None, |
| 452 | auth: None, |
| 453 | warnings: Vec::new(), |
| 454 | } |
no test coverage detected