mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-03 17:48:17 +00:00
CREATE TABLE
support
This commit is contained in:
parent
422dac50f2
commit
fc5e42bb94
5 changed files with 277 additions and 2 deletions
|
@ -42,7 +42,24 @@ pub fn translate(
|
|||
ast::Stmt::Begin(_, _) => bail_parse_error!("BEGIN not supported yet"),
|
||||
ast::Stmt::Commit(_) => bail_parse_error!("COMMIT not supported yet"),
|
||||
ast::Stmt::CreateIndex { .. } => bail_parse_error!("CREATE INDEX not supported yet"),
|
||||
ast::Stmt::CreateTable { .. } => bail_parse_error!("CREATE TABLE not supported yet"),
|
||||
ast::Stmt::CreateTable {
|
||||
temporary,
|
||||
if_not_exists,
|
||||
tbl_name,
|
||||
body,
|
||||
} => {
|
||||
if temporary {
|
||||
bail_parse_error!("TEMPORARY table not supported yet");
|
||||
}
|
||||
translate_create_table(
|
||||
tbl_name,
|
||||
body,
|
||||
if_not_exists,
|
||||
database_header,
|
||||
connection,
|
||||
schema,
|
||||
)
|
||||
}
|
||||
ast::Stmt::CreateTrigger { .. } => bail_parse_error!("CREATE TRIGGER not supported yet"),
|
||||
ast::Stmt::CreateView { .. } => bail_parse_error!("CREATE VIEW not supported yet"),
|
||||
ast::Stmt::CreateVirtualTable { .. } => {
|
||||
|
@ -85,6 +102,195 @@ pub fn translate(
|
|||
}
|
||||
}
|
||||
|
||||
/* Example:
|
||||
|
||||
sqlite> EXPLAIN CREATE TABLE users (id INT, email TEXT);;
|
||||
addr opcode p1 p2 p3 p4 p5 comment
|
||||
---- ------------- ---- ---- ---- ------------- -- -------------
|
||||
0 Init 0 30 0 0 Start at 30
|
||||
1 ReadCookie 0 3 2 0
|
||||
2 If 3 5 0 0
|
||||
3 SetCookie 0 2 4 0
|
||||
4 SetCookie 0 5 1 0
|
||||
5 CreateBtree 0 2 1 0 r[2]=root iDb=0 flags=1
|
||||
6 OpenWrite 0 1 0 5 0 root=1 iDb=0
|
||||
7 NewRowid 0 1 0 0 r[1]=rowid
|
||||
8 Blob 6 3 0 0 r[3]= (len=6)
|
||||
9 Insert 0 3 1 8 intkey=r[1] data=r[3]
|
||||
10 Close 0 0 0 0
|
||||
11 Close 0 0 0 0
|
||||
12 Null 0 4 5 0 r[4..5]=NULL
|
||||
13 Noop 2 0 4 0
|
||||
14 OpenWrite 1 1 0 5 0 root=1 iDb=0; sqlite_master
|
||||
15 SeekRowid 1 17 1 0 intkey=r[1]
|
||||
16 Rowid 1 5 0 0 r[5]= rowid of 1
|
||||
17 IsNull 5 26 0 0 if r[5]==NULL goto 26
|
||||
18 String8 0 6 0 table 0 r[6]='table'
|
||||
19 String8 0 7 0 users 0 r[7]='users'
|
||||
20 String8 0 8 0 users 0 r[8]='users'
|
||||
21 Copy 2 9 0 0 r[9]=r[2]
|
||||
22 String8 0 10 0 CREATE TABLE users (id INT, email TEXT) 0 r[10]='CREATE TABLE users (id INT, email TEXT)'
|
||||
23 MakeRecord 6 5 4 BBBDB 0 r[4]=mkrec(r[6..10])
|
||||
24 Delete 1 68 5 0
|
||||
25 Insert 1 4 5 0 intkey=r[5] data=r[4]
|
||||
26 SetCookie 0 1 1 0
|
||||
27 ParseSchema 0 0 0 tbl_name='users' AND type!='trigger' 0
|
||||
28 SqlExec 1 0 0 PRAGMA "main".integrity_check('users') 0
|
||||
29 Halt 0 0 0 0
|
||||
30 Transaction 0 1 0 0 1 usesStmtJournal=1
|
||||
31 Goto 0 1 0 0
|
||||
|
||||
*/
|
||||
fn translate_create_table(
|
||||
tbl_name: ast::QualifiedName,
|
||||
body: ast::CreateTableBody,
|
||||
if_not_exists: bool,
|
||||
database_header: Rc<RefCell<DatabaseHeader>>,
|
||||
connection: Weak<Connection>,
|
||||
schema: &Schema,
|
||||
) -> Result<Program> {
|
||||
let mut program = ProgramBuilder::new();
|
||||
let parse_schema_label = program.allocate_label();
|
||||
let init_label = program.allocate_label();
|
||||
program.emit_insn_with_label_dependency(
|
||||
Insn::Init {
|
||||
target_pc: init_label,
|
||||
},
|
||||
init_label,
|
||||
);
|
||||
let start_offset = program.offset();
|
||||
// TODO: ReadCookie
|
||||
// TODO: If
|
||||
// TODO: SetCookie
|
||||
// TODO: SetCookie
|
||||
let root_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::CreateBtree {
|
||||
db: 0,
|
||||
root: root_reg,
|
||||
flags: 1,
|
||||
});
|
||||
let table_id = "sqlite_schema".to_string();
|
||||
let table = schema.get_table(&table_id).unwrap();
|
||||
let table = crate::schema::Table::BTree(table.clone());
|
||||
let sqlite_schema_cursor_id =
|
||||
program.alloc_cursor_id(Some(table_id.to_owned()), Some(table.to_owned()));
|
||||
program.emit_insn(Insn::OpenWriteAsync {
|
||||
cursor_id: sqlite_schema_cursor_id,
|
||||
root_page: 1,
|
||||
});
|
||||
program.emit_insn(Insn::OpenWriteAwait {});
|
||||
let rowid_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::NewRowid {
|
||||
cursor: sqlite_schema_cursor_id,
|
||||
rowid_reg,
|
||||
prev_largest_reg: 0,
|
||||
});
|
||||
let blob_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::Blob {
|
||||
value: vec![0, 0, 0, 0, 0, 0],
|
||||
dest: blob_reg,
|
||||
});
|
||||
program.emit_insn(Insn::InsertAsync {
|
||||
cursor: sqlite_schema_cursor_id,
|
||||
key_reg: rowid_reg,
|
||||
record_reg: blob_reg,
|
||||
flag: 0,
|
||||
});
|
||||
program.emit_insn(Insn::InsertAwait {
|
||||
cursor_id: sqlite_schema_cursor_id,
|
||||
});
|
||||
program.emit_insn(Insn::Close {
|
||||
cursor_id: sqlite_schema_cursor_id,
|
||||
});
|
||||
program.emit_insn(Insn::Close {
|
||||
cursor_id: sqlite_schema_cursor_id,
|
||||
});
|
||||
let null_reg_1 = program.alloc_register();
|
||||
let null_reg_2 = program.alloc_register();
|
||||
program.emit_insn(Insn::Null {
|
||||
dest: null_reg_1,
|
||||
dest_end: Some(null_reg_2),
|
||||
});
|
||||
// TODO: Noop
|
||||
let sqlite_schema_cursor_id = program.alloc_cursor_id(Some(table_id), Some(table));
|
||||
program.emit_insn(Insn::OpenWriteAsync {
|
||||
cursor_id: sqlite_schema_cursor_id,
|
||||
root_page: 1,
|
||||
});
|
||||
program.emit_insn(Insn::OpenWriteAwait {});
|
||||
let seek_failed_label = program.allocate_label();
|
||||
program.emit_insn_with_label_dependency(
|
||||
Insn::SeekRowid {
|
||||
cursor_id: sqlite_schema_cursor_id,
|
||||
src_reg: rowid_reg,
|
||||
target_pc: seek_failed_label,
|
||||
},
|
||||
seek_failed_label,
|
||||
);
|
||||
let rowid_reg_2 = program.alloc_register();
|
||||
program.emit_insn(Insn::RowId {
|
||||
cursor_id: sqlite_schema_cursor_id,
|
||||
dest: rowid_reg_2,
|
||||
});
|
||||
program.resolve_label(seek_failed_label, program.offset());
|
||||
program.emit_insn_with_label_dependency(
|
||||
Insn::IsNull {
|
||||
src: rowid_reg,
|
||||
target_pc: parse_schema_label,
|
||||
},
|
||||
parse_schema_label,
|
||||
);
|
||||
let type_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::String8 {
|
||||
value: "table".to_string(),
|
||||
dest: type_reg,
|
||||
});
|
||||
let name_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::String8 {
|
||||
value: tbl_name.name.0.to_string(),
|
||||
dest: name_reg,
|
||||
});
|
||||
let tbl_name_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::String8 {
|
||||
value: tbl_name.name.0.to_string(),
|
||||
dest: tbl_name_reg,
|
||||
});
|
||||
let rootpage_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::Copy {
|
||||
src_reg: root_reg,
|
||||
dst_reg: rootpage_reg,
|
||||
amount: 1,
|
||||
});
|
||||
let sql_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::String8 {
|
||||
value: "TODO".to_string(),
|
||||
dest: sql_reg,
|
||||
});
|
||||
let record_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: type_reg,
|
||||
count: 4,
|
||||
dest_reg: record_reg,
|
||||
});
|
||||
// TODO: Delete
|
||||
// TODO: Insert
|
||||
program.resolve_label(parse_schema_label, program.offset());
|
||||
// TODO: SetCookie
|
||||
// TODO: ParseSchema
|
||||
// TODO: SqlExec
|
||||
program.emit_insn(Insn::Halt {
|
||||
err_code: 0,
|
||||
description: String::new(),
|
||||
});
|
||||
program.resolve_label(init_label, program.offset());
|
||||
program.emit_insn(Insn::Transaction { write: true });
|
||||
program.emit_constant_insns();
|
||||
program.emit_insn(Insn::Goto {
|
||||
target_pc: start_offset,
|
||||
});
|
||||
Ok(program.build(database_header, connection))
|
||||
}
|
||||
|
||||
fn translate_pragma(
|
||||
name: &ast::QualifiedName,
|
||||
body: Option<ast::PragmaBody>,
|
||||
|
|
|
@ -319,6 +319,10 @@ impl ProgramBuilder {
|
|||
assert!(*target_pc < 0);
|
||||
*target_pc = to_offset;
|
||||
}
|
||||
Insn::IsNull { src, target_pc } => {
|
||||
assert!(*target_pc < 0);
|
||||
*target_pc = to_offset;
|
||||
}
|
||||
_ => {
|
||||
todo!("missing resolve_label for {:?}", insn);
|
||||
}
|
||||
|
|
|
@ -866,6 +866,33 @@ pub fn insn_to_str(
|
|||
0,
|
||||
format!("r[{}]=r[{}]", dst_reg, src_reg),
|
||||
),
|
||||
Insn::CreateBtree { db, root, flags } => (
|
||||
"CreateBtree",
|
||||
*db as i32,
|
||||
*root as i32,
|
||||
*flags as i32,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
format!("r[{}]=root iDb={} flags={}", root, db, flags),
|
||||
),
|
||||
Insn::Close { cursor_id } => (
|
||||
"Close",
|
||||
*cursor_id as i32,
|
||||
0,
|
||||
0,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
"".to_string(),
|
||||
),
|
||||
Insn::IsNull { src, target_pc } => (
|
||||
"IsNull",
|
||||
*src as i32,
|
||||
*target_pc as i32,
|
||||
0,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
format!("if (r[{}]==NULL) goto {}", src, target_pc),
|
||||
),
|
||||
};
|
||||
format!(
|
||||
"{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}",
|
||||
|
|
|
@ -469,6 +469,30 @@ pub enum Insn {
|
|||
dst_reg: usize,
|
||||
amount: usize, // 0 amount means we include src_reg, dst_reg..=dst_reg+amount = src_reg..=src_reg+amount
|
||||
},
|
||||
|
||||
/// Allocate a new b-tree.
|
||||
CreateBtree {
|
||||
/// Allocate b-tree in main database if zero or in temp database if non-zero (P1).
|
||||
db: usize,
|
||||
/// The root page of the new b-tree (P2).
|
||||
root: usize,
|
||||
/// Flags (P3).
|
||||
flags: usize,
|
||||
},
|
||||
|
||||
/// Close a cursor.
|
||||
Close {
|
||||
cursor_id: CursorID,
|
||||
},
|
||||
|
||||
/// Check if the register is null.
|
||||
IsNull {
|
||||
/// Source register (P1).
|
||||
src: usize,
|
||||
|
||||
/// Jump to this PC if the register is null (P2).
|
||||
target_pc: BranchOffset,
|
||||
},
|
||||
}
|
||||
|
||||
// Index of insn in list of insns
|
||||
|
@ -2087,6 +2111,20 @@ impl Program {
|
|||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::CreateBtree { .. } => {
|
||||
todo!();
|
||||
}
|
||||
Insn::Close { cursor_id } => {
|
||||
cursors.remove(cursor_id);
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::IsNull { src, target_pc } => {
|
||||
if matches!(state.registers[*src], OwnedValue::Null) {
|
||||
state.pc = *target_pc;
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -885,7 +885,7 @@ fn sqlite3_errstr_impl(rc: i32) -> *const std::ffi::c_char {
|
|||
"datatype mismatch", // SQLITE_MISMATCH
|
||||
"bad parameter or other API misuse", // SQLITE_MISUSE
|
||||
#[cfg(feature = "lfs")]
|
||||
"", // SQLITE_NOLFS
|
||||
"", // SQLITE_NOLFS
|
||||
#[cfg(not(feature = "lfs"))]
|
||||
"large file support is disabled", // SQLITE_NOLFS
|
||||
"authorization denied", // SQLITE_AUTH
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue