Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move crsql_createCrr to Rust #349

Merged
merged 5 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/rs/core/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ pub extern "C" fn crsql_create_clock_table(
}
}

fn create_clock_table(
pub fn create_clock_table(
db: *mut sqlite3,
table_info: *mut crsql_TableInfo,
_err: *mut *mut c_char,
Expand Down
45 changes: 37 additions & 8 deletions core/rs/core/src/c.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
extern crate alloc;
use alloc::boxed::Box;
use alloc::ffi::CString;
use alloc::vec::Vec;
use core::ffi::{c_char, c_int};
use core::ptr::null_mut;
#[cfg(not(feature = "std"))]
use num_derive::FromPrimitive;

Expand Down Expand Up @@ -135,14 +139,6 @@ extern "C" {
ext_data: *mut crsql_ExtData,
err_msg: *mut *mut c_char,
) -> c_int;
pub fn crsql_createCrr(
db: *mut sqlite::sqlite3,
schemaName: *const c_char,
tblName: *const c_char,
isCommitAlter: c_int,
noTx: c_int,
err: *mut *mut c_char,
) -> c_int;
}

#[test]
Expand Down Expand Up @@ -640,3 +636,36 @@ fn bindgen_test_layout_crsql_ExtData() {
)
);
}

pub trait CPointer<T> {
/**
* Returns a C compatible pointer to the underlying data.
* After calling this function, the caller is responsible for the memory.
*/
fn into_c_ptr(self) -> *mut T;
tantaman marked this conversation as resolved.
Show resolved Hide resolved
}

impl<T> CPointer<T> for Vec<T> {
fn into_c_ptr(mut self) -> *mut T {
if self.len() == 0 {
null_mut()
} else {
self.shrink_to(0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why shrink before converting to raw parts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because Vec also has a capacity which might be larger than its length. When we transfer ownership out of Rust we need to strip this unused space to avoid memory leaks. Essentially we make sure that capacity = length which is what we assume when transferring ownership back to Rust to free all the stuff.

self.into_raw_parts().0
}
}
}

impl CPointer<c_char> for &str {
fn into_c_ptr(self) -> *mut c_char {
CString::new(self)
.map(|x| x.into_raw())
.unwrap_or(null_mut())
}
}

impl CPointer<crsql_TableInfo> for crsql_TableInfo {
fn into_c_ptr(self) -> *mut crsql_TableInfo {
Box::into_raw(Box::from(self))
}
}
14 changes: 5 additions & 9 deletions core/rs/core/src/create_cl_set_vtab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ extern crate alloc;
use core::ffi::{c_char, c_int, c_void};

use crate::alloc::borrow::ToOwned;
use crate::c::crsql_createCrr;
use crate::create_crr::create_crr;
use alloc::boxed::Box;
use alloc::ffi::CString;
use alloc::format;
use alloc::string::String;
use sqlite::{convert_rc, sqlite3, Connection, CursorRef, StrRef, VTabArgs, VTabRef};
use sqlite::{sqlite3, Connection, CursorRef, StrRef, VTabArgs, VTabRef};
use sqlite_nostd as sqlite;
use sqlite_nostd::ResultCode;

Expand Down Expand Up @@ -68,12 +67,9 @@ fn create_impl(
// We can't wrap this in a savepoint for some reason. I guess because the `CREATE VIRTUAL TABLE..`
// statement is processing? 🤷‍♂️
create_clset_storage(db, &vtab_args, err)?;
let db_name_c = CString::new(vtab_args.database_name)?;
let table_name_c = CString::new(base_name_from_virtual_name(vtab_args.table_name))?;

// TODO: move `createCrr` to Rust
let rc = unsafe { crsql_createCrr(db, db_name_c.as_ptr(), table_name_c.as_ptr(), 0, 1, err) };
convert_rc(rc)
let schema = vtab_args.database_name;
let table = base_name_from_virtual_name(vtab_args.table_name);
create_crr(db, schema, table, false, true, err)
}

fn create_clset_storage(
Expand Down
70 changes: 70 additions & 0 deletions core/rs/core/src/create_crr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use alloc::vec::Vec;
use core::ffi::{c_char, CStr};
use sqlite_nostd as sqlite;
use sqlite_nostd::ResultCode;

use crate::bootstrap::create_clock_table;
use crate::c::crsql_TableInfo;
use crate::tableinfo::{free_table_info, is_table_compatible, pull_table_info};
use crate::triggers::create_triggers;
use crate::{backfill_table, is_crr, remove_crr_triggers_if_exist};

/**
* Create a new crr --
* all triggers, views, tables
*/
pub fn create_crr(
db: *mut sqlite::sqlite3,
_schema: &str,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one day we'll support attached dbs in cr-sqlite...

table: &str,
is_commit_alter: bool,
no_tx: bool,
err: *mut *mut c_char,
) -> Result<ResultCode, ResultCode> {
if !is_table_compatible(db, table, err)? {
return Err(ResultCode::ERROR);
}
if is_crr(db, table)? {
return Ok(ResultCode::OK);
}

let mut table_info: *mut crsql_TableInfo = core::ptr::null_mut();
pull_table_info(db, table, &mut table_info, err)?;

let cleanup = |err: ResultCode| unsafe {
free_table_info(table_info);
err
};

create_clock_table(db, table_info, err)
.and_then(|_| remove_crr_triggers_if_exist(db, table))
.and_then(|_| create_triggers(db, table_info, err))
.map_err(cleanup)?;

let (non_pk_cols, pk_cols) = unsafe {
let info = table_info
.as_ref()
.ok_or(ResultCode::ERROR)
.map_err(cleanup)?;
let (pks, non_pks) = (info.pksLen as usize, info.nonPksLen as usize);
// Iterate without ownership transfer
(
(0..non_pks)
.map(|i| &*info.nonPks.offset(i as isize))
.map(|x| CStr::from_ptr(x.name).to_str())
.collect::<Result<Vec<_>, _>>()
.map_err(|_| ResultCode::ERROR)
.map_err(cleanup)?,
(0..pks)
.map(|i| &*info.pks.offset(i as isize))
.map(|x| CStr::from_ptr(x.name).to_str())
.collect::<Result<Vec<_>, _>>()
.map_err(|_| ResultCode::ERROR)
.map_err(cleanup)?,
)
};

backfill_table(db, table, pk_cols, non_pk_cols, is_commit_alter, no_tx).map_err(cleanup)?;

Ok(cleanup(ResultCode::OK))
}
62 changes: 62 additions & 0 deletions core/rs/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,34 @@ mod changes_vtab_write;
mod compare_values;
mod consts;
mod create_cl_set_vtab;
mod create_crr;
mod is_crr;
mod pack_columns;
mod stmt_cache;
mod tableinfo;
mod teardown;
mod triggers;
mod unpack_columns_vtab;
mod util;

use crate::c::crsql_TableInfo;
use core::{ffi::c_char, slice};
extern crate alloc;
use alloc::vec::Vec;
pub use automigrate::*;
pub use backfill::*;
use core::ffi::{c_int, CStr};
use create_crr::create_crr;
pub use is_crr::*;
use pack_columns::crsql_pack_columns;
pub use pack_columns::unpack_columns;
pub use pack_columns::ColumnValue;
use sqlite::ResultCode;
use sqlite_nostd as sqlite;
use sqlite_nostd::{Connection, Context, Value};
use tableinfo::free_table_info;
use tableinfo::is_table_compatible;
use tableinfo::pull_table_info;
pub use teardown::*;

pub extern "C" fn crsql_as_table(
Expand Down Expand Up @@ -209,3 +216,58 @@ pub extern "C" fn crsql_is_crr(db: *mut sqlite::sqlite3, table: *const c_char) -
(ResultCode::NOMEM as c_int) * -1
}
}

#[no_mangle]
pub extern "C" fn crsql_is_table_compatible(
db: *mut sqlite::sqlite3,
table: *const c_char,
err: *mut *mut c_char,
) -> c_int {
if let Ok(table) = unsafe { CStr::from_ptr(table).to_str() } {
is_table_compatible(db, table, err)
.map(|x| x as c_int)
.unwrap_or_else(|err| (err as c_int) * -1)
} else {
(ResultCode::NOMEM as c_int) * -1
}
}

#[no_mangle]
pub extern "C" fn crsql_pull_table_info(
db: *mut sqlite::sqlite3,
table: *const c_char,
table_info: *mut *mut crsql_TableInfo,
err: *mut *mut c_char,
) -> c_int {
if let Ok(table) = unsafe { CStr::from_ptr(table).to_str() } {
pull_table_info(db, table, table_info, err).unwrap_or_else(|err| err) as c_int
} else {
(ResultCode::NOMEM as c_int) * -1
}
}

#[no_mangle]
pub extern "C" fn crsql_free_table_info(table_info: *mut crsql_TableInfo) {
unsafe { free_table_info(table_info) };
}

#[no_mangle]
pub extern "C" fn crsql_create_crr(
db: *mut sqlite::sqlite3,
schema: *const c_char,
table: *const c_char,
is_commit_alter: c_int,
no_tx: c_int,
err: *mut *mut c_char,
) -> c_int {
let schema = unsafe { CStr::from_ptr(schema).to_str() };
let table = unsafe { CStr::from_ptr(table).to_str() };

return match (table, schema) {
(Ok(table), Ok(schema)) => {
create_crr(db, schema, table, is_commit_alter != 0, no_tx != 0, err)
.unwrap_or_else(|err| err) as c_int
}
_ => ResultCode::NOMEM as c_int,
};
}
Loading