Skip to content

Commit

Permalink
chore: hoist node.js bindings on top of rust sdk (#2879)
Browse files Browse the repository at this point in the history
  • Loading branch information
tychoish authored Apr 16, 2024
1 parent 36d667d commit beab1fd
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 362 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions bindings/nodejs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ crate-type = ["cdylib"]
workspace = true

[dependencies]
arrow_util = { path = "../../crates/arrow_util" }
datafusion_ext = { path = "../../crates/datafusion_ext" }
glaredb = { path = "../../crates/glaredb" }
ioutil = { path = "../../crates/ioutil" }
sqlexec = { path = "../../crates/sqlexec" }
metastore = { path = "../../crates/metastore" }
telemetry = { path = "../../crates/telemetry" }
pgsrv = { path = "../../crates/pgsrv" }
pgrepr = { path = "../../crates/pgrepr" }
datafusion_ext = { path = "../../crates/datafusion_ext" }
arrow_util = { path = "../../crates/arrow_util" }
pgsrv = { path = "../../crates/pgsrv" }
sqlexec = { path = "../../crates/sqlexec" }
telemetry = { path = "../../crates/telemetry" }
terminal_util = { path = "../../crates/terminal_util" }
futures = { workspace = true }
datafusion = { workspace = true }
Expand All @@ -32,6 +33,7 @@ napi = { version = "2.16.2", default-features = false, features = ["full"] }
napi-derive = "2.16.2"
once_cell = "1.19.0"
bytes = "1.6.0"
async-once-cell = "0.5.3"

[build-dependencies]
napi-build = "2.1.2"
2 changes: 1 addition & 1 deletion bindings/nodejs/glaredb.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const glaredb = require('./index.js')
// Some of methods can't be performed through `n-api`
// So we need to monkey patch them here
// The methods should still be defined in rust so we can keep a consistent `index.d.ts` file.
Object.assign(glaredb.JsLogicalPlan.prototype, {
Object.assign(glaredb.JsExecutionOutput.prototype, {
async toPolars() {
try {
const pl = require("nodejs-polars")
Expand Down
16 changes: 8 additions & 8 deletions bindings/nodejs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ export function connect(dataDirOrCloudUrl?: string | undefined | null, options?:
/** A connected session to a GlareDB database. */
export class Connection {
/**
* Returns a default connection to the global in-memory database.
* Returns the default connection to a global in-memory database.
*
* The database is only initialized once, and all subsequent calls
* will return the same connection, and therefore have access to the
* same data and database.
* The database is only initialized once, and all subsequent
* calls will return the same connection object and therefore
* have access to the same data.
*/
static defaultInMemory(): Promise<Connection>
/**
Expand Down Expand Up @@ -61,7 +61,7 @@ export class Connection {
* await con.sql('create table my_table (a int)').then(cursor => cursor.execute())
* ```
*/
sql(query: string): Promise<JsLogicalPlan>
sql(query: string): Promise<JsExecutionOutput>
/**
* Run a PRQL query against a GlareDB database. Does not change
* the state or dialect of the connection object.
Expand All @@ -70,14 +70,14 @@ export class Connection {
* import glaredb from "@glaredb/glaredb"
*
* let con = glaredb.connect()
* let cursor = await con.sql('from my_table | take 1');
* let cursor = await con.prql('from my_table | take 1');
* await cursor.show()
* ```
*
* All operations execute lazily when their results are
* processed.
*/
prql(query: string): Promise<JsLogicalPlan>
prql(query: string): Promise<JsExecutionOutput>
/**
* Execute a query.
*
Expand All @@ -96,7 +96,7 @@ export class Connection {
/** Close the current session. */
close(): Promise<void>
}
export class JsLogicalPlan {
export class JsExecutionOutput {
toString(): string
show(): Promise<void>
execute(): Promise<void>
Expand Down
4 changes: 2 additions & 2 deletions bindings/nodejs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,8 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { connect, Connection, JsLogicalPlan } = nativeBinding
const { connect, Connection, JsExecutionOutput } = nativeBinding

module.exports.connect = connect
module.exports.Connection = Connection
module.exports.JsLogicalPlan = JsLogicalPlan
module.exports.JsExecutionOutput = JsExecutionOutput
60 changes: 29 additions & 31 deletions bindings/nodejs/src/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
//! queries.
use std::collections::HashMap;
use std::sync::Arc;

use sqlexec::remote::client::RemoteClientType;

use crate::connection::Connection;
use crate::error::JsGlareDbError;

#[napi(object)]
#[derive(Default)]
Expand All @@ -17,44 +21,38 @@ pub struct ConnectOptions {
pub storage_options: Option<HashMap<String, String>>,
}

impl ConnectOptions {
fn spill_path(&mut self) -> Option<String> {
self.spill_path.take()
}

fn disable_tls(&self) -> bool {
self.disable_tls.unwrap_or(false)
}

fn cloud_addr(&self) -> String {
self.cloud_addr
.clone()
.unwrap_or(String::from("https://console.glaredb.com"))
}

fn location(&mut self) -> Option<String> {
self.location.take()
}

fn storage_options(&mut self) -> Option<HashMap<String, String>> {
self.storage_options.take()
impl TryFrom<ConnectOptions> for glaredb::ConnectOptions {
type Error = glaredb::ConnectOptionsBuilderError;

fn try_from(
val: ConnectOptions,
) -> Result<glaredb::ConnectOptions, glaredb::ConnectOptionsBuilderError> {
glaredb::ConnectOptionsBuilder::default()
.spill_path(val.spill_path)
.disable_tls_opt(val.disable_tls)
.cloud_addr_opt(val.cloud_addr)
.location(val.location)
.storage_options_opt(val.storage_options)
.client_type(RemoteClientType::Node)
.build()
}
}


/// Connect to a GlareDB database.
#[napi(catch_unwind)]
pub async fn connect(
data_dir_or_cloud_url: Option<String>,
options: Option<ConnectOptions>,
) -> napi::Result<Connection> {
let mut options = options.unwrap_or_default();
Connection::connect(
data_dir_or_cloud_url,
options.spill_path(),
options.disable_tls(),
options.cloud_addr(),
options.location(),
options.storage_options(),
)
.await
let mut options: glaredb::ConnectOptions = options
.unwrap_or_default()
.try_into()
.map_err(JsGlareDbError::from)?;

options.connection_target = data_dir_or_cloud_url;

Ok(Connection {
inner: Arc::new(options.connect().await.map_err(JsGlareDbError::from)?),
})
}
Loading

0 comments on commit beab1fd

Please sign in to comment.