-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* build loadable ext * fix warning * add loadable macros * fix comment * fix clippy error * fix clippy
- Loading branch information
1 parent
80a492c
commit b310e9b
Showing
8 changed files
with
196 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
name = "duckdb-loadable-macros" | ||
version = "0.1.0" | ||
authors = ["wangfenjin <[email protected]>"] | ||
edition = "2021" | ||
license = "MIT" | ||
repository = "https://github.com/wangfenjin/duckdb-rs" | ||
homepage = "https://github.com/wangfenjin/duckdb-rs" | ||
keywords = ["duckdb", "ffi", "database"] | ||
readme = "README.md" | ||
categories = ["external-ffi-bindings", "database"] | ||
description = "Native bindings to the libduckdb library, C API; build loadable extensions" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
proc-macro2 = { version = "1.0.56" } | ||
quote = { version = "1.0.21" } | ||
syn = { version = "1.0.95", features = [ "extra-traits", "full", "fold", "parsing" ] } | ||
|
||
[lib] | ||
proc-macro = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../LICENSE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
use proc_macro2::Ident; | ||
|
||
use syn::{parse_macro_input, spanned::Spanned, Item}; | ||
|
||
use proc_macro::TokenStream; | ||
use quote::quote_spanned; | ||
|
||
/// Wraps an entrypoint function to expose an unsafe extern "C" function of the same name. | ||
#[proc_macro_attribute] | ||
pub fn duckdb_entrypoint(_attr: TokenStream, item: TokenStream) -> TokenStream { | ||
let ast = parse_macro_input!(item as syn::Item); | ||
match ast { | ||
Item::Fn(mut func) => { | ||
let c_entrypoint = func.sig.ident.clone(); | ||
|
||
let original_funcname = func.sig.ident.to_string(); | ||
func.sig.ident = Ident::new(format!("_{}", original_funcname).as_str(), func.sig.ident.span()); | ||
|
||
let prefixed_original_function = func.sig.ident.clone(); | ||
|
||
quote_spanned! {func.span()=> | ||
#func | ||
|
||
/// # Safety | ||
/// | ||
/// Will be called by duckdb | ||
#[no_mangle] | ||
pub unsafe extern "C" fn #c_entrypoint(db: *mut c_void) { | ||
let connection = Connection::open_from_raw(db.cast()).expect("can't open db connection"); | ||
#prefixed_original_function(connection).expect("init failed"); | ||
} | ||
|
||
/// # Safety | ||
/// | ||
/// Predefined function, don't need to change unless you are sure | ||
#[no_mangle] | ||
pub unsafe extern "C" fn libhello_ext_version() -> *const c_char { | ||
ffi::duckdb_library_version() | ||
} | ||
|
||
|
||
} | ||
.into() | ||
} | ||
_ => panic!("Only function items are allowed on duckdb_entrypoint"), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
extern crate duckdb; | ||
extern crate duckdb_loadable_macros; | ||
extern crate libduckdb_sys; | ||
|
||
use duckdb::{ | ||
vtab::{BindInfo, DataChunk, Free, FunctionInfo, InitInfo, Inserter, LogicalType, LogicalTypeId, VTab}, | ||
Connection, Result, | ||
}; | ||
use duckdb_loadable_macros::duckdb_entrypoint; | ||
use libduckdb_sys as ffi; | ||
use std::{ | ||
error::Error, | ||
ffi::{c_char, c_void, CString}, | ||
}; | ||
|
||
#[repr(C)] | ||
struct HelloBindData { | ||
name: *mut c_char, | ||
} | ||
|
||
impl Free for HelloBindData { | ||
fn free(&mut self) { | ||
unsafe { | ||
if self.name.is_null() { | ||
return; | ||
} | ||
drop(CString::from_raw(self.name)); | ||
} | ||
} | ||
} | ||
|
||
#[repr(C)] | ||
struct HelloInitData { | ||
done: bool, | ||
} | ||
|
||
struct HelloVTab; | ||
|
||
impl Free for HelloInitData {} | ||
|
||
impl VTab for HelloVTab { | ||
type InitData = HelloInitData; | ||
type BindData = HelloBindData; | ||
|
||
fn bind(bind: &BindInfo, data: *mut HelloBindData) -> Result<(), Box<dyn std::error::Error>> { | ||
bind.add_result_column("column0", LogicalType::new(LogicalTypeId::Varchar)); | ||
let param = bind.get_parameter(0).to_string(); | ||
unsafe { | ||
(*data).name = CString::new(param).unwrap().into_raw(); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn init(_: &InitInfo, data: *mut HelloInitData) -> Result<(), Box<dyn std::error::Error>> { | ||
unsafe { | ||
(*data).done = false; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn func(func: &FunctionInfo, output: &mut DataChunk) -> Result<(), Box<dyn std::error::Error>> { | ||
let init_info = func.get_init_data::<HelloInitData>(); | ||
let bind_info = func.get_bind_data::<HelloBindData>(); | ||
|
||
unsafe { | ||
if (*init_info).done { | ||
output.set_len(0); | ||
} else { | ||
(*init_info).done = true; | ||
let vector = output.flat_vector(0); | ||
let name = CString::from_raw((*bind_info).name); | ||
let result = CString::new(format!("Hello {}", name.to_str()?))?; | ||
// Can't consume the CString | ||
(*bind_info).name = CString::into_raw(name); | ||
vector.insert(0, result); | ||
output.set_len(1); | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn parameters() -> Option<Vec<LogicalType>> { | ||
Some(vec![LogicalType::new(LogicalTypeId::Varchar)]) | ||
} | ||
} | ||
|
||
// Exposes a extern C function named "libhello_ext_init" in the compiled dynamic library, | ||
// the "entrypoint" that duckdb will use to load the extension. | ||
#[duckdb_entrypoint] | ||
pub fn libhello_ext_init(conn: Connection) -> Result<(), Box<dyn Error>> { | ||
conn.register_table_function::<HelloVTab>("hello")?; | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters