Skip to content

Instantly share code, notes, and snippets.

@dvas0004
Last active April 1, 2024 12:57
Show Gist options
  • Select an option

  • Save dvas0004/84a8f4048dc60eb948a1a17d0ecb1d05 to your computer and use it in GitHub Desktop.

Select an option

Save dvas0004/84a8f4048dc60eb948a1a17d0ecb1d05 to your computer and use it in GitHub Desktop.

Revisions

  1. dvas0004 revised this gist Apr 1, 2024. No changes.
  2. dvas0004 created this gist Apr 1, 2024.
    94 changes: 94 additions & 0 deletions vtab_1.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    use std::{ffi::c_int, marker::PhantomData};
    use rusqlite::ffi;
    use rusqlite::{ffi::{sqlite3_vtab, sqlite3_vtab_cursor}, vtab::{Context, IndexInfo, VTab, VTabConnection, VTabCursor, Values}, Connection};
    use std::os::raw::c_char;

    use rusqlite::{to_sqlite_error, Result};

    #[repr(C)]
    struct Test {
    /// Base class. Must be first
    base: sqlite3_vtab,
    }

    #[derive(Default)]
    #[repr(C)]
    struct TestCursor<'vtab> {
    /// Base class. Must be first
    base: sqlite3_vtab_cursor,
    /// The rowid
    row_id: i64,
    phantom: PhantomData<&'vtab Test>,
    data: Vec<String>, // this is where we load and store our "external" data - see `open`
    }

    // Write implementation of VTab trait.
    // Step 1(a) from https://docs.rs/rusqlite/latest/rusqlite/vtab/index.html
    unsafe impl<'vtab> VTab<'vtab> for Test {
    type Aux = ();
    type Cursor = TestCursor<'vtab>;

    fn connect(
    _: &mut VTabConnection,
    _aux: Option<&()>,
    _args: &[&[u8]],
    ) -> Result<(String, Test), rusqlite::Error> {
    let vtab = Test {
    base: sqlite3_vtab::default()
    };
    // our vtab schema is defined here
    Ok(("CREATE TABLE test(id INT, name TEXT)".to_owned(), vtab))
    }

    fn best_index(&self, info: &mut IndexInfo) -> Result<(), rusqlite::Error> {
    info.set_estimated_cost(1.);
    Ok(())
    }

    // this is where we do external calls (e.g. APIs, files)
    // to populate our external data
    fn open(&'vtab mut self) -> Result<TestCursor<'vtab>, rusqlite::Error> {
    let mut test_cursor = TestCursor::default();
    test_cursor.data = vec!["a".to_owned(), "b".to_owned(), "c".to_owned()];
    Ok(test_cursor)
    }
    }

    // Write implementation of VTabCursor trait.
    // Step 1(b) from https://docs.rs/rusqlite/latest/rusqlite/vtab/index.html
    unsafe impl VTabCursor for TestCursor<'_> {
    fn filter(
    &mut self,
    _idx_num: c_int,
    _idx_str: Option<&str>,
    _args: &Values<'_>,
    ) -> Result<(), rusqlite::Error> {
    Ok(())
    }

    // next - how do we get the next record?
    fn next(&mut self) -> Result<(), rusqlite::Error> {
    self.row_id += 1;
    Ok(())
    }

    // EOF - when should we stop calling `next`?
    fn eof(&self) -> bool {
    self.row_id >= self.data.len().try_into().unwrap()
    }

    // descibe the mappings between columns (expressed as numbers) and our data
    // stored in the cursor
    fn column(&self, ctx: &mut Context, col_number: c_int) -> Result<(), rusqlite::Error> {
    match col_number {
    0 => ctx.set_result(&self.row_id),
    1 => ctx.set_result(&self.data[self.row_id as usize]),
    _ => Err(rusqlite::Error::InvalidColumnName("n/a".to_owned())),
    }

    }

    fn rowid(&self) -> Result<i64, rusqlite::Error> {
    Ok(self.row_id)
    }
    }