diff --git a/bindings/javascript/packages/wasm/promise.test.ts b/bindings/javascript/packages/wasm/promise.test.ts index 77176d9f9..d80dee8b7 100644 --- a/bindings/javascript/packages/wasm/promise.test.ts +++ b/bindings/javascript/packages/wasm/promise.test.ts @@ -1,6 +1,97 @@ import { expect, test } from 'vitest' import { connect, Database } from './promise-default.js' +test('explain', async () => { + const db = await connect(":memory:"); + const stmt = db.prepare("EXPLAIN SELECT 1"); + expect(stmt.columns()).toEqual([ + { + "name": "addr", + "type": "INTEGER", + }, + { + "name": "opcode", + "type": "TEXT", + }, + { + "name": "p1", + "type": "INTEGER", + }, + { + "name": "p2", + "type": "INTEGER", + }, + { + "name": "p3", + "type": "INTEGER", + }, + { + "name": "p4", + "type": "INTEGER", + }, + { + "name": "p5", + "type": "INTEGER", + }, + { + "name": "comment", + "type": "TEXT", + }, + ].map(x => ({ ...x, column: null, database: null, table: null }))); + expect(await stmt.all()).toEqual([ + { + "addr": 0, + "comment": "Start at 3", + "opcode": "Init", + "p1": 0, + "p2": 3, + "p3": 0, + "p4": "", + "p5": 0, + }, + { + "addr": 1, + "comment": "output=r[1]", + "opcode": "ResultRow", + "p1": 1, + "p2": 1, + "p3": 0, + "p4": "", + "p5": 0, + }, + { + "addr": 2, + "comment": "", + "opcode": "Halt", + "p1": 0, + "p2": 0, + "p3": 0, + "p4": "", + "p5": 0, + }, + { + "addr": 3, + "comment": "r[1]=1", + "opcode": "Integer", + "p1": 1, + "p2": 1, + "p3": 0, + "p4": "", + "p5": 0, + }, + { + "addr": 4, + "comment": "", + "opcode": "Goto", + "p1": 0, + "p2": 1, + "p3": 0, + "p4": "", + "p5": 0, + }, + ]); +}) + test('in-memory db', async () => { const db = await connect(":memory:"); await db.exec("CREATE TABLE t(x)"); @@ -10,6 +101,7 @@ test('in-memory db', async () => { expect(rows).toEqual([{ x: 1 }, { x: 3 }]); }) + test('implicit connect', async () => { const db = new Database(':memory:'); const defer = db.prepare("SELECT * FROM t"); diff --git a/core/lib.rs b/core/lib.rs index 211876d89..cf51ae283 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -46,6 +46,7 @@ use crate::translate::pragma::TURSO_CDC_DEFAULT_TABLE_NAME; use crate::types::{WalFrameInfo, WalState}; #[cfg(feature = "fs")] use crate::util::{OpenMode, OpenOptions}; +use crate::vdbe::explain::{EXPLAIN_COLUMNS_TYPE, EXPLAIN_QUERY_PLAN_COLUMNS_TYPE}; use crate::vdbe::metrics::ConnectionMetrics; use crate::vtab::VirtualTable; use crate::{incremental::view::AllViewsTxState, translate::emitter::TransactionMode}; @@ -2667,6 +2668,17 @@ impl Statement { } pub fn get_column_name(&self, idx: usize) -> Cow<'_, str> { + if self.query_mode == QueryMode::Explain { + return Cow::Owned(EXPLAIN_COLUMNS.get(idx).expect("No column").to_string()); + } + if self.query_mode == QueryMode::ExplainQueryPlan { + return Cow::Owned( + EXPLAIN_QUERY_PLAN_COLUMNS + .get(idx) + .expect("No column") + .to_string(), + ); + } match self.query_mode { QueryMode::Normal => { let column = &self.program.result_columns.get(idx).expect("No column"); @@ -2685,6 +2697,9 @@ impl Statement { } pub fn get_column_table_name(&self, idx: usize) -> Option> { + if self.query_mode == QueryMode::Explain || self.query_mode == QueryMode::ExplainQueryPlan { + return None; + } let column = &self.program.result_columns.get(idx).expect("No column"); match &column.expr { turso_parser::ast::Expr::Column { table, .. } => self @@ -2697,6 +2712,22 @@ impl Statement { } pub fn get_column_type(&self, idx: usize) -> Option { + if self.query_mode == QueryMode::Explain { + return Some( + EXPLAIN_COLUMNS_TYPE + .get(idx) + .expect("No column") + .to_string(), + ); + } + if self.query_mode == QueryMode::ExplainQueryPlan { + return Some( + EXPLAIN_QUERY_PLAN_COLUMNS_TYPE + .get(idx) + .expect("No column") + .to_string(), + ); + } let column = &self.program.result_columns.get(idx).expect("No column"); match &column.expr { turso_parser::ast::Expr::Column { diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index f480a8a4c..5e8dde2fe 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -6,7 +6,11 @@ use super::{Insn, InsnReference, Program, Value}; use crate::function::{Func, ScalarFunc}; pub const EXPLAIN_COLUMNS: [&str; 8] = ["addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment"]; +pub const EXPLAIN_COLUMNS_TYPE: [&str; 8] = [ + "INTEGER", "TEXT", "INTEGER", "INTEGER", "INTEGER", "INTEGER", "INTEGER", "TEXT", +]; pub const EXPLAIN_QUERY_PLAN_COLUMNS: [&str; 4] = ["id", "parent", "notused", "detail"]; +pub const EXPLAIN_QUERY_PLAN_COLUMNS_TYPE: [&str; 4] = ["INTEGER", "INTEGER", "INTEGER", "TEXT"]; pub fn insn_to_row( program: &Program,