mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-27 04:54:21 +01:00
Merge pull request #95 from haaawk/schema
This commit is contained in:
122
core/schema.rs
122
core/schema.rs
@@ -1,7 +1,9 @@
|
||||
use crate::util::normalize_ident;
|
||||
use anyhow::Result;
|
||||
use core::fmt;
|
||||
use fallible_iterator::FallibleIterator;
|
||||
use log::trace;
|
||||
use sqlite3_parser::ast::TableOptions;
|
||||
use sqlite3_parser::{
|
||||
ast::{Cmd, CreateTableBody, QualifiedName, ResultColumn, Stmt},
|
||||
lexer::sql::Parser,
|
||||
@@ -9,8 +11,6 @@ use sqlite3_parser::{
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::util::normalize_ident;
|
||||
|
||||
pub struct Schema {
|
||||
pub tables: HashMap<String, Rc<BTreeTable>>,
|
||||
}
|
||||
@@ -72,12 +72,13 @@ pub struct BTreeTable {
|
||||
pub root_page: usize,
|
||||
pub name: String,
|
||||
pub columns: Vec<Column>,
|
||||
pub has_rowid: bool,
|
||||
}
|
||||
|
||||
impl BTreeTable {
|
||||
pub fn column_is_rowid_alias(&self, col: &Column) -> bool {
|
||||
let composite_primary_key = self.columns.iter().filter(|col| col.primary_key).count() > 1;
|
||||
col.primary_key && col.ty == Type::Integer && !composite_primary_key
|
||||
col.primary_key && col.ty == Type::Integer && !composite_primary_key && self.has_rowid
|
||||
}
|
||||
|
||||
pub fn get_column(&self, name: &str) -> Option<(usize, &Column)> {
|
||||
@@ -152,9 +153,14 @@ fn create_table(
|
||||
) -> Result<BTreeTable> {
|
||||
let table_name = normalize_ident(&tbl_name.name.0);
|
||||
trace!("Creating table {}", table_name);
|
||||
let mut has_rowid = true;
|
||||
let mut cols = vec![];
|
||||
match body {
|
||||
CreateTableBody::ColumnsAndConstraints { columns, .. } => {
|
||||
CreateTableBody::ColumnsAndConstraints {
|
||||
columns,
|
||||
constraints: _,
|
||||
options,
|
||||
} => {
|
||||
for column in columns {
|
||||
let name = column.col_name.0.to_string();
|
||||
let ty = match column.col_type {
|
||||
@@ -192,12 +198,16 @@ fn create_table(
|
||||
primary_key,
|
||||
});
|
||||
}
|
||||
if options.contains(TableOptions::WITHOUT_ROWID) {
|
||||
has_rowid = false;
|
||||
}
|
||||
}
|
||||
CreateTableBody::AsSelect(_) => todo!(),
|
||||
};
|
||||
Ok(BTreeTable {
|
||||
root_page,
|
||||
name: table_name,
|
||||
has_rowid,
|
||||
columns: cols,
|
||||
})
|
||||
}
|
||||
@@ -256,6 +266,7 @@ pub fn sqlite_schema_table() -> BTreeTable {
|
||||
BTreeTable {
|
||||
root_page: 1,
|
||||
name: "sqlite_schema".to_string(),
|
||||
has_rowid: true,
|
||||
columns: vec![
|
||||
Column {
|
||||
name: "type".to_string(),
|
||||
@@ -290,6 +301,109 @@ pub fn sqlite_schema_table() -> BTreeTable {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_has_rowid_true() -> Result<()> {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
assert!(table.has_rowid, "has_rowid should be set to true");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_has_rowid_false() -> Result<()> {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT) WITHOUT ROWID;"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
assert!(!table.has_rowid, "has_rowid should be set to false");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_column_is_rowid_alias_single_text() -> Result<()> {
|
||||
let sql = r#"CREATE TABLE t1 (a TEXT PRIMARY KEY, b TEXT);"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!table.column_is_rowid_alias(column),
|
||||
"column 'a´ has type different than INTEGER so can't be a rowid alias"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_column_is_rowid_alias_single_integer() -> Result<()> {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
table.column_is_rowid_alias(column),
|
||||
"column 'a´ should be a rowid alias"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // We don't support separate definition of primary keys yet
|
||||
pub fn test_column_is_rowid_alias_single_integer_separate_primary_key_definition() -> Result<()>
|
||||
{
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER, b TEXT, PRIMARY KEY(a));"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
table.column_is_rowid_alias(column),
|
||||
"column 'a´ should be a rowid alias"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_column_is_rowid_alias_single_integer_separate_primary_key_definition_without_rowid(
|
||||
) -> Result<()> {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER, b TEXT, PRIMARY KEY(a)) WITHOUT ROWID;"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!table.column_is_rowid_alias(column),
|
||||
"column 'a´ shouldn't be a rowid alias because table has no rowid"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_column_is_rowid_alias_single_integer_without_rowid() -> Result<()> {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT) WITHOUT ROWID;"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!table.column_is_rowid_alias(column),
|
||||
"column 'a´ shouldn't be a rowid alias because table has no rowid"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_column_is_rowid_alias_inline_composite_primary_key() -> Result<()> {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT PRIMARY KEY);"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!table.column_is_rowid_alias(column),
|
||||
"column 'a´ shouldn't be a rowid alias because table has composite primary key"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_column_is_rowid_alias_separate_composite_primary_key_definition() -> Result<()> {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER, b TEXT, PRIMARY KEY(a, b));"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!table.column_is_rowid_alias(column),
|
||||
"column 'a´ shouldn't be a rowid alias because table has composite primary key"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sqlite_schema() {
|
||||
let expected = r#"CREATE TABLE sqlite_schema (
|
||||
|
||||
@@ -72,7 +72,10 @@ impl PageIO for FileStorage {
|
||||
fn get(&self, page_idx: usize, c: Rc<Completion>) -> Result<()> {
|
||||
let size = c.buf().len();
|
||||
assert!(page_idx > 0);
|
||||
ensure!(size >= 1 << 9 && size <= 1 << 16 && size & (size - 1) == 0, StorageError::NotADB);
|
||||
ensure!(
|
||||
size >= 1 << 9 && size <= 1 << 16 && size & (size - 1) == 0,
|
||||
StorageError::NotADB
|
||||
);
|
||||
let pos = (page_idx - 1) * size;
|
||||
self.file.pread(pos, c)?;
|
||||
Ok(())
|
||||
|
||||
@@ -3,5 +3,6 @@ pub fn normalize_ident(ident: &str) -> String {
|
||||
&ident[1..ident.len() - 1]
|
||||
} else {
|
||||
ident
|
||||
}).to_lowercase()
|
||||
})
|
||||
.to_lowercase()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user