Implement ResultRow opcode

This commit is contained in:
Pekka Enberg
2023-09-02 10:54:26 +03:00
parent 6e748a066e
commit 3ec9c0be7c
8 changed files with 240 additions and 58 deletions

114
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"

View File

@@ -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) => {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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
View 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 }
}
}

View File

@@ -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,