0
0
mirror of https://github.com/tursodatabase/libsql.git synced 2025-06-30 07:08:58 +00:00
Files
libsql/libsql-sqlite3/ext/crr/rs/integration-check/tests/pack_columns.rs

150 lines
4.7 KiB
Rust

use crsql_core::unpack_columns;
use crsql_core::ColumnValue;
use sqlite::{Connection, ResultCode};
use sqlite_nostd as sqlite;
#[test]
fn pack_columns_test() {
pack_columns_impl().unwrap();
}
#[test]
fn unpack_columns_test() {
unpack_columns_impl().unwrap();
}
// The rust test is mainly to check with valgrind and ensure we're correctly
// freeing data as we do some passing of destructors from rust to SQLite.
// Complete property based tests for encode & decode exist in python.
fn pack_columns_impl() -> Result<(), ResultCode> {
let db = integration_utils::opendb()?;
db.db.exec_safe("CREATE TABLE foo (id PRIMARY KEY, x, y)")?;
let insert_stmt = db.db.prepare_v2("INSERT INTO foo VALUES (?, ?, ?)")?;
let blob: [u8; 3] = [1, 2, 3];
insert_stmt.bind_int(1, 12)?;
insert_stmt.bind_text(2, "str", sqlite::Destructor::STATIC)?;
insert_stmt.bind_blob(3, &blob, sqlite::Destructor::STATIC)?;
insert_stmt.step()?;
let select_stmt = db
.db
.prepare_v2("SELECT quote(crsql_pack_columns(id, x, y)) FROM foo")?;
select_stmt.step()?;
let result = select_stmt.column_text(0)?;
assert!(result == "X'03090C0B037374720C03010203'");
// 03 09 0C 0B 03 73 74 72 0C 03 01 02 03
// cols: 03
// type & intlen: 09 -> 0b00001001 -> 01 type & 01 intlen
// value: 0C -> 12
// type & intlen: 0B -> 0b00001011 -> 03 type & 01 intlen
// 03 -> len
// 73 74 72 -> str
// type & intlen: 0C -> 0b00001100 -> 04 type & 01 intlen
// len: 03
// bytes: 01 02 3
// voila, done in 13 bytes! < 18 byte string < 26 byte binary w/o varints
// Before variable length encoding:
// 03 01 00 00 00 00 00 00 00 0C 03 00 00 00 03 73 74 72 04 00 00 00 03 01 02 03
// cols:03
// type: 01 (integer)
// value: 00 00 00 00 00 00 00 0C (12) TODO: encode as variable length integers to save space?
// type: 03 (text)
// len: 00 00 00 03 (3)
// byes: 73 (s) 74 (t) 72 (r)
// type: 04 (blob)
// len: 00 00 00 03 (3)
// bytes: 01 02 03
// vs string:
// 12|'str'|x'010203'
// ^ 18 bytes via string
// vs
// 26 bytes via binary
// 13 bytes are wasted due to not using variable length encoding for integers
// So.. do variable length ints?
let select_stmt = db
.db
.prepare_v2("SELECT crsql_pack_columns(id, x, y) FROM foo")?;
select_stmt.step()?;
let result = select_stmt.column_blob(0)?;
assert!(result.len() == 13);
let unpacked = unpack_columns(result)?;
assert!(unpacked.len() == 3);
if let ColumnValue::Integer(i) = unpacked[0] {
assert!(i == 12);
} else {
assert!("unexpected type" == "");
}
if let ColumnValue::Text(s) = &unpacked[1] {
assert!(s == "str")
} else {
assert!("unexpected type" == "");
}
if let ColumnValue::Blob(b) = &unpacked[2] {
assert!(b[..] == blob);
} else {
assert!("unexpected type" == "");
}
db.db.exec_safe("DELETE FROM foo")?;
let insert_stmt = db.db.prepare_v2("INSERT INTO foo VALUES (?, ?, ?)")?;
insert_stmt.bind_int(1, 0)?;
insert_stmt.bind_int(2, 10000000)?;
insert_stmt.bind_int(3, -2500000)?;
insert_stmt.step()?;
let select_stmt = db
.db
.prepare_v2("SELECT crsql_pack_columns(id, x, y) FROM foo")?;
select_stmt.step()?;
let result = select_stmt.column_blob(0)?;
let unpacked = unpack_columns(result)?;
assert!(unpacked.len() == 3);
if let ColumnValue::Integer(i) = unpacked[0] {
assert!(i == 0);
} else {
assert!("unexpected type" == "");
}
if let ColumnValue::Integer(i) = unpacked[1] {
assert!(i == 10000000)
} else {
assert!("unexpected type" == "");
}
if let ColumnValue::Integer(i) = unpacked[2] {
assert!(i == -2500000);
} else {
assert!("unexpected type" == "");
}
Ok(())
}
fn unpack_columns_impl() -> Result<(), ResultCode> {
let db = integration_utils::opendb().unwrap();
db.db.exec_safe("CREATE TABLE foo (id PRIMARY KEY, x, y)")?;
let insert_stmt = db.db.prepare_v2("INSERT INTO foo VALUES (?, ?, ?)")?;
let blob: [u8; 3] = [1, 2, 3];
insert_stmt.bind_int(1, 12)?;
insert_stmt.bind_text(2, "str", sqlite::Destructor::STATIC)?;
insert_stmt.bind_blob(3, &blob, sqlite::Destructor::STATIC)?;
insert_stmt.step()?;
let select_stmt = db
.db
.prepare_v2("SELECT cell FROM crsql_unpack_columns WHERE package = (SELECT crsql_pack_columns(id, x, y) FROM foo)")?;
select_stmt.step()?;
assert!(select_stmt.column_int(0)? == 12);
select_stmt.step()?;
assert!(select_stmt.column_text(0)? == "str");
select_stmt.step()?;
assert!(select_stmt.column_blob(0)? == blob);
Ok(())
}