diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 02da772b9a..d35d067edc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -50,6 +50,7 @@ jobs: runs-on: ${{ matrix.os }} env: CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Adead_code" # TODO(alexandreyc): remove this line when implementation is complete steps: - uses: actions/checkout@v4 with: diff --git a/rust/core/src/error.rs b/rust/core/src/error.rs index 7aede86f06..75fe359e1e 100644 --- a/rust/core/src/error.rs +++ b/rust/core/src/error.rs @@ -67,7 +67,7 @@ pub enum Status { } /// An ADBC error. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Error { /// The error message. pub message: String, diff --git a/rust/core/src/ffi/constants.rs b/rust/core/src/ffi/constants.rs index 7585ef2c83..754df1477f 100644 --- a/rust/core/src/ffi/constants.rs +++ b/rust/core/src/ffi/constants.rs @@ -93,9 +93,16 @@ pub const ADBC_OPTION_ISOLATION_LEVEL_LINEARIZABLE: &str = "adbc.connection.transaction.isolation.linearizable"; pub const ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_KEY: i16 = 0; +pub const ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_NAME: &str = "adbc.statistic.byte_width"; pub const ADBC_STATISTIC_DISTINCT_COUNT_KEY: i16 = 1; +pub const ADBC_STATISTIC_DISTINCT_COUNT_NAME: &str = "adbc.statistic.distinct_count"; pub const ADBC_STATISTIC_MAX_BYTE_WIDTH_KEY: i16 = 2; +pub const ADBC_STATISTIC_MAX_BYTE_WIDTH_NAME: &str = "adbc.statistic.max_byte_width"; pub const ADBC_STATISTIC_MAX_VALUE_KEY: i16 = 3; +pub const ADBC_STATISTIC_MAX_VALUE_NAME: &str = "adbc.statistic.max_value"; pub const ADBC_STATISTIC_MIN_VALUE_KEY: i16 = 4; +pub const ADBC_STATISTIC_MIN_VALUE_NAME: &str = "adbc.statistic.min_value"; pub const ADBC_STATISTIC_NULL_COUNT_KEY: i16 = 5; +pub const ADBC_STATISTIC_NULL_COUNT_NAME: &str = "adbc.statistic.null_count"; pub const ADBC_STATISTIC_ROW_COUNT_KEY: i16 = 6; +pub const ADBC_STATISTIC_ROW_COUNT_NAME: &str = "adbc.statistic.row_count"; diff --git a/rust/core/src/ffi/methods.rs b/rust/core/src/ffi/methods.rs new file mode 100644 index 0000000000..e7f40459a2 --- /dev/null +++ b/rust/core/src/ffi/methods.rs @@ -0,0 +1,93 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#![allow(non_camel_case_types, non_snake_case)] + +use std::os::raw::{c_char, c_int}; + +use arrow::ffi::{FFI_ArrowArray, FFI_ArrowSchema}; +use arrow::ffi_stream::FFI_ArrowArrayStream; + +use super::{ + constants::ADBC_STATUS_NOT_IMPLEMENTED, FFI_AdbcConnection, FFI_AdbcDatabase, FFI_AdbcError, + FFI_AdbcErrorDetail, FFI_AdbcPartitions, FFI_AdbcStatement, FFI_AdbcStatusCode, +}; + +macro_rules! method { + ($func_name:ident ; $type_name:ident ; $return_type:ty ; $return_value:expr ; $( $arg:ty ),*) => { + pub(crate) type $type_name = unsafe extern "C" fn($( $arg ),*) -> $return_type; + pub(crate) unsafe extern "C" fn $func_name($(_:$arg),*) -> $return_type { + $return_value + } + }; +} + +method!(DatabaseInit ; FuncDatabaseInit ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *mut FFI_AdbcError); +method!(DatabaseNew ; FuncDatabaseNew ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *mut FFI_AdbcError); +method!(DatabaseSetOption ; FuncDatabaseSetOption ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *const c_char, *const c_char, *mut FFI_AdbcError); +method!(DatabaseRelease ; FuncDatabaseRelease ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *mut FFI_AdbcError); +method!(ConnectionCommit ; FuncConnectionCommit ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_AdbcError); +method!(ConnectionGetInfo ; FuncConnectionGetInfo ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const u32, usize, *mut FFI_ArrowArrayStream, *mut FFI_AdbcError); +method!(ConnectionGetObjects ; FuncConnectionGetObjects ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, c_int, *const c_char, *const c_char, *const c_char, *const *const c_char, *const c_char, *mut FFI_ArrowArrayStream, *mut FFI_AdbcError); +method!(ConnectionGetTableSchema ; FuncConnectionGetTableSchema ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, *const c_char, *const c_char, *mut FFI_ArrowSchema, *mut FFI_AdbcError); +method!(ConnectionGetTableTypes ; FuncConnectionGetTableTypes ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_ArrowArrayStream, *mut FFI_AdbcError); +method!(ConnectionInit ; FuncConnectionInit ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_AdbcDatabase, *mut FFI_AdbcError); +method!(ConnectionNew ; FuncConnectionNew ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_AdbcError); +method!(ConnectionSetOption ; FuncConnectionSetOption ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, *const c_char, *mut FFI_AdbcError); +method!(ConnectionReadPartition ; FuncConnectionReadPartition ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const u8, usize, *mut FFI_ArrowArrayStream, *mut FFI_AdbcError); +method!(ConnectionRelease ; FuncConnectionRelease ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_AdbcError); +method!(ConnectionRollback ; FuncConnectionRollback ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_AdbcError); +method!(StatementBind ; FuncStatementBind ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_ArrowArray, *mut FFI_ArrowSchema, *mut FFI_AdbcError); +method!(StatementBindStream ; FuncStatementBindStream ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_ArrowArrayStream, *mut FFI_AdbcError); +method!(StatementExecuteQuery ; FuncStatementExecuteQuery ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_ArrowArrayStream, *mut i64, *mut FFI_AdbcError); +method!(StatementExecutePartitions ; FuncStatementExecutePartitions ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_ArrowSchema, *mut FFI_AdbcPartitions, *mut i64, *mut FFI_AdbcError); +method!(StatementGetParameterSchema ; FuncStatementGetParameterSchema ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_ArrowSchema, *mut FFI_AdbcError); +method!(StatementNew ; FuncStatementNew ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_AdbcStatement, *mut FFI_AdbcError); +method!(StatementPrepare ; FuncStatementPrepare ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_AdbcError); +method!(StatementRelease ; FuncStatementRelease ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_AdbcError); +method!(StatementSetOption ; FuncStatementSetOption ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, *const c_char, *mut FFI_AdbcError); +method!(StatementSetSqlQuery ; FuncStatementSetSqlQuery ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, *mut FFI_AdbcError); +method!(StatementSetSubstraitPlan ; FuncStatementSetSubstraitPlan ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const u8, usize, *mut FFI_AdbcError); +method!(ErrorGetDetailCount ; FuncErrorGetDetailCount ; c_int ; 0 ; *const FFI_AdbcError); +method!(ErrorGetDetail ; FuncErrorGetDetail ; FFI_AdbcErrorDetail ; FFI_AdbcErrorDetail::default() ; *const FFI_AdbcError, c_int); +method!(ErrorFromArrayStream ; FuncErrorFromArrayStream ; *const FFI_AdbcError ; std::ptr::null() ; *mut FFI_ArrowArrayStream, *mut FFI_AdbcStatusCode); +method!(DatabaseGetOption ; FuncDatabaseGetOption ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *const c_char, *mut c_char, *mut usize, *mut FFI_AdbcError); +method!(DatabaseGetOptionBytes ; FuncDatabaseGetOptionBytes ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *const c_char, *mut u8, *mut usize, *mut FFI_AdbcError); +method!(DatabaseGetOptionDouble ; FuncDatabaseGetOptionDouble ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *const c_char, *mut f64, *mut FFI_AdbcError); +method!(DatabaseGetOptionInt ; FuncDatabaseGetOptionInt ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *const c_char, *mut i64, *mut FFI_AdbcError); +method!(DatabaseSetOptionBytes ; FuncDatabaseSetOptionBytes ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *const c_char, *const u8, usize, *mut FFI_AdbcError); +method!(DatabaseSetOptionDouble ; FuncDatabaseSetOptionDouble ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *const c_char, f64, *mut FFI_AdbcError); +method!(DatabaseSetOptionInt ; FuncDatabaseSetOptionInt ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcDatabase, *const c_char, i64, *mut FFI_AdbcError); +method!(ConnectionCancel ; FuncConnectionCancel ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_AdbcError); +method!(ConnectionGetOption ; FuncConnectionGetOption ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, *mut c_char, *mut usize, *mut FFI_AdbcError); +method!(ConnectionGetOptionBytes ; FuncConnectionGetOptionBytes ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, *mut u8, *mut usize, *mut FFI_AdbcError); +method!(ConnectionGetOptionDouble ; FuncConnectionGetOptionDouble ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, *mut f64, *mut FFI_AdbcError); +method!(ConnectionGetOptionInt ; FuncConnectionGetOptionInt ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, *mut i64, *mut FFI_AdbcError); +method!(ConnectionGetStatistics ; FuncConnectionGetStatistics ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, *const c_char, *const c_char, c_char, *mut FFI_ArrowArrayStream, *mut FFI_AdbcError); +method!(ConnectionGetStatisticNames ; FuncConnectionGetStatisticNames ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *mut FFI_ArrowArrayStream, *mut FFI_AdbcError); +method!(ConnectionSetOptionBytes ; FuncConnectionSetOptionBytes ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, *const u8, usize, *mut FFI_AdbcError); +method!(ConnectionSetOptionDouble ; FuncConnectionSetOptionDouble ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, f64, *mut FFI_AdbcError); +method!(ConnectionSetOptionInt ; FuncConnectionSetOptionInt ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcConnection, *const c_char, i64, *mut FFI_AdbcError); +method!(StatementCancel ; FuncStatementCancel ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_AdbcError); +method!(StatementExecuteSchema ; FuncStatementExecuteSchema ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *mut FFI_ArrowSchema, *mut FFI_AdbcError); +method!(StatementGetOption ; FuncStatementGetOption ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, *mut c_char, *mut usize, *mut FFI_AdbcError); +method!(StatementGetOptionBytes ; FuncStatementGetOptionBytes ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, *mut u8, *mut usize, *mut FFI_AdbcError); +method!(StatementGetOptionDouble ; FuncStatementGetOptionDouble ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, *mut f64, *mut FFI_AdbcError); +method!(StatementGetOptionInt ; FuncStatementGetOptionInt ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, *mut i64, *mut FFI_AdbcError); +method!(StatementSetOptionBytes ; FuncStatementSetOptionBytes ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, *const u8, usize, *mut FFI_AdbcError); +method!(StatementSetOptionDouble ; FuncStatementSetOptionDouble ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, f64, *mut FFI_AdbcError); +method!(StatementSetOptionInt ; FuncStatementSetOptionInt ; FFI_AdbcStatusCode ; ADBC_STATUS_NOT_IMPLEMENTED ; *mut FFI_AdbcStatement, *const c_char, i64, *mut FFI_AdbcError); diff --git a/rust/core/src/ffi/mod.rs b/rust/core/src/ffi/mod.rs index c8bfe5ff30..69e866c0cb 100644 --- a/rust/core/src/ffi/mod.rs +++ b/rust/core/src/ffi/mod.rs @@ -16,11 +16,9 @@ // under the License. pub mod constants; +pub(crate) mod methods; pub(crate) mod types; - -// TODO(alexandreyc): uncomment these lines during follow-up PRs -// pub(crate) mod methods; -// pub use types::{ -// FFI_AdbcConnection, FFI_AdbcDatabase, FFI_AdbcDriver, FFI_AdbcDriverInitFunc, FFI_AdbcError, -// FFI_AdbcErrorDetail, FFI_AdbcPartitions, FFI_AdbcStatement, FFI_AdbcStatusCode, -// }; +pub use types::{ + FFI_AdbcConnection, FFI_AdbcDatabase, FFI_AdbcDriver, FFI_AdbcDriverInitFunc, FFI_AdbcError, + FFI_AdbcErrorDetail, FFI_AdbcPartitions, FFI_AdbcStatement, FFI_AdbcStatusCode, +}; diff --git a/rust/core/src/ffi/types.rs b/rust/core/src/ffi/types.rs index 8dfc25381c..080786aff2 100644 --- a/rust/core/src/ffi/types.rs +++ b/rust/core/src/ffi/types.rs @@ -17,4 +17,647 @@ #![allow(non_camel_case_types, non_snake_case)] +use std::ffi::{CStr, CString}; +use std::mem::ManuallyDrop; +use std::os::raw::{c_char, c_int, c_void}; +use std::ptr::{null, null_mut}; + +use super::{constants, methods}; +use crate::{ + error::{Error, Status}, + Partitions, +}; + pub type FFI_AdbcStatusCode = u8; + +/// A driver initialization function. +pub type FFI_AdbcDriverInitFunc = + unsafe extern "C" fn(c_int, *mut c_void, *mut FFI_AdbcError) -> FFI_AdbcStatusCode; + +#[repr(C)] +#[derive(Debug)] +pub struct FFI_AdbcError { + message: *mut c_char, + vendor_code: i32, + sqlstate: [c_char; 5], + release: Option, + /// Added in ADBC 1.1.0. + pub(crate) private_data: *mut c_void, + /// Added in ADBC 1.1.0. + pub private_driver: *const FFI_AdbcDriver, +} + +#[repr(C)] +#[derive(Debug)] +pub struct FFI_AdbcErrorDetail { + /// The metadata key. + pub(crate) key: *const c_char, + /// The binary metadata value. + pub(crate) value: *const u8, + /// The length of the metadata value. + pub(crate) value_length: usize, +} + +#[repr(C)] +#[derive(Debug)] +pub struct FFI_AdbcDatabase { + /// Opaque implementation-defined state. + /// This field is NULLPTR iff the connection is unintialized/freed. + pub(crate) private_data: *mut c_void, + /// The associated driver (used by the driver manager to help track state). + pub(crate) private_driver: *const FFI_AdbcDriver, +} + +unsafe impl Send for FFI_AdbcDatabase {} + +#[repr(C)] +#[derive(Debug)] +pub struct FFI_AdbcConnection { + /// Opaque implementation-defined state. + /// This field is NULLPTR iff the connection is unintialized/freed. + pub(crate) private_data: *mut c_void, + /// The associated driver (used by the driver manager to help track state). + pub(crate) private_driver: *const FFI_AdbcDriver, +} + +#[repr(C)] +#[derive(Debug)] +pub struct FFI_AdbcStatement { + /// Opaque implementation-defined state. + /// This field is NULLPTR iff the connection is unintialized/freed. + pub(crate) private_data: *mut c_void, + /// The associated driver (used by the driver manager to help track state). + pub(crate) private_driver: *const FFI_AdbcDriver, +} + +#[repr(C)] +#[derive(Debug)] +pub struct FFI_AdbcPartitions { + /// The number of partitions. + num_partitions: usize, + + /// The partitions of the result set, where each entry (up to + /// num_partitions entries) is an opaque identifier that can be + /// passed to AdbcConnectionReadPartition. + // It's defined as a const const pointer in C but we need to release it so it + // probably needs to be mutable. + partitions: *mut *mut u8, + + /// The length of each corresponding entry in partitions. + // It's defined as a const pointer in C but we need to release it so it + // probably needs to be mutable. + partition_lengths: *mut usize, + + /// Opaque implementation-defined state. + /// This field is NULLPTR iff the connection is unintialized/freed. + pub(crate) private_data: *mut c_void, + + /// Release the contained partitions. + /// Unlike other structures, this is an embedded callback to make it + /// easier for the driver manager and driver to cooperate. + release: Option, +} + +#[repr(C)] +#[derive(Debug)] +pub struct FFI_AdbcDriver { + /// Opaque driver-defined state. + /// This field is NULL if the driver is unintialized/freed (but + /// it need not have a value even if the driver is initialized). + pub(crate) private_data: *mut c_void, + /// Opaque driver manager-defined state. + /// This field is NULL if the driver is unintialized/freed (but + /// it need not have a value even if the driver is initialized). + pub(crate) private_manager: *const c_void, + pub(crate) release: Option< + unsafe extern "C" fn(driver: *mut Self, error: *mut FFI_AdbcError) -> FFI_AdbcStatusCode, + >, + pub(crate) DatabaseInit: Option, + pub(crate) DatabaseNew: Option, + pub(crate) DatabaseSetOption: Option, + pub(crate) DatabaseRelease: Option, + pub(crate) ConnectionCommit: Option, + pub(crate) ConnectionGetInfo: Option, + pub(crate) ConnectionGetObjects: Option, + pub(crate) ConnectionGetTableSchema: Option, + pub(crate) ConnectionGetTableTypes: Option, + pub(crate) ConnectionInit: Option, + pub(crate) ConnectionNew: Option, + pub(crate) ConnectionSetOption: Option, + pub(crate) ConnectionReadPartition: Option, + pub(crate) ConnectionRelease: Option, + pub(crate) ConnectionRollback: Option, + pub(crate) StatementBind: Option, + pub(crate) StatementBindStream: Option, + pub(crate) StatementExecuteQuery: Option, + pub(crate) StatementExecutePartitions: Option, + pub(crate) StatementGetParameterSchema: Option, + pub(crate) StatementNew: Option, + pub(crate) StatementPrepare: Option, + pub(crate) StatementRelease: Option, + pub(crate) StatementSetOption: Option, + pub(crate) StatementSetSqlQuery: Option, + pub(crate) StatementSetSubstraitPlan: Option, + pub(crate) ErrorGetDetailCount: Option, + pub(crate) ErrorGetDetail: Option, + pub(crate) ErrorFromArrayStream: Option, + pub(crate) DatabaseGetOption: Option, + pub(crate) DatabaseGetOptionBytes: Option, + pub(crate) DatabaseGetOptionDouble: Option, + pub(crate) DatabaseGetOptionInt: Option, + pub(crate) DatabaseSetOptionBytes: Option, + pub(crate) DatabaseSetOptionDouble: Option, + pub(crate) DatabaseSetOptionInt: Option, + pub(crate) ConnectionCancel: Option, + pub(crate) ConnectionGetOption: Option, + pub(crate) ConnectionGetOptionBytes: Option, + pub(crate) ConnectionGetOptionDouble: Option, + pub(crate) ConnectionGetOptionInt: Option, + pub(crate) ConnectionGetStatistics: Option, + pub(crate) ConnectionGetStatisticNames: Option, + pub(crate) ConnectionSetOptionBytes: Option, + pub(crate) ConnectionSetOptionDouble: Option, + pub(crate) ConnectionSetOptionInt: Option, + pub(crate) StatementCancel: Option, + pub(crate) StatementExecuteSchema: Option, + pub(crate) StatementGetOption: Option, + pub(crate) StatementGetOptionBytes: Option, + pub(crate) StatementGetOptionDouble: Option, + pub(crate) StatementGetOptionInt: Option, + pub(crate) StatementSetOptionBytes: Option, + pub(crate) StatementSetOptionDouble: Option, + pub(crate) StatementSetOptionInt: Option, +} + +unsafe impl Send for FFI_AdbcDriver {} +unsafe impl Sync for FFI_AdbcDriver {} + +macro_rules! driver_method { + ($driver:expr, $method:ident) => { + $driver.$method.unwrap_or(crate::ffi::methods::$method) + }; +} + +pub(crate) use driver_method; + +impl TryFrom for Status { + type Error = Error; + + fn try_from(value: FFI_AdbcStatusCode) -> Result { + match value { + constants::ADBC_STATUS_OK => Ok(Status::Ok), + constants::ADBC_STATUS_UNKNOWN => Ok(Status::Unknown), + constants::ADBC_STATUS_NOT_IMPLEMENTED => Ok(Status::NotImplemented), + constants::ADBC_STATUS_NOT_FOUND => Ok(Status::NotFound), + constants::ADBC_STATUS_ALREADY_EXISTS => Ok(Status::AlreadyExists), + constants::ADBC_STATUS_INVALID_ARGUMENT => Ok(Status::InvalidArguments), + constants::ADBC_STATUS_INVALID_STATE => Ok(Status::InvalidState), + constants::ADBC_STATUS_INVALID_DATA => Ok(Status::InvalidData), + constants::ADBC_STATUS_INTEGRITY => Ok(Status::Integrity), + constants::ADBC_STATUS_INTERNAL => Ok(Status::Internal), + constants::ADBC_STATUS_IO => Ok(Status::IO), + constants::ADBC_STATUS_CANCELLED => Ok(Status::Cancelled), + constants::ADBC_STATUS_TIMEOUT => Ok(Status::Timeout), + constants::ADBC_STATUS_UNAUTHENTICATED => Ok(Status::Unauthenticated), + constants::ADBC_STATUS_UNAUTHORIZED => Ok(Status::Unauthorized), + v => Err(Error::with_message_and_status( + format!("Unknown status code: {v}"), + Status::InvalidData, + )), + } + } +} + +impl From for FFI_AdbcStatusCode { + fn from(value: Status) -> Self { + match value { + Status::Ok => constants::ADBC_STATUS_OK, + Status::Unknown => constants::ADBC_STATUS_UNKNOWN, + Status::NotImplemented => constants::ADBC_STATUS_NOT_IMPLEMENTED, + Status::NotFound => constants::ADBC_STATUS_NOT_FOUND, + Status::AlreadyExists => constants::ADBC_STATUS_ALREADY_EXISTS, + Status::InvalidArguments => constants::ADBC_STATUS_INVALID_ARGUMENT, + Status::InvalidState => constants::ADBC_STATUS_INVALID_STATE, + Status::InvalidData => constants::ADBC_STATUS_INVALID_DATA, + Status::Integrity => constants::ADBC_STATUS_INTEGRITY, + Status::Internal => constants::ADBC_STATUS_INTERNAL, + Status::IO => constants::ADBC_STATUS_IO, + Status::Cancelled => constants::ADBC_STATUS_CANCELLED, + Status::Timeout => constants::ADBC_STATUS_TIMEOUT, + Status::Unauthenticated => constants::ADBC_STATUS_UNAUTHENTICATED, + Status::Unauthorized => constants::ADBC_STATUS_UNAUTHORIZED, + } + } +} + +impl From<&Status> for FFI_AdbcStatusCode { + fn from(value: &Status) -> Self { + (*value).into() + } +} + +impl From for Partitions { + fn from(value: FFI_AdbcPartitions) -> Self { + let mut partitions = Vec::with_capacity(value.num_partitions); + for p in 0..value.num_partitions { + let partition = unsafe { + let ptr = *value.partitions.add(p); + let len = *value.partition_lengths.add(p); + std::slice::from_raw_parts(ptr, len) + }; + partitions.push(partition.to_vec()); + } + partitions + } +} + +// Taken from `Vec::into_raw_parts` which is currently nightly-only. +fn vec_into_raw_parts(data: Vec) -> (*mut T, usize, usize) { + let mut md = ManuallyDrop::new(data); + (md.as_mut_ptr(), md.len(), md.capacity()) +} + +// We need to store capacities to correctly release memory because the C API +// only stores lengths and so capacities are lost in translation. +struct PartitionsPrivateData { + partitions_capacity: usize, + partition_lengths_capacity: usize, + partition_capacities: Vec, +} + +impl From for FFI_AdbcPartitions { + fn from(value: Partitions) -> Self { + let num_partitions = value.len(); + let mut partition_lengths = Vec::with_capacity(num_partitions); + let mut partition_capacities = Vec::with_capacity(num_partitions); + let mut partition_ptrs = Vec::with_capacity(num_partitions); + + for partition in value.into_iter() { + let (partition_ptr, partition_len, partition_cap) = vec_into_raw_parts(partition); + partition_lengths.push(partition_len); + partition_capacities.push(partition_cap); + partition_ptrs.push(partition_ptr); + } + + let (partition_lengths_ptr, _, partition_lengths_cap) = + vec_into_raw_parts(partition_lengths); + let (partitions_ptr, _, partitions_cap) = vec_into_raw_parts(partition_ptrs); + let private_data = Box::new(PartitionsPrivateData { + partitions_capacity: partitions_cap, + partition_lengths_capacity: partition_lengths_cap, + partition_capacities, + }); + + FFI_AdbcPartitions { + num_partitions, + partition_lengths: partition_lengths_ptr, + partitions: partitions_ptr, + private_data: Box::into_raw(private_data) as *mut c_void, + release: Some(release_ffi_partitions), + } + } +} + +unsafe extern "C" fn release_ffi_partitions(partitions: *mut FFI_AdbcPartitions) { + match partitions.as_mut() { + None => (), + Some(partitions) => { + // SAFETY: `partitions.private_data` was necessarily obtained with `Box::into_raw`. + // Additionally, the boxed data is necessarily `PartitionsPrivateData`. + // Finally, C should call the release function only once. + let private_data = Box::from_raw(partitions.private_data as *mut PartitionsPrivateData); + + for p in 0..partitions.num_partitions { + let partition_ptr = *partitions.partitions.add(p); + let partition_len = *partitions.partition_lengths.add(p); + let partition_cap = private_data.partition_capacities[p]; + // SAFETY: `partition_ptr`, `partition_len` and `partition_cap` has been created in + // `From for FFI_AdbcPartitions` from the result of `vec_into_raw_parts`. + let partition = Vec::from_raw_parts(partition_ptr, partition_len, partition_cap); + drop(partition); + } + + // SAFETY: `partitions.partition_lengths`, `partitions.num_partitions` + // and `private_data.partition_lengths_capacity` has been created in + // `From for FFI_AdbcPartitions` from the result of `vec_into_raw_parts`. + let partition_lengths = Vec::from_raw_parts( + partitions.partition_lengths, + partitions.num_partitions, + private_data.partition_lengths_capacity, + ); + drop(partition_lengths); + + // SAFETY: `partitions.partitions`, `partitions.num_partitions` and + // `private_data.partitions_capacity` has been created in + // `From for FFI_AdbcPartitions` from the result of `vec_into_raw_parts`. + let partitions_vec = Vec::from_raw_parts( + partitions.partitions, + partitions.num_partitions, + private_data.partitions_capacity, + ); + drop(partitions_vec); + + drop(private_data); + } + } +} + +impl Default for FFI_AdbcDriver { + fn default() -> Self { + Self { + private_data: null_mut(), + private_manager: null_mut(), + release: None, + DatabaseInit: None, + DatabaseNew: None, + DatabaseSetOption: None, + DatabaseRelease: None, + ConnectionCommit: None, + ConnectionGetInfo: None, + ConnectionGetObjects: None, + ConnectionGetTableSchema: None, + ConnectionGetTableTypes: None, + ConnectionInit: None, + ConnectionNew: None, + ConnectionSetOption: None, + ConnectionReadPartition: None, + ConnectionRelease: None, + ConnectionRollback: None, + StatementBind: None, + StatementBindStream: None, + StatementExecuteQuery: None, + StatementExecutePartitions: None, + StatementGetParameterSchema: None, + StatementNew: None, + StatementPrepare: None, + StatementRelease: None, + StatementSetOption: None, + StatementSetSqlQuery: None, + StatementSetSubstraitPlan: None, + ErrorGetDetailCount: None, + ErrorGetDetail: None, + ErrorFromArrayStream: None, + DatabaseGetOption: None, + DatabaseGetOptionBytes: None, + DatabaseGetOptionDouble: None, + DatabaseGetOptionInt: None, + DatabaseSetOptionBytes: None, + DatabaseSetOptionDouble: None, + DatabaseSetOptionInt: None, + ConnectionCancel: None, + ConnectionGetOption: None, + ConnectionGetOptionBytes: None, + ConnectionGetOptionDouble: None, + ConnectionGetOptionInt: None, + ConnectionGetStatistics: None, + ConnectionGetStatisticNames: None, + ConnectionSetOptionBytes: None, + ConnectionSetOptionDouble: None, + ConnectionSetOptionInt: None, + StatementCancel: None, + StatementExecuteSchema: None, + StatementGetOption: None, + StatementGetOptionBytes: None, + StatementGetOptionDouble: None, + StatementGetOptionInt: None, + StatementSetOptionBytes: None, + StatementSetOptionDouble: None, + StatementSetOptionInt: None, + } + } +} + +impl Default for FFI_AdbcError { + fn default() -> Self { + Self { + message: null_mut(), + vendor_code: constants::ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA, + sqlstate: [0; 5], + release: None, + private_data: null_mut(), + private_driver: null(), + } + } +} + +impl Default for FFI_AdbcDatabase { + fn default() -> Self { + Self { + private_data: null_mut(), + private_driver: null_mut(), + } + } +} + +impl Default for FFI_AdbcConnection { + fn default() -> Self { + Self { + private_data: null_mut(), + private_driver: null_mut(), + } + } +} + +impl Default for FFI_AdbcErrorDetail { + fn default() -> Self { + Self { + key: null(), + value: null(), + value_length: 0, + } + } +} + +impl Default for FFI_AdbcStatement { + fn default() -> Self { + Self { + private_data: null_mut(), + private_driver: null(), + } + } +} + +impl Default for FFI_AdbcPartitions { + fn default() -> Self { + Self { + num_partitions: 0, + partitions: null_mut(), + partition_lengths: null_mut(), + private_data: null_mut(), + release: None, + } + } +} + +impl FFI_AdbcError { + pub fn with_driver(driver: &FFI_AdbcDriver) -> Self { + FFI_AdbcError { + private_driver: driver, + ..Default::default() + } + } +} + +impl TryFrom<&FFI_AdbcError> for Error { + type Error = Error; + + fn try_from(value: &FFI_AdbcError) -> Result { + let message = match value.message.is_null() { + true => "".to_string(), + false => { + // SAFETY: we assume that C gives us a valid string. + let message = unsafe { CStr::from_ptr(value.message) }; + let message = message.to_owned(); + message.into_string()? + } + }; + + let mut error = Error { + message, + status: Status::Unknown, + vendor_code: value.vendor_code, + sqlstate: value.sqlstate, + details: None, + }; + + if value.vendor_code == constants::ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA { + if let Some(driver) = unsafe { value.private_driver.as_ref() } { + let get_detail_count = driver_method!(driver, ErrorGetDetailCount); + let get_detail = driver_method!(driver, ErrorGetDetail); + let num_details = unsafe { get_detail_count(value) }; + let details = (0..num_details) + .map(|i| unsafe { get_detail(value, i) }) + .filter(|d| !d.key.is_null() && !d.value.is_null()) + .map(|d| unsafe { + // SAFETY: we assume that C gives us a valid string. + let key = CStr::from_ptr(d.key).to_string_lossy().to_string(); + // SAFETY: we assume that C gives us valid data. + let value = std::slice::from_raw_parts(d.value, d.value_length); + (key, value.to_vec()) + }) + .collect(); + error.details = Some(details); + } + } + + Ok(error) + } +} + +impl TryFrom for Error { + type Error = Error; + + fn try_from(value: FFI_AdbcError) -> Result { + (&value).try_into() + } +} + +// Invariant: keys.len() == values.len() +pub(crate) struct ErrorPrivateData { + pub(crate) keys: Vec, + pub(crate) values: Vec>, +} + +impl TryFrom for FFI_AdbcError { + type Error = Error; + + fn try_from(mut value: Error) -> Result { + let message = CString::new(value.message)?; + + let private_data = match value.details.take() { + None => null_mut(), + Some(details) => { + let keys = details + .iter() + .map(|(key, _)| CString::new(key.as_str())) + .collect::, _>>()?; + let values: Vec> = details.into_iter().map(|(_, value)| value).collect(); + + let private_data = Box::new(ErrorPrivateData { keys, values }); + let private_data = Box::into_raw(private_data); + private_data as *mut c_void + } + }; + + Ok(FFI_AdbcError { + message: message.into_raw(), + release: Some(release_ffi_error), + vendor_code: value.vendor_code, + sqlstate: value.sqlstate, + private_data, + private_driver: null(), + }) + } +} + +unsafe extern "C" fn release_ffi_error(error: *mut FFI_AdbcError) { + match error.as_mut() { + None => (), + Some(error) => { + // SAFETY: `error.message` was necessarily obtained with `CString::into_raw`. + // Additionally, C should not modify the string's length. + drop(CString::from_raw(error.message)); + + if !error.private_data.is_null() { + // SAFETY: `error.private_data` was necessarily obtained with `Box::into_raw`. + // Additionally, the boxed data is necessarily `ErrorPrivateData`. + // Finally, C should call the release function only once. + let private_data = Box::from_raw(error.private_data as *mut ErrorPrivateData); + drop(private_data); + } + } + } +} + +impl Drop for FFI_AdbcError { + fn drop(&mut self) { + if let Some(release) = self.release { + unsafe { release(self) }; + } + } +} + +impl Drop for FFI_AdbcDriver { + fn drop(&mut self) { + if let Some(release) = self.release { + // TODO(alexandreyc): how should we handle `release` failing? + // See: https://github.com/apache/arrow-adbc/pull/1742#discussion_r1574388409 + unsafe { release(self, null_mut()) }; + } + } +} + +impl Drop for FFI_AdbcPartitions { + fn drop(&mut self) { + if let Some(release) = self.release { + unsafe { release(self) }; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_roundtrip() { + let error_expected: Error = Error { + message: "Hello world!".into(), + status: Status::Unknown, + vendor_code: constants::ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA, + sqlstate: [1, 2, 3, 4, 5], + details: None, // Details are not transfered here because there is no driver + }; + let error_ffi: FFI_AdbcError = error_expected.clone().try_into().unwrap(); + let error_actual: Error = error_ffi.try_into().unwrap(); + assert_eq!(error_expected, error_actual); + } + + #[test] + fn test_partitions_roundtrip() { + let partitions_expected: Partitions = vec![b"A".into(), b"BB".into(), b"CCC".into()]; + let partitions_ffi: FFI_AdbcPartitions = partitions_expected.clone().into(); + let partitions_actual: Partitions = partitions_ffi.into(); + assert_eq!(partitions_expected, partitions_actual); + } +}