mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-07 01:04:26 +01:00
Implement ResultRow opcode
This commit is contained in:
114
Cargo.lock
generated
114
Cargo.lock
generated
@@ -120,7 +120,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -129,6 +129,29 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||
|
||||
[[package]]
|
||||
name = "cli-table"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adfbb116d9e2c4be7011360d0c0bee565712c11e969c9609b25b619366dc379d"
|
||||
dependencies = [
|
||||
"cli-table-derive",
|
||||
"csv",
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cli-table-derive"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af3bfb9da627b0a6c467624fb7963921433774ed435493b5c08a3053e829ad4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "4.5.0"
|
||||
@@ -146,6 +169,27 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
|
||||
dependencies = [
|
||||
"csv-core",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
@@ -269,6 +313,12 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
@@ -281,6 +331,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"cli-table",
|
||||
"dirs",
|
||||
"lig_core",
|
||||
"rustyline",
|
||||
@@ -493,12 +544,38 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
@@ -542,6 +619,17 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.29"
|
||||
@@ -553,6 +641,15 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.47"
|
||||
@@ -570,7 +667,7 @@ checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -639,7 +736,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.29",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -661,7 +758,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.29",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -688,6 +785,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
||||
@@ -17,6 +17,7 @@ path = "main.rs"
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
clap = { version = "4.4.0", features = ["derive"] }
|
||||
cli-table = "0.4.7"
|
||||
dirs = "5.0.1"
|
||||
lig_core = { path = "../core" }
|
||||
rustyline = "12.0.0"
|
||||
|
||||
32
cli/main.rs
32
cli/main.rs
@@ -1,6 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use lig_core::{Database, DatabaseRef};
|
||||
use cli_table::{Cell, Table};
|
||||
use lig_core::{Database, DatabaseRef, Value};
|
||||
use rustyline::{error::ReadlineError, DefaultEditor};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
@@ -31,10 +32,31 @@ fn main() -> anyhow::Result<()> {
|
||||
let readline = rl.readline("\x1b[90m>\x1b[0m ");
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
if let Err(err) = conn.execute(&line) {
|
||||
eprintln!("{}", err);
|
||||
} else {
|
||||
rl.add_history_entry(line)?;
|
||||
rl.add_history_entry(line.to_owned())?;
|
||||
match conn.query(line) {
|
||||
Ok(Some(ref mut rows)) => {
|
||||
let mut table_rows: Vec<Vec<_>> = vec![];
|
||||
while let Some(row) = rows.next()? {
|
||||
table_rows.push(
|
||||
row.values
|
||||
.iter()
|
||||
.map(|value| match value {
|
||||
Value::Null => "NULL".cell(),
|
||||
Value::Integer(i) => i.to_string().cell(),
|
||||
Value::Float(f) => f.to_string().cell(),
|
||||
Value::Text(s) => s.cell(),
|
||||
Value::Blob(b) => format!("{:?}", b).cell(),
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
let table = table_rows.table();
|
||||
cli_table::print_stdout(table).unwrap();
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::pager::Pager;
|
||||
use crate::sqlite3_ondisk::{BTreeCell, BTreePage, Record, TableLeafCell};
|
||||
use crate::sqlite3_ondisk::{BTreeCell, BTreePage, TableLeafCell};
|
||||
use crate::types::Record;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
|
||||
55
core/lib.rs
55
core/lib.rs
@@ -3,6 +3,7 @@ mod buffer_pool;
|
||||
mod pager;
|
||||
mod schema;
|
||||
mod sqlite3_ondisk;
|
||||
mod types;
|
||||
mod vdbe;
|
||||
|
||||
use anyhow::Result;
|
||||
@@ -12,6 +13,8 @@ use schema::Schema;
|
||||
use sqlite3_parser::{ast::Cmd, lexer::sql::Parser};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use types::Value;
|
||||
|
||||
pub struct Database {
|
||||
pager: Arc<Pager>,
|
||||
schema: Arc<Schema>,
|
||||
@@ -43,7 +46,7 @@ pub struct Connection {
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub fn query(&self, sql: impl Into<String>) -> Result<()> {
|
||||
pub fn query(&self, sql: impl Into<String>) -> Result<Option<Rows>> {
|
||||
let sql = sql.into();
|
||||
let mut parser = Parser::new(sql.as_bytes());
|
||||
let cmd = parser.next()?;
|
||||
@@ -53,28 +56,14 @@ impl Connection {
|
||||
let program = vdbe::translate(&self.schema, stmt)?;
|
||||
let mut state =
|
||||
vdbe::ProgramState::new(self.pager.clone(), program.max_registers);
|
||||
loop {
|
||||
let result = program.step(&mut state)?;
|
||||
match result {
|
||||
vdbe::StepResult::Row => {
|
||||
let mut row = Vec::new();
|
||||
for i in 0..state.column_count() {
|
||||
row.push(state.column(i).unwrap().to_string());
|
||||
}
|
||||
log::trace!("Row = {:?}", row);
|
||||
}
|
||||
vdbe::StepResult::IO => todo!(),
|
||||
vdbe::StepResult::Done => break,
|
||||
}
|
||||
}
|
||||
Ok(Some(Rows::new(state, program)))
|
||||
}
|
||||
Cmd::Explain(_stmt) => {
|
||||
todo!();
|
||||
}
|
||||
Cmd::ExplainQueryPlan(_stmt) => todo!(),
|
||||
Cmd::Explain(_stmt) => Ok(None),
|
||||
Cmd::ExplainQueryPlan(_stmt) => Ok(None),
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn execute(&self, sql: impl Into<String>) -> Result<()> {
|
||||
@@ -100,6 +89,32 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rows {
|
||||
state: vdbe::ProgramState,
|
||||
program: vdbe::Program,
|
||||
}
|
||||
|
||||
impl Rows {
|
||||
pub fn new(state: vdbe::ProgramState, program: vdbe::Program) -> Self {
|
||||
Self { state, program }
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Result<Option<crate::types::Record>> {
|
||||
loop {
|
||||
let result = self.program.step(&mut self.state)?;
|
||||
match result {
|
||||
vdbe::StepResult::Row(row) => {
|
||||
return Ok(Some(row));
|
||||
}
|
||||
vdbe::StepResult::IO => todo!(),
|
||||
vdbe::StepResult::Done => {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type DatabaseRef = usize;
|
||||
|
||||
pub trait IO {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
///
|
||||
/// For more information, see: https://www.sqlite.org/fileformat.html
|
||||
use crate::buffer_pool::BufferPool;
|
||||
use crate::types::{Record, Value};
|
||||
use crate::{DatabaseRef, IO};
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::borrow::BorrowMut;
|
||||
@@ -202,20 +203,6 @@ pub fn read_btree_cell(page: &[u8], page_type: &PageType, pos: usize) -> Result<
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
Null,
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Text(String),
|
||||
Blob(Vec<u8>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Record {
|
||||
pub values: Vec<Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SerialType {
|
||||
Null,
|
||||
|
||||
19
core/types.rs
Normal file
19
core/types.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
Null,
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Text(String),
|
||||
Blob(Vec<u8>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Record {
|
||||
pub values: Vec<Value>,
|
||||
}
|
||||
|
||||
impl Record {
|
||||
pub fn new(values: Vec<Value>) -> Self {
|
||||
Self { values }
|
||||
}
|
||||
}
|
||||
59
core/vdbe.rs
59
core/vdbe.rs
@@ -1,7 +1,7 @@
|
||||
use crate::btree::Cursor;
|
||||
use crate::pager::Pager;
|
||||
use crate::schema::Schema;
|
||||
use crate::sqlite3_ondisk::Value;
|
||||
use crate::types::{Record, Value};
|
||||
|
||||
use anyhow::Result;
|
||||
use sqlite3_parser::ast::{OneSelect, Select, Stmt};
|
||||
@@ -49,8 +49,8 @@ pub enum Insn {
|
||||
|
||||
// Emit a row of results.
|
||||
ResultRow {
|
||||
// FIXME: This is incorrect, it should be reading from registers.
|
||||
cursor_id: CursorID,
|
||||
register_start: usize,
|
||||
register_end: usize,
|
||||
},
|
||||
|
||||
// Advance the cursor to the next row.
|
||||
@@ -101,6 +101,10 @@ impl ProgramBuilder {
|
||||
reg
|
||||
}
|
||||
|
||||
pub fn next_free_register(&self) -> usize {
|
||||
self.next_free_register
|
||||
}
|
||||
|
||||
pub fn emit_placeholder(&mut self) -> usize {
|
||||
let offset = self.insns.len();
|
||||
self.insns.push(Insn::Halt);
|
||||
@@ -130,7 +134,7 @@ impl ProgramBuilder {
|
||||
pub enum StepResult {
|
||||
Done,
|
||||
IO,
|
||||
Row,
|
||||
Row(Record),
|
||||
}
|
||||
|
||||
/// The program state describes the environment in which the program executes.
|
||||
@@ -231,11 +235,16 @@ impl Program {
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::ResultRow { cursor_id } => {
|
||||
let cursor = state.cursors.get_mut(cursor_id).unwrap();
|
||||
let _ = cursor.record()?;
|
||||
Insn::ResultRow {
|
||||
register_start,
|
||||
register_end,
|
||||
} => {
|
||||
let mut values = vec![];
|
||||
for i in *register_start..*register_end {
|
||||
values.push(state.registers[i].clone().unwrap());
|
||||
}
|
||||
state.pc += 1;
|
||||
return Ok(StepResult::Row);
|
||||
return Ok(StepResult::Row(Record::new(values)));
|
||||
}
|
||||
Insn::NextAsync { cursor_id } => {
|
||||
let cursor = state.cursors.get_mut(cursor_id).unwrap();
|
||||
@@ -309,8 +318,12 @@ fn translate_select(schema: &Schema, select: Select) -> Result<Program> {
|
||||
program.emit_insn(Insn::OpenReadAwait);
|
||||
program.emit_insn(Insn::RewindAsync { cursor_id });
|
||||
let rewind_await_offset = program.emit_placeholder();
|
||||
translate_columns(&mut program, Some(cursor_id), Some(table), columns);
|
||||
program.emit_insn(Insn::ResultRow { cursor_id });
|
||||
let (register_start, register_end) =
|
||||
translate_columns(&mut program, Some(cursor_id), Some(table), columns);
|
||||
program.emit_insn(Insn::ResultRow {
|
||||
register_start,
|
||||
register_end,
|
||||
});
|
||||
program.emit_insn(Insn::NextAsync { cursor_id });
|
||||
program.emit_insn(Insn::NextAwait {
|
||||
cursor_id,
|
||||
@@ -344,8 +357,12 @@ fn translate_select(schema: &Schema, select: Select) -> Result<Program> {
|
||||
let mut program = ProgramBuilder::new();
|
||||
let init_offset = program.emit_placeholder();
|
||||
let after_init_offset = program.offset();
|
||||
translate_columns(&mut program, None, None, columns);
|
||||
program.emit_insn(Insn::ResultRow { cursor_id: 0 });
|
||||
let (register_start, register_end) =
|
||||
translate_columns(&mut program, None, None, columns);
|
||||
program.emit_insn(Insn::ResultRow {
|
||||
register_start,
|
||||
register_end,
|
||||
});
|
||||
program.emit_insn(Insn::Halt);
|
||||
program.fixup_insn(
|
||||
init_offset,
|
||||
@@ -367,7 +384,8 @@ fn translate_columns(
|
||||
cursor_id: Option<usize>,
|
||||
table: Option<&crate::schema::Table>,
|
||||
columns: Vec<sqlite3_parser::ast::ResultColumn>,
|
||||
) {
|
||||
) -> (usize, usize) {
|
||||
let register_start = program.next_free_register();
|
||||
for col in columns {
|
||||
match col {
|
||||
sqlite3_parser::ast::ResultColumn::Expr(expr, _) => match expr {
|
||||
@@ -449,6 +467,8 @@ fn translate_columns(
|
||||
sqlite3_parser::ast::ResultColumn::TableStar(_) => todo!(),
|
||||
}
|
||||
}
|
||||
let register_end = program.next_free_register();
|
||||
(register_start, register_end)
|
||||
}
|
||||
|
||||
fn trace_insn(addr: usize, insn: &Insn) {
|
||||
@@ -503,7 +523,18 @@ fn insn_to_str(addr: usize, insn: &Insn) -> String {
|
||||
column,
|
||||
dest,
|
||||
} => ("Column", *cursor_id, *column, *dest, "", 0, "".to_string()),
|
||||
Insn::ResultRow { cursor_id } => ("ResultRow", *cursor_id, 0, 0, "", 0, "".to_string()),
|
||||
Insn::ResultRow {
|
||||
register_start,
|
||||
register_end,
|
||||
} => (
|
||||
"ResultRow",
|
||||
*register_start,
|
||||
*register_end,
|
||||
0,
|
||||
"",
|
||||
0,
|
||||
"".to_string(),
|
||||
),
|
||||
Insn::NextAsync { cursor_id } => ("NextAsync", *cursor_id, 0, 0, "", 0, "".to_string()),
|
||||
Insn::NextAwait {
|
||||
cursor_id,
|
||||
|
||||
Reference in New Issue
Block a user