This commit is contained in:
parent
5ae8b48845
commit
63f4dce705
46
Cargo.lock
generated
46
Cargo.lock
generated
@ -11,6 +11,24 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "2.0.0-rc.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f609ceb2c41b0d0277314a789ef0e7eb14593d5485f7c67320bed3924ebb1b33"
|
||||||
|
dependencies = [
|
||||||
|
"bincode_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode_derive"
|
||||||
|
version = "2.0.0-rc.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "913287a8f3e00db4c7ae1b87e9b9b8cebd6b89217eaadfc281fa5c897da35dc3"
|
||||||
|
dependencies = [
|
||||||
|
"virtue",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_format"
|
name = "const_format"
|
||||||
version = "0.2.22"
|
version = "0.2.22"
|
||||||
@ -78,10 +96,32 @@ version = "0.6.25"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.136"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.136"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlite_clone"
|
name = "sqlite_clone"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bincode",
|
||||||
|
"serde",
|
||||||
"sscanf",
|
"sscanf",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -125,3 +165,9 @@ name = "unicode-xid"
|
|||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtue"
|
||||||
|
version = "0.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "757cfbfe0d17ee6f22fe97e536d463047d451b47cf9d11e2b7d1398b0ef274dd"
|
||||||
|
@ -7,3 +7,5 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sscanf = {version="0.2.1"}
|
sscanf = {version="0.2.1"}
|
||||||
|
bincode = "2.0.0-rc.1"
|
||||||
|
serde = {version="1.0.136", features=["derive"]}
|
||||||
|
31
src/main.rs
31
src/main.rs
@ -1,18 +1,25 @@
|
|||||||
|
mod page;
|
||||||
|
mod pager;
|
||||||
|
mod row;
|
||||||
mod statement;
|
mod statement;
|
||||||
|
mod table;
|
||||||
|
|
||||||
|
use crate::statement::ExecuteResult;
|
||||||
|
use crate::table::Table;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
struct InputBuffer {
|
struct InputBuffer {
|
||||||
buffer: Option<String>,
|
buffer: Option<String>,
|
||||||
|
table: Table,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl InputBuffer {
|
impl InputBuffer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
return Self {
|
return Self {
|
||||||
buffer: None
|
buffer: None,
|
||||||
|
table: Table::new(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,11 +41,11 @@ impl InputBuffer {
|
|||||||
Err(String::from("could not handle input"))
|
Err(String::from("could not handle input"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => { Err(String::from("Could not initialize buffer")) }
|
_ => Err(String::from("Could not initialize buffer")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse(&self) {
|
pub(crate) fn parse(&mut self) {
|
||||||
match &self.buffer {
|
match &self.buffer {
|
||||||
Some(command) => {
|
Some(command) => {
|
||||||
if command.starts_with(".") {
|
if command.starts_with(".") {
|
||||||
@ -46,7 +53,15 @@ impl InputBuffer {
|
|||||||
} else {
|
} else {
|
||||||
match Self::prepare_statement(&command.replace("\n", "")) {
|
match Self::prepare_statement(&command.replace("\n", "")) {
|
||||||
Ok(statement) => {
|
Ok(statement) => {
|
||||||
statement.execute()
|
let execution_result = statement.execute(&mut self.table);
|
||||||
|
match execution_result {
|
||||||
|
ExecuteResult::Success => {
|
||||||
|
println!("success")
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}", e);
|
println!("{}", e);
|
||||||
@ -74,14 +89,10 @@ impl InputBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
let mut input_buffer = InputBuffer::new();
|
let mut input_buffer = InputBuffer::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
input_buffer
|
input_buffer.print_prompt().read_input()?.parse()
|
||||||
.print_prompt()
|
|
||||||
.read_input()?
|
|
||||||
.parse()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
src/page.rs
Normal file
28
src/page.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Page {
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Page {
|
||||||
|
pub fn new(buffer: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Page {
|
||||||
|
type Target = Vec<u8>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Page {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.buffer
|
||||||
|
}
|
||||||
|
}
|
35
src/pager.rs
Normal file
35
src/pager.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crate::page::Page;
|
||||||
|
|
||||||
|
pub struct Pager {
|
||||||
|
pages: Vec<Option<Page>>,
|
||||||
|
page_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pager {
|
||||||
|
pub fn new(page_size: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
page_size,
|
||||||
|
pages: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut_or_alloc(&mut self, page_num: usize) -> &mut Page {
|
||||||
|
if page_num >= self.pages.len() {
|
||||||
|
self.grow_pages(page_num)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.pages[page_num].is_none() {
|
||||||
|
let buf = vec![0; self.page_size];
|
||||||
|
let page = Page::new(buf);
|
||||||
|
self.pages[page_num] = Some(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pages[page_num].as_mut().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn grow_pages(&mut self, index: usize) {
|
||||||
|
println!("debug: Growing pages");
|
||||||
|
self.pages.resize(index + 1, None)
|
||||||
|
}
|
||||||
|
}
|
84
src/row.rs
Normal file
84
src/row.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use std::{iter, slice};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use bincode::{Decode, Encode};
|
||||||
|
use bincode::config::Configuration;
|
||||||
|
use bincode::error::{DecodeError, EncodeError};
|
||||||
|
|
||||||
|
const COLUMN_USERNAME_SIZE: usize = 32;
|
||||||
|
const COLUMN_EMAIL_SIZE: usize = 255;
|
||||||
|
|
||||||
|
#[derive(Encode, Decode)]
|
||||||
|
pub struct Row {
|
||||||
|
pub id: u32,
|
||||||
|
pub username: [u8; COLUMN_USERNAME_SIZE],
|
||||||
|
pub email: [u8; COLUMN_EMAIL_SIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Serialize {
|
||||||
|
fn serialize(&self) -> Result<Vec<u8>, EncodeError>;
|
||||||
|
fn deserialize(buffer: &[u8]) -> Result<Self, DecodeError>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Row {
|
||||||
|
fn serialize(&self) -> Result<Vec<u8>, EncodeError> {
|
||||||
|
bincode::encode_to_vec(self, bincode::config::standard())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(buffer: &[u8]) -> Result<Self, DecodeError> {
|
||||||
|
match bincode::decode_from_slice::<Self, Configuration>(buffer, bincode::config::standard())
|
||||||
|
{
|
||||||
|
Ok((row, _)) => Ok(row),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Row {
|
||||||
|
pub fn new(id: u32, username: String, email: String) -> Result<Self, String> {
|
||||||
|
let username_bytes = username.as_bytes();
|
||||||
|
let email_bytes = email.as_bytes();
|
||||||
|
if username_bytes.len() > COLUMN_USERNAME_SIZE {
|
||||||
|
Err(String::from("username is too long"))
|
||||||
|
} else if email_bytes.len() > COLUMN_EMAIL_SIZE {
|
||||||
|
Err(String::from("email is too long"))
|
||||||
|
} else {
|
||||||
|
let username_buffer = &mut [0; COLUMN_USERNAME_SIZE];
|
||||||
|
if let Err(_) = pad_buffer(username_buffer, username_bytes) {
|
||||||
|
panic!("could not pad username buffer")
|
||||||
|
}
|
||||||
|
let email_buffer = &mut [0; COLUMN_EMAIL_SIZE];
|
||||||
|
if let Err(_) = pad_buffer(email_buffer, username_bytes) {
|
||||||
|
panic!("could not pad username buffer")
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
id,
|
||||||
|
username: *username_buffer,
|
||||||
|
email: *email_buffer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(crate) fn print(&self) {
|
||||||
|
println!("({}, {}, {})", self.id, String::from_utf8(self.email.to_vec()).unwrap(), String::from_utf8(self.username.to_vec()).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_buffer(mut bytes: &mut [u8], source: &[u8]) -> std::io::Result<usize> {
|
||||||
|
bytes.write(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RowIter<'a> {
|
||||||
|
inner: slice::Iter<'a, Row>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> iter::Iterator for RowIter<'a> {
|
||||||
|
type Item = &'a Row;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.inner.next()
|
||||||
|
}
|
||||||
|
}
|
@ -1,50 +1,16 @@
|
|||||||
use std::io::Write;
|
use crate::row::Serialize;
|
||||||
use std::mem::size_of;
|
use crate::table::Table;
|
||||||
|
use crate::{row};
|
||||||
|
|
||||||
|
|
||||||
pub enum StatementType {
|
pub enum StatementType {
|
||||||
Insert {
|
Insert { row: row::Row },
|
||||||
row: Row
|
|
||||||
},
|
|
||||||
Select,
|
Select,
|
||||||
}
|
}
|
||||||
|
|
||||||
const COLUMN_USERNAME_SIZE: usize = 32;
|
pub enum ExecuteResult {
|
||||||
const COLUMN_EMAIL_SIZE: usize = 255;
|
Error { error: String },
|
||||||
|
Success,
|
||||||
pub struct Row {
|
|
||||||
id: u32,
|
|
||||||
username: [u8; COLUMN_USERNAME_SIZE],
|
|
||||||
email: [u8; COLUMN_EMAIL_SIZE],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Row {
|
|
||||||
fn new(id: u32, username: String, email: String) -> Result<Self, String> {
|
|
||||||
let username_bytes = username.as_bytes();
|
|
||||||
let email_bytes = email.as_bytes();
|
|
||||||
if username_bytes.len() > COLUMN_USERNAME_SIZE {
|
|
||||||
Err(String::from("username is too long"))
|
|
||||||
} else if email_bytes.len() > COLUMN_EMAIL_SIZE {
|
|
||||||
Err(String::from("email is too long"))
|
|
||||||
} else {
|
|
||||||
let username_buffer = &mut [0; COLUMN_USERNAME_SIZE];
|
|
||||||
if let Err(_) = pad_buffer(username_buffer, username_bytes) {
|
|
||||||
panic!("could not pad username buffer")
|
|
||||||
}
|
|
||||||
let email_buffer = &mut [0; COLUMN_EMAIL_SIZE];
|
|
||||||
if let Err(_) = pad_buffer(email_buffer, username_bytes) {
|
|
||||||
panic!("could not pad username buffer")
|
|
||||||
}
|
|
||||||
Ok(Self {
|
|
||||||
id,
|
|
||||||
username: *username_buffer,
|
|
||||||
email: *email_buffer,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pad_buffer(mut bytes: &mut [u8], source: &[u8]) -> std::io::Result<usize> {
|
|
||||||
bytes.write(source)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Statement {
|
pub struct Statement {
|
||||||
@ -53,43 +19,50 @@ pub struct Statement {
|
|||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
pub fn new(statement_type: StatementType) -> Self {
|
pub fn new(statement_type: StatementType) -> Self {
|
||||||
Self {
|
Self { statement_type }
|
||||||
statement_type,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_statement(command: &String) -> Result<Self, String> {
|
pub fn parse_statement(command: &String) -> Result<Self, String> {
|
||||||
if command.starts_with("insert") {
|
if command.starts_with("insert") {
|
||||||
match sscanf::scanf!(command, "insert {} {} {}", usize, str, str) {
|
match sscanf::scanf!(command, "insert {} {} {}", usize, str, str) {
|
||||||
Ok((id, username, email)) => {
|
Ok((id, username, email)) => {
|
||||||
match Row::new(u32::try_from(id).unwrap(), username.to_string(), email.to_string()) {
|
match row::Row::new(
|
||||||
Ok(row) => {
|
u32::try_from(id).unwrap(),
|
||||||
Ok(Statement::new(StatementType::Insert { row: row }))
|
username.to_string(),
|
||||||
}
|
email.to_string(),
|
||||||
Err(e) => { Err(e) }
|
) {
|
||||||
|
Ok(row) => Ok(Statement::new(StatementType::Insert { row })),
|
||||||
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Err(String::from("could not parse insert statement")),
|
||||||
Err(String::from("could not parse insert statement"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if command.starts_with("select") {
|
} else if command.starts_with("select") {
|
||||||
Ok(Statement::new(StatementType::Select))
|
Ok(Statement::new(StatementType::Select))
|
||||||
} else {
|
} else {
|
||||||
return Err(String::from("Could not parse command"));
|
Err(String::from("Could not parse command"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn execute(&self) {
|
pub(crate) fn execute(&self, table: &mut Table) -> ExecuteResult {
|
||||||
match &self.statement_type {
|
match &self.statement_type {
|
||||||
StatementType::Insert { row } => {
|
StatementType::Insert { row } => match row.serialize() {
|
||||||
println!("This is where you do an insert {}", row.id)
|
Err(e) => ExecuteResult::Error {
|
||||||
|
error: e.to_string(),
|
||||||
|
},
|
||||||
|
Ok(serialization) => {
|
||||||
|
table.append_row(serialization);
|
||||||
|
ExecuteResult::Success
|
||||||
}
|
}
|
||||||
|
},
|
||||||
StatementType::Select => {
|
StatementType::Select => {
|
||||||
println!("This is where you do an select")
|
let rows = table.get_rows();
|
||||||
}
|
for row in rows {
|
||||||
}
|
row.print()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ExecuteResult::Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
61
src/table.rs
Normal file
61
src/table.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use crate::pager::Pager;
|
||||||
|
use crate::row;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
use crate::row::Row;
|
||||||
|
use crate::row::Serialize;
|
||||||
|
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
pub struct Table {
|
||||||
|
num_rows: usize,
|
||||||
|
pager: Pager,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Table {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
num_rows: 0,
|
||||||
|
pager: Pager::new(PAGE_SIZE),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn append_row(&mut self, row: Vec<u8>) {
|
||||||
|
let row_size = size_of::<row::Row>();
|
||||||
|
let page_num = self.num_rows / self.rows_per_page(row_size);
|
||||||
|
let row_offset = self.num_rows % self.rows_per_page(row_size);
|
||||||
|
|
||||||
|
let page = self.pager.get_mut_or_alloc(page_num);
|
||||||
|
|
||||||
|
let byte_offset = row_offset * row_size;
|
||||||
|
|
||||||
|
page.deref_mut()
|
||||||
|
.splice(byte_offset..byte_offset + row_size, row);
|
||||||
|
self.num_rows += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn row(&mut self, row_num: usize) -> Row {
|
||||||
|
let row_size = size_of::<row::Row>();
|
||||||
|
let page_num = row_num / self.rows_per_page(row_size);
|
||||||
|
let row_offset = row_num % self.rows_per_page(row_size);
|
||||||
|
let byte_offset = row_offset * row_size;
|
||||||
|
|
||||||
|
let page = self.pager.get_mut_or_alloc(page_num);
|
||||||
|
|
||||||
|
Row::deserialize(&page[byte_offset..byte_offset + row_size]).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn rows_per_page(&self, row_size: usize) -> usize {
|
||||||
|
PAGE_SIZE / row_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_rows(&mut self) -> Vec<Row> {
|
||||||
|
let mut rows: Vec<Row> = Vec::with_capacity(self.num_rows);
|
||||||
|
for i in 0..self.num_rows {
|
||||||
|
rows.push(self.row(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user