| // 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. |
| |
| mod client; |
| use crate::client::{ |
| AdbcConnectionCore as CoreConnection, AdbcDatabaseCore as CoreDatabase, |
| AdbcResultIteratorCore as CoreResultIterator, AdbcStatementCore as CoreStatement, ClientError, |
| ConnectOptions as CoreConnectOptions, GetObjectsOptions as CoreGetObjectsOptions, |
| GetTableSchemaOptions as CoreGetTableSchemaOptions, |
| }; |
| use adbc_core::options::AdbcVersion; |
| use napi::bindgen_prelude::{ |
| AsyncTask, Buffer, Error, JsObjectValue, Result, Status, ToNapiValue, Unknown, |
| }; |
| use napi::{Env, Task}; |
| use std::collections::HashMap; |
| use std::sync::{Arc, Mutex}; |
| |
| #[macro_use] |
| extern crate napi_derive; |
| |
| fn to_napi_err(err: ClientError) -> Error { |
| match err { |
| ClientError::Adbc(e) => Error::new(Status::GenericFailure, e.message), |
| ClientError::Arrow(e) => Error::new(Status::GenericFailure, format!("Arrow Error: {e}")), |
| ClientError::Other(e) => Error::new(Status::GenericFailure, format!("Internal Error: {e}")), |
| } |
| } |
| |
| /// Converts an ADBC error into a `napi::Error` whose JS form is a structured `AdbcError` |
| /// object with `name`, `code`, `vendorCode`, and `sqlState` properties. |
| /// |
| /// The JS object is stored via `napi_ref` (napi-rs's `maybe_raw` field) so napi-rs uses it |
| /// directly as the throw/rejection value. This avoids `env.throw()`, which sets a V8 pending |
| /// exception that would escape `Task::reject` as an uncaughtException instead of rejecting |
| /// the Promise. |
| fn build_adbc_err(env: Env, ae: adbc_core::error::Error) -> Error { |
| let adbc_core::error::Error { |
| message, |
| status, |
| vendor_code, |
| sqlstate, |
| .. |
| } = ae; |
| // details (ADBC 1.1.0 key-value metadata) are not yet exposed to JS. |
| let Ok(mut js_err) = env.create_error(Error::new(Status::GenericFailure, message.as_str())) |
| else { |
| return Error::new(Status::GenericFailure, message); |
| }; |
| let _ = js_err.set_named_property("name", "AdbcError"); |
| let _ = js_err.set_named_property("code", format!("{status:?}").as_str()); |
| if vendor_code != i32::MIN { |
| let _ = js_err.set_named_property("vendorCode", vendor_code); |
| } |
| let sqlstate_bytes: [u8; 5] = sqlstate.map(|c| c as u8); |
| if sqlstate_bytes.iter().any(|&b| b != 0) { |
| let sql_state = std::str::from_utf8(&sqlstate_bytes).unwrap_or(""); |
| let _ = js_err.set_named_property("sqlState", sql_state); |
| } |
| let raw_env = env.raw(); |
| match unsafe { ToNapiValue::to_napi_value(raw_env, js_err) } { |
| Ok(raw_val) => Error::from(unsafe { Unknown::from_raw_unchecked(raw_env, raw_val) }), |
| Err(fallback) => fallback, |
| } |
| } |
| |
| /// For synchronous `#[napi]` methods: converts a `ClientError` to a `napi::Error` that, |
| /// when propagated by napi-rs, throws a structured `AdbcError` JS object. |
| fn sync_adbc_err(err: ClientError, env: Env) -> Error { |
| match err { |
| ClientError::Adbc(ae) => build_adbc_err(env, ae), |
| other => to_napi_err(other), |
| } |
| } |
| |
| /// Captures an ADBC error from a thread-pool `compute()` result into `sink` so that |
| /// `reject()` (main JS thread) can later call `build_adbc_err` with an `Env`. |
| fn capture_adbc_err<T>( |
| result: crate::client::Result<T>, |
| sink: &mut Option<adbc_core::error::Error>, |
| ) -> Result<T> { |
| result.map_err(|e| match e { |
| ClientError::Adbc(ae) => { |
| let napi_err = Error::new(Status::GenericFailure, ae.message.as_str()); |
| *sink = Some(ae); |
| napi_err |
| } |
| other => to_napi_err(other), |
| }) |
| } |
| |
| fn reject_adbc<T>( |
| env: Env, |
| adbc_err: &mut Option<adbc_core::error::Error>, |
| err: Error, |
| ) -> Result<T> { |
| Err( |
| adbc_err |
| .take() |
| .map(|ae| build_adbc_err(env, ae)) |
| .unwrap_or(err), |
| ) |
| } |
| |
| fn closed_err() -> Error { |
| Error::new(Status::GenericFailure, "Object is closed") |
| } |
| |
| #[napi] |
| pub fn crate_version() -> String { |
| env!("CARGO_PKG_VERSION").to_string() |
| } |
| |
| #[napi] |
| pub fn default_adbc_version() -> String { |
| match AdbcVersion::default() { |
| AdbcVersion::V100 => "1.0.0".to_string(), |
| AdbcVersion::V110 => "1.1.0".to_string(), |
| _ => "unknown".to_string(), |
| } |
| } |
| |
| #[napi] |
| pub fn default_load_flags() -> u32 { |
| adbc_core::LOAD_FLAG_DEFAULT |
| } |
| |
| // Options |
| #[napi(object)] |
| pub struct _NativeConnectOptions { |
| pub driver: Option<String>, |
| pub entrypoint: Option<String>, |
| pub manifest_search_paths: Option<Vec<String>>, |
| pub profile_search_paths: Option<Vec<String>>, |
| pub load_flags: Option<u32>, |
| pub database_options: Option<HashMap<String, String>>, |
| } |
| |
| impl From<_NativeConnectOptions> for CoreConnectOptions { |
| fn from(opts: _NativeConnectOptions) -> Self { |
| Self { |
| driver: opts.driver, |
| entrypoint: opts.entrypoint, |
| manifest_search_paths: opts.manifest_search_paths, |
| profile_search_paths: opts.profile_search_paths, |
| load_flags: opts.load_flags, |
| database_options: opts.database_options, |
| } |
| } |
| } |
| |
| #[napi(object)] |
| pub struct GetObjectsOptions { |
| pub depth: i32, |
| pub catalog: Option<String>, |
| pub db_schema: Option<String>, |
| pub table_name: Option<String>, |
| pub table_type: Option<Vec<String>>, |
| pub column_name: Option<String>, |
| } |
| |
| impl From<GetObjectsOptions> for CoreGetObjectsOptions { |
| fn from(opts: GetObjectsOptions) -> Self { |
| Self { |
| depth: opts.depth, |
| catalog: opts.catalog, |
| db_schema: opts.db_schema, |
| table_name: opts.table_name, |
| table_type: opts.table_type, |
| column_name: opts.column_name, |
| } |
| } |
| } |
| |
| #[napi(object)] |
| pub struct GetTableSchemaOptions { |
| pub catalog: Option<String>, |
| pub db_schema: Option<String>, |
| pub table_name: String, |
| } |
| |
| impl From<GetTableSchemaOptions> for CoreGetTableSchemaOptions { |
| fn from(opts: GetTableSchemaOptions) -> Self { |
| Self { |
| catalog: opts.catalog, |
| db_schema: opts.db_schema, |
| table_name: opts.table_name, |
| } |
| } |
| } |
| |
| // --- Database --- |
| |
| #[napi] |
| pub struct _NativeAdbcDatabase { |
| inner: Option<Arc<CoreDatabase>>, |
| } |
| |
| #[napi] |
| impl _NativeAdbcDatabase { |
| #[napi(constructor)] |
| pub fn new(env: Env, opts: _NativeConnectOptions) -> Result<Self> { |
| let db = CoreDatabase::new(opts.into()).map_err(|e| sync_adbc_err(e, env))?; |
| Ok(Self { |
| inner: Some(Arc::new(db)), |
| }) |
| } |
| |
| #[napi] |
| pub fn connect( |
| &self, |
| options: Option<HashMap<String, String>>, |
| ) -> Result<AsyncTask<ConnectTask>> { |
| let db = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(ConnectTask { |
| database: db.clone(), |
| options, |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn close(&mut self) -> Result<()> { |
| self.inner.take(); |
| Ok(()) |
| } |
| } |
| |
| pub struct ConnectTask { |
| database: Arc<CoreDatabase>, |
| options: Option<HashMap<String, String>>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for ConnectTask { |
| type Output = CoreConnection; |
| type JsValue = _NativeAdbcConnection; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| capture_adbc_err( |
| self.database.connect(self.options.take()), |
| &mut self.adbc_err, |
| ) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(_NativeAdbcConnection { |
| inner: Some(Arc::new(output)), |
| }) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| // --- Connection --- |
| |
| #[napi] |
| pub struct _NativeAdbcConnection { |
| inner: Option<Arc<CoreConnection>>, |
| } |
| |
| #[napi] |
| impl _NativeAdbcConnection { |
| #[napi] |
| pub fn create_statement(&self) -> Result<AsyncTask<CreateStatementTask>> { |
| let conn = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(CreateStatementTask { |
| connection: conn.clone(), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn set_option(&self, env: Env, key: String, value: String) -> Result<()> { |
| let conn = self.inner.as_ref().ok_or_else(closed_err)?; |
| conn |
| .set_option(&key, &value) |
| .map_err(|e| sync_adbc_err(e, env)) |
| } |
| |
| #[napi] |
| pub fn get_objects(&self, opts: GetObjectsOptions) -> Result<AsyncTask<GetObjectsTask>> { |
| let conn = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(GetObjectsTask { |
| connection: conn.clone(), |
| options: Some(opts.into()), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn get_table_schema( |
| &self, |
| opts: GetTableSchemaOptions, |
| ) -> Result<AsyncTask<GetTableSchemaTask>> { |
| let conn = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(GetTableSchemaTask { |
| connection: conn.clone(), |
| options: Some(opts.into()), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn get_table_types(&self) -> Result<AsyncTask<GetTableTypesTask>> { |
| let conn = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(GetTableTypesTask { |
| connection: conn.clone(), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn get_info(&self, info_codes: Option<Vec<u32>>) -> Result<AsyncTask<GetInfoTask>> { |
| let conn = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(GetInfoTask { |
| connection: conn.clone(), |
| info_codes, |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn commit(&self) -> Result<AsyncTask<CommitTask>> { |
| let conn = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(CommitTask { |
| connection: conn.clone(), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn rollback(&self) -> Result<AsyncTask<RollbackTask>> { |
| let conn = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(RollbackTask { |
| connection: conn.clone(), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn close(&mut self) -> Result<()> { |
| self.inner.take(); |
| Ok(()) |
| } |
| } |
| |
| pub struct CreateStatementTask { |
| connection: Arc<CoreConnection>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for CreateStatementTask { |
| type Output = CoreStatement; |
| type JsValue = _NativeAdbcStatement; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| capture_adbc_err(self.connection.new_statement(), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(_NativeAdbcStatement { |
| inner: Some(Arc::new(Mutex::new(output))), |
| stream_sender: None, |
| }) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| // Metadata Tasks |
| |
| pub struct GetObjectsTask { |
| connection: Arc<CoreConnection>, |
| options: Option<CoreGetObjectsOptions>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for GetObjectsTask { |
| type Output = CoreResultIterator; |
| type JsValue = _NativeAdbcResultIterator; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| let opts = self.options.take().expect("compute called twice"); |
| capture_adbc_err(self.connection.get_objects(opts), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(_NativeAdbcResultIterator { |
| inner: Some(Arc::new(Mutex::new(output))), |
| }) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct GetTableSchemaTask { |
| connection: Arc<CoreConnection>, |
| options: Option<CoreGetTableSchemaOptions>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for GetTableSchemaTask { |
| type Output = Vec<u8>; |
| type JsValue = Buffer; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| let opts = self.options.take().expect("compute called twice"); |
| capture_adbc_err(self.connection.get_table_schema(opts), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(Buffer::from(output)) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct GetTableTypesTask { |
| connection: Arc<CoreConnection>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for GetTableTypesTask { |
| type Output = CoreResultIterator; |
| type JsValue = _NativeAdbcResultIterator; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| capture_adbc_err(self.connection.get_table_types(), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(_NativeAdbcResultIterator { |
| inner: Some(Arc::new(Mutex::new(output))), |
| }) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct GetInfoTask { |
| connection: Arc<CoreConnection>, |
| info_codes: Option<Vec<u32>>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for GetInfoTask { |
| type Output = CoreResultIterator; |
| type JsValue = _NativeAdbcResultIterator; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| capture_adbc_err( |
| self.connection.get_info(self.info_codes.take()), |
| &mut self.adbc_err, |
| ) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(_NativeAdbcResultIterator { |
| inner: Some(Arc::new(Mutex::new(output))), |
| }) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct CommitTask { |
| connection: Arc<CoreConnection>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for CommitTask { |
| type Output = (); |
| type JsValue = (); |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| capture_adbc_err(self.connection.commit(), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result<Self::JsValue> { |
| Ok(()) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct RollbackTask { |
| connection: Arc<CoreConnection>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for RollbackTask { |
| type Output = (); |
| type JsValue = (); |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| capture_adbc_err(self.connection.rollback(), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result<Self::JsValue> { |
| Ok(()) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| // --- Statement --- |
| |
| #[napi] |
| pub struct _NativeAdbcStatement { |
| inner: Option<Arc<Mutex<CoreStatement>>>, |
| stream_sender: Option<std::sync::mpsc::Sender<Vec<u8>>>, |
| } |
| |
| #[napi] |
| impl _NativeAdbcStatement { |
| #[napi] |
| pub fn set_sql_query(&self, env: Env, query: String) -> Result<()> { |
| let mutex = self.inner.as_ref().ok_or_else(closed_err)?; |
| let mut stmt = mutex |
| .lock() |
| .map_err(|e| Error::from_reason(e.to_string()))?; |
| stmt |
| .set_sql_query(&query) |
| .map_err(|e| sync_adbc_err(e, env)) |
| } |
| |
| #[napi] |
| pub fn set_option(&self, env: Env, key: String, value: String) -> Result<()> { |
| let mutex = self.inner.as_ref().ok_or_else(closed_err)?; |
| let mut stmt = mutex |
| .lock() |
| .map_err(|e| Error::from_reason(e.to_string()))?; |
| stmt |
| .set_option(&key, &value) |
| .map_err(|e| sync_adbc_err(e, env)) |
| } |
| |
| #[napi] |
| pub fn execute_query(&self) -> Result<AsyncTask<ExecuteQueryTask>> { |
| let mutex = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(ExecuteQueryTask { |
| statement: mutex.clone(), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn execute_update(&self) -> Result<AsyncTask<ExecuteUpdateTask>> { |
| let mutex = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(ExecuteUpdateTask { |
| statement: mutex.clone(), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn bind(&self, data: Buffer) -> Result<AsyncTask<BindTask>> { |
| let mutex = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(BindTask { |
| statement: mutex.clone(), |
| data: data.to_vec(), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn start_bind_stream_execute( |
| &mut self, |
| schema_bytes: Buffer, |
| ) -> Result<AsyncTask<BindStreamExecuteTask>> { |
| let mutex = self.inner.as_ref().ok_or_else(closed_err)?; |
| // Unbounded channel: a bounded channel would block the JS main thread. |
| let (sender, receiver) = std::sync::mpsc::channel::<Vec<u8>>(); |
| self.stream_sender = Some(sender); |
| Ok(AsyncTask::new(BindStreamExecuteTask { |
| statement: mutex.clone(), |
| schema_bytes: schema_bytes.to_vec(), |
| receiver: Some(receiver), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn start_bind_stream(&mut self, schema_bytes: Buffer) -> Result<AsyncTask<BindStreamTask>> { |
| let mutex = self.inner.as_ref().ok_or_else(closed_err)?; |
| // Unbounded channel: a bounded channel would block the JS main thread. |
| let (sender, receiver) = std::sync::mpsc::channel::<Vec<u8>>(); |
| self.stream_sender = Some(sender); |
| Ok(AsyncTask::new(BindStreamTask { |
| statement: mutex.clone(), |
| schema_bytes: schema_bytes.to_vec(), |
| receiver: Some(receiver), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn push_batch(&self, data: Buffer) -> Result<()> { |
| let sender = self |
| .stream_sender |
| .as_ref() |
| .ok_or_else(|| Error::new(Status::GenericFailure, "No active bind stream"))?; |
| sender |
| .send(data.to_vec()) |
| .map_err(|_| Error::new(Status::GenericFailure, "Stream channel closed"))?; |
| Ok(()) |
| } |
| |
| #[napi] |
| pub fn end_stream(&mut self) -> Result<()> { |
| self.stream_sender.take(); |
| Ok(()) |
| } |
| |
| #[napi] |
| pub fn close(&mut self) -> Result<()> { |
| self.inner.take(); |
| self.stream_sender.take(); |
| Ok(()) |
| } |
| } |
| |
| pub struct BindStreamExecuteTask { |
| statement: Arc<Mutex<CoreStatement>>, |
| schema_bytes: Vec<u8>, |
| receiver: Option<std::sync::mpsc::Receiver<Vec<u8>>>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for BindStreamExecuteTask { |
| type Output = i64; |
| type JsValue = i64; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| let mut stmt = self |
| .statement |
| .lock() |
| .map_err(|e| Error::from_reason(e.to_string()))?; |
| let receiver = self.receiver.take().expect("compute called twice"); |
| let schema_bytes = std::mem::take(&mut self.schema_bytes); |
| capture_adbc_err( |
| stmt.bind_stream_and_execute(schema_bytes, receiver), |
| &mut self.adbc_err, |
| ) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(output) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct BindStreamTask { |
| statement: Arc<Mutex<CoreStatement>>, |
| schema_bytes: Vec<u8>, |
| receiver: Option<std::sync::mpsc::Receiver<Vec<u8>>>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for BindStreamTask { |
| type Output = (); |
| type JsValue = (); |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| let mut stmt = self |
| .statement |
| .lock() |
| .map_err(|e| Error::from_reason(e.to_string()))?; |
| let receiver = self.receiver.take().expect("compute called twice"); |
| let schema_bytes = std::mem::take(&mut self.schema_bytes); |
| capture_adbc_err( |
| stmt.bind_channel_stream(schema_bytes, receiver), |
| &mut self.adbc_err, |
| ) |
| } |
| |
| fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result<Self::JsValue> { |
| Ok(()) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct ExecuteQueryTask { |
| statement: Arc<Mutex<CoreStatement>>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for ExecuteQueryTask { |
| type Output = CoreResultIterator; |
| type JsValue = _NativeAdbcResultIterator; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| let mut stmt = self |
| .statement |
| .lock() |
| .map_err(|e| Error::from_reason(e.to_string()))?; |
| capture_adbc_err(stmt.execute_query(), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(_NativeAdbcResultIterator { |
| inner: Some(Arc::new(Mutex::new(output))), |
| }) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct ExecuteUpdateTask { |
| statement: Arc<Mutex<CoreStatement>>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for ExecuteUpdateTask { |
| type Output = i64; |
| type JsValue = i64; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| let mut stmt = self |
| .statement |
| .lock() |
| .map_err(|e| Error::from_reason(e.to_string()))?; |
| capture_adbc_err(stmt.execute_update(), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(output) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| pub struct BindTask { |
| statement: Arc<Mutex<CoreStatement>>, |
| data: Vec<u8>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for BindTask { |
| type Output = (); |
| type JsValue = (); |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| let mut stmt = self |
| .statement |
| .lock() |
| .map_err(|e| Error::from_reason(e.to_string()))?; |
| capture_adbc_err( |
| stmt.bind(std::mem::take(&mut self.data)), |
| &mut self.adbc_err, |
| ) |
| } |
| |
| fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result<Self::JsValue> { |
| Ok(()) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| // --- Iterators --- |
| |
| pub struct IteratorNextTask { |
| iterator: Arc<Mutex<CoreResultIterator>>, |
| adbc_err: Option<adbc_core::error::Error>, |
| } |
| |
| impl Task for IteratorNextTask { |
| type Output = Option<Vec<u8>>; |
| type JsValue = Option<Buffer>; |
| |
| fn compute(&mut self) -> Result<Self::Output> { |
| let mut iterator = self |
| .iterator |
| .lock() |
| .map_err(|e| Error::from_reason(e.to_string()))?; |
| capture_adbc_err(iterator.next(), &mut self.adbc_err) |
| } |
| |
| fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> { |
| Ok(output.map(Buffer::from)) |
| } |
| |
| fn reject(&mut self, env: Env, err: Error) -> Result<Self::JsValue> { |
| reject_adbc(env, &mut self.adbc_err, err) |
| } |
| } |
| |
| #[napi] |
| pub struct _NativeAdbcResultIterator { |
| inner: Option<Arc<Mutex<CoreResultIterator>>>, |
| } |
| |
| #[napi] |
| impl _NativeAdbcResultIterator { |
| #[napi] |
| pub fn next(&self) -> Result<AsyncTask<IteratorNextTask>> { |
| let iterator = self.inner.as_ref().ok_or_else(closed_err)?; |
| Ok(AsyncTask::new(IteratorNextTask { |
| iterator: iterator.clone(), |
| adbc_err: None, |
| })) |
| } |
| |
| #[napi] |
| pub fn close(&mut self) -> Result<()> { |
| self.inner.take(); |
| Ok(()) |
| } |
| } |