mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-18 17:14:20 +01:00
Better support for BLOBs
- Limbo command line shell supports e.g. `SELECT x'616263';` - `EXPLAIN SELECT x'616263';` lists the opcode Missing: - Command line shell not entirely compatible with SQLite when blobs have non-printable characters in the middle (e.g. `SELECT x'610062';`) - Python bindings not supported (incoming soon)
This commit is contained in:
@@ -216,7 +216,7 @@ This document describes the SQLite compatibility status of Limbo:
|
|||||||
| BitAnd | No |
|
| BitAnd | No |
|
||||||
| BitNot | No |
|
| BitNot | No |
|
||||||
| BitOr | No |
|
| BitOr | No |
|
||||||
| Blob | No |
|
| Blob | Yes |
|
||||||
| Checkpoint | No |
|
| Checkpoint | No |
|
||||||
| Clear | No |
|
| Clear | No |
|
||||||
| Close | No |
|
| Close | No |
|
||||||
|
|||||||
@@ -275,7 +275,9 @@ fn query(
|
|||||||
Value::Integer(i) => print!("{}", i),
|
Value::Integer(i) => print!("{}", i),
|
||||||
Value::Float(f) => print!("{:?}", f),
|
Value::Float(f) => print!("{:?}", f),
|
||||||
Value::Text(s) => print!("{}", s),
|
Value::Text(s) => print!("{}", s),
|
||||||
Value::Blob(b) => print!("{:?}", b),
|
Value::Blob(b) => {
|
||||||
|
print!("{}", String::from_utf8_lossy(b))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
@@ -305,7 +307,9 @@ fn query(
|
|||||||
Value::Integer(i) => i.to_string().cell(),
|
Value::Integer(i) => i.to_string().cell(),
|
||||||
Value::Float(f) => f.to_string().cell(),
|
Value::Float(f) => f.to_string().cell(),
|
||||||
Value::Text(s) => s.cell(),
|
Value::Text(s) => s.cell(),
|
||||||
Value::Blob(b) => format!("{:?}", b).cell(),
|
Value::Blob(b) => {
|
||||||
|
format!("{}", String::from_utf8_lossy(b)).cell()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1433,7 +1433,23 @@ pub fn translate_expr(
|
|||||||
});
|
});
|
||||||
Ok(target_register)
|
Ok(target_register)
|
||||||
}
|
}
|
||||||
ast::Literal::Blob(_) => todo!(),
|
ast::Literal::Blob(s) => {
|
||||||
|
let bytes = s
|
||||||
|
.as_bytes()
|
||||||
|
.chunks_exact(2)
|
||||||
|
.map(|pair| {
|
||||||
|
// We assume that sqlite3-parser has already validated that
|
||||||
|
// the input is valid hex string, thus unwrap is safe.
|
||||||
|
let hex_byte = std::str::from_utf8(pair).unwrap();
|
||||||
|
u8::from_str_radix(hex_byte, 16).unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
program.emit_insn(Insn::Blob {
|
||||||
|
value: bytes,
|
||||||
|
dest: target_register,
|
||||||
|
});
|
||||||
|
Ok(target_register)
|
||||||
|
}
|
||||||
ast::Literal::Keyword(_) => todo!(),
|
ast::Literal::Keyword(_) => todo!(),
|
||||||
ast::Literal::Null => {
|
ast::Literal::Null => {
|
||||||
program.emit_insn(Insn::Null {
|
program.emit_insn(Insn::Null {
|
||||||
|
|||||||
@@ -470,6 +470,20 @@ pub fn insn_to_str(
|
|||||||
0,
|
0,
|
||||||
format!("r[{}]='{}'", dest, value),
|
format!("r[{}]='{}'", dest, value),
|
||||||
),
|
),
|
||||||
|
Insn::Blob { value, dest } => (
|
||||||
|
"Blob",
|
||||||
|
0,
|
||||||
|
*dest as i32,
|
||||||
|
0,
|
||||||
|
OwnedValue::Blob(Rc::new(value.clone())),
|
||||||
|
0,
|
||||||
|
format!(
|
||||||
|
"r[{}]={} (len={})",
|
||||||
|
dest,
|
||||||
|
String::from_utf8_lossy(value),
|
||||||
|
value.len()
|
||||||
|
),
|
||||||
|
),
|
||||||
Insn::RowId { cursor_id, dest } => (
|
Insn::RowId { cursor_id, dest } => (
|
||||||
"RowId",
|
"RowId",
|
||||||
*cursor_id as i32,
|
*cursor_id as i32,
|
||||||
|
|||||||
@@ -279,6 +279,12 @@ pub enum Insn {
|
|||||||
dest: usize,
|
dest: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Write a blob value into a register.
|
||||||
|
Blob {
|
||||||
|
value: Vec<u8>,
|
||||||
|
dest: usize,
|
||||||
|
},
|
||||||
|
|
||||||
// Read the rowid of the current row.
|
// Read the rowid of the current row.
|
||||||
RowId {
|
RowId {
|
||||||
cursor_id: CursorID,
|
cursor_id: CursorID,
|
||||||
@@ -1074,6 +1080,10 @@ impl Program {
|
|||||||
state.registers[*dest] = OwnedValue::Text(Rc::new(value.into()));
|
state.registers[*dest] = OwnedValue::Text(Rc::new(value.into()));
|
||||||
state.pc += 1;
|
state.pc += 1;
|
||||||
}
|
}
|
||||||
|
Insn::Blob { value, dest } => {
|
||||||
|
state.registers[*dest] = OwnedValue::Blob(Rc::new(value.clone()));
|
||||||
|
state.pc += 1;
|
||||||
|
}
|
||||||
Insn::RowId { cursor_id, dest } => {
|
Insn::RowId { cursor_id, dest } => {
|
||||||
let cursor = cursors.get_mut(cursor_id).unwrap();
|
let cursor = cursors.get_mut(cursor_id).unwrap();
|
||||||
if let Some(ref rowid) = cursor.rowid()? {
|
if let Some(ref rowid) = cursor.rowid()? {
|
||||||
|
|||||||
@@ -395,14 +395,13 @@ do_execsql_test typeof-real {
|
|||||||
SELECT typeof(1.0);
|
SELECT typeof(1.0);
|
||||||
} {real}
|
} {real}
|
||||||
|
|
||||||
# TODO: Uncomment when blobs are better supported
|
do_execsql_test typeof-blob {
|
||||||
# do_execsql_test typeof-blob {
|
SELECT typeof(x'61');
|
||||||
# SELECT typeof(x'61');
|
} {blob}
|
||||||
# } {blob}
|
|
||||||
#
|
do_execsql_test typeof-blob-empty {
|
||||||
# do_execsql_test typeof-blob-empty {
|
SELECT typeof(x'');
|
||||||
# SELECT typeof(x'');
|
} {blob}
|
||||||
# } {blob}
|
|
||||||
|
|
||||||
do_execsql_test typeof-sum-integer {
|
do_execsql_test typeof-sum-integer {
|
||||||
SELECT typeof(sum(age)) from users;
|
SELECT typeof(sum(age)) from users;
|
||||||
|
|||||||
@@ -11,6 +11,18 @@ do_execsql_test select-const-2 {
|
|||||||
SELECT 2
|
SELECT 2
|
||||||
} {2}
|
} {2}
|
||||||
|
|
||||||
|
do_execsql_test select-blob-empty {
|
||||||
|
SELECT x'';
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_execsql_test select-blob-ascii {
|
||||||
|
SELECT x'6C696D626f';
|
||||||
|
} {limbo}
|
||||||
|
|
||||||
|
do_execsql_test select-blob-emoji {
|
||||||
|
SELECT x'F09FA680';
|
||||||
|
} {🦀}
|
||||||
|
|
||||||
do_execsql_test select-limit-0 {
|
do_execsql_test select-limit-0 {
|
||||||
SELECT id FROM users LIMIT 0;
|
SELECT id FROM users LIMIT 0;
|
||||||
} {}
|
} {}
|
||||||
|
|||||||
Reference in New Issue
Block a user