From 97c57bdd26363c8edc6c938304901f12f7e87b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lapeyre?= Date: Mon, 5 Aug 2024 14:23:45 +0200 Subject: [PATCH 1/4] Give the full server struct to the FDW when instanciating it. --- supabase-wrappers/src/instance.rs | 27 +++++++++++++++++-- supabase-wrappers/src/interface.rs | 3 ++- supabase-wrappers/src/lib.rs | 1 + wrappers/src/fdw/airtable_fdw/airtable_fdw.rs | 8 +++--- wrappers/src/fdw/auth0_fdw/auth0_fdw.rs | 8 +++--- wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs | 14 +++++----- .../src/fdw/clickhouse_fdw/clickhouse_fdw.rs | 6 ++--- wrappers/src/fdw/cognito_fdw/cognito_fdw.rs | 14 +++++----- wrappers/src/fdw/firebase_fdw/firebase_fdw.rs | 10 +++---- .../src/fdw/helloworld_fdw/helloworld_fdw.rs | 2 +- wrappers/src/fdw/logflare_fdw/logflare_fdw.rs | 8 +++--- wrappers/src/fdw/mssql_fdw/mssql_fdw.rs | 6 ++--- wrappers/src/fdw/redis_fdw/redis_fdw.rs | 6 ++--- wrappers/src/fdw/s3_fdw/s3_fdw.rs | 16 +++++------ wrappers/src/fdw/stripe_fdw/stripe_fdw.rs | 10 +++---- wrappers/src/fdw/wasm_fdw/wasm_fdw.rs | 12 ++++----- 16 files changed, 88 insertions(+), 63 deletions(-) diff --git a/supabase-wrappers/src/instance.rs b/supabase-wrappers/src/instance.rs index 3b4850c4..04d3047b 100644 --- a/supabase-wrappers/src/instance.rs +++ b/supabase-wrappers/src/instance.rs @@ -1,7 +1,17 @@ +use std::collections::HashMap; +use std::ffi::CStr; + use crate::prelude::*; use pgrx::pg_sys::panic::ErrorReport; use pgrx::prelude::*; +pub struct ForeignServer { + pub server_name: String, + pub server_type: String, + pub server_version: String, + pub options: HashMap +} + // create a fdw instance from its id pub(super) unsafe fn create_fdw_instance_from_server_id< E: Into, @@ -9,9 +19,22 @@ pub(super) unsafe fn create_fdw_instance_from_server_id< >( fserver_id: pg_sys::Oid, ) -> W { + let to_string = |raw: *mut std::ffi::c_char| -> String { + let c_str = CStr::from_ptr(raw); + c_str.to_str().map_err(|_| { + OptionsError::OptionValueIsInvalidUtf8( + String::from_utf8_lossy(c_str.to_bytes()).to_string(), + ) + }).report_unwrap().to_string() + }; let fserver = pg_sys::GetForeignServer(fserver_id); - let fserver_opts = options_to_hashmap((*fserver).options).report_unwrap(); - let wrapper = W::new(&fserver_opts); + let server = ForeignServer { + server_name: to_string((*fserver).servername), + server_type: to_string((*fserver).servertype), + server_version: to_string((*fserver).serverversion), + options: options_to_hashmap((*fserver).options).report_unwrap(), + }; + let wrapper = W::new(server); wrapper.report_unwrap() } diff --git a/supabase-wrappers/src/interface.rs b/supabase-wrappers/src/interface.rs index 733e7009..ee5fac9e 100644 --- a/supabase-wrappers/src/interface.rs +++ b/supabase-wrappers/src/interface.rs @@ -1,6 +1,7 @@ //! Provides interface types and trait to develop Postgres foreign data wrapper //! +use crate::instance::ForeignServer; use crate::FdwRoutine; use pgrx::pg_sys::panic::ErrorReport; use pgrx::prelude::{Date, Timestamp, TimestampWithTimeZone}; @@ -502,7 +503,7 @@ pub trait ForeignDataWrapper> { /// You can do any initalization in this function, like saving connection /// info or API url in an variable, but don't do heavy works like database /// connection or API call. - fn new(options: &HashMap) -> Result + fn new(server: ForeignServer) -> Result where Self: Sized; diff --git a/supabase-wrappers/src/lib.rs b/supabase-wrappers/src/lib.rs index 900eca8d..6299c8dc 100644 --- a/supabase-wrappers/src/lib.rs +++ b/supabase-wrappers/src/lib.rs @@ -299,6 +299,7 @@ pub mod utils; /// The prelude includes all necessary imports to make Wrappers work pub mod prelude { pub use crate::import_foreign_schema::*; + pub use crate::instance::ForeignServer; pub use crate::interface::*; pub use crate::options::*; pub use crate::utils::*; diff --git a/wrappers/src/fdw/airtable_fdw/airtable_fdw.rs b/wrappers/src/fdw/airtable_fdw/airtable_fdw.rs index c61f798a..fc2f003a 100644 --- a/wrappers/src/fdw/airtable_fdw/airtable_fdw.rs +++ b/wrappers/src/fdw/airtable_fdw/airtable_fdw.rs @@ -90,16 +90,16 @@ impl AirtableFdw { // TODO Add support for INSERT, UPDATE, DELETE impl ForeignDataWrapper for AirtableFdw { - fn new(options: &HashMap) -> AirtableFdwResult { - let base_url = options + fn new(server: ForeignServer) -> AirtableFdwResult { + let base_url = server.options .get("api_url") .map(|t| t.to_owned()) .unwrap_or_else(|| "https://api.airtable.com/v0".to_string()); - let client = match options.get("api_key") { + let client = match server.options.get("api_key") { Some(api_key) => Some(create_client(api_key)?), None => { - let key_id = require_option("api_key_id", options)?; + let key_id = require_option("api_key_id", &server.options)?; if let Some(api_key) = get_vault_secret(key_id) { Some(create_client(&api_key)?) } else { diff --git a/wrappers/src/fdw/auth0_fdw/auth0_fdw.rs b/wrappers/src/fdw/auth0_fdw/auth0_fdw.rs index 9ce78375..fff39ed2 100644 --- a/wrappers/src/fdw/auth0_fdw/auth0_fdw.rs +++ b/wrappers/src/fdw/auth0_fdw/auth0_fdw.rs @@ -94,12 +94,12 @@ impl ForeignDataWrapper for Auth0Fdw { // info or API url in an variable, but don't do any heavy works like making a // database connection or API call. - fn new(options: &HashMap) -> Result { - let url = require_option("url", options)?.to_string(); - let api_key = if let Some(api_key) = options.get("api_key") { + fn new(server: ForeignServer) -> Result { + let url = require_option("url", &server.options)?.to_string(); + let api_key = if let Some(api_key) = server.options.get("api_key") { api_key.clone() } else { - let api_key_id = options + let api_key_id = server.options .get("api_key_id") .expect("`api_key_id` must be set if `api_key` is not"); get_vault_secret(api_key_id).ok_or(Auth0FdwError::SecretNotFound(api_key_id.clone()))? diff --git a/wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs b/wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs index f772745f..bd9005d7 100644 --- a/wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs +++ b/wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs @@ -146,12 +146,12 @@ impl BigQueryFdw { } impl ForeignDataWrapper for BigQueryFdw { - fn new(options: &HashMap) -> Result { + fn new(server: ForeignServer) -> Result { let mut ret = BigQueryFdw { rt: create_async_runtime()?, client: None, - project_id: require_option("project_id", options)?.to_string(), - dataset_id: require_option("dataset_id", options)?.to_string(), + project_id: require_option("project_id", &server.options)?.to_string(), + dataset_id: require_option("dataset_id", &server.options)?.to_string(), table: "".to_string(), rowid_col: "".to_string(), tgt_cols: Vec::new(), @@ -160,13 +160,13 @@ impl ForeignDataWrapper for BigQueryFdw { }; // Is authentication mocked - let mock_auth: bool = options + let mock_auth: bool = server.options .get("mock_auth") .map(|t| t.to_owned()) .unwrap_or_else(|| "false".to_string()) == *"true"; - let api_endpoint = options + let api_endpoint = server.options .get("api_endpoint") .map(|t| t.to_owned()) .unwrap_or_else(|| "https://bigquery.googleapis.com/bigquery/v2".to_string()); @@ -182,10 +182,10 @@ impl ForeignDataWrapper for BigQueryFdw { serde_json::to_string_pretty(&dummy_auth_config) .expect("dummy auth config should not fail to serialize") } - false => match options.get("sa_key") { + false => match server.options.get("sa_key") { Some(sa_key) => sa_key.to_owned(), None => { - let sa_key_id = require_option("sa_key_id", options)?; + let sa_key_id = require_option("sa_key_id", &server.options)?; match get_vault_secret(sa_key_id) { Some(sa_key) => sa_key, None => return Ok(ret), diff --git a/wrappers/src/fdw/clickhouse_fdw/clickhouse_fdw.rs b/wrappers/src/fdw/clickhouse_fdw/clickhouse_fdw.rs index ccca2095..e069e251 100644 --- a/wrappers/src/fdw/clickhouse_fdw/clickhouse_fdw.rs +++ b/wrappers/src/fdw/clickhouse_fdw/clickhouse_fdw.rs @@ -201,12 +201,12 @@ impl ClickHouseFdw { } impl ForeignDataWrapper for ClickHouseFdw { - fn new(options: &HashMap) -> ClickHouseFdwResult { + fn new(server: ForeignServer) -> ClickHouseFdwResult { let rt = create_async_runtime()?; - let conn_str = match options.get("conn_string") { + let conn_str = match server.options.get("conn_string") { Some(conn_str) => conn_str.to_owned(), None => { - let conn_str_id = require_option("conn_string_id", options)?; + let conn_str_id = require_option("conn_string_id", &server.options)?; get_vault_secret(conn_str_id).unwrap_or_default() } }; diff --git a/wrappers/src/fdw/cognito_fdw/cognito_fdw.rs b/wrappers/src/fdw/cognito_fdw/cognito_fdw.rs index 22403af8..7a400064 100644 --- a/wrappers/src/fdw/cognito_fdw/cognito_fdw.rs +++ b/wrappers/src/fdw/cognito_fdw/cognito_fdw.rs @@ -97,16 +97,16 @@ impl ForeignDataWrapper for CognitoFdw { // info or API url in an variable, but don't do any heavy works like making a // database connection or API call. - fn new(options: &HashMap) -> Result { - let user_pool_id = require_option("user_pool_id", options)?.to_string(); - let aws_region = require_option("region", options)?.to_string(); + fn new(server: ForeignServer) -> Result { + let user_pool_id = require_option("user_pool_id", &server.options)?.to_string(); + let aws_region = require_option("region", &server.options)?.to_string(); - let aws_access_key_id = require_option("aws_access_key_id", options)?.to_string(); + let aws_access_key_id = require_option("aws_access_key_id", &server.options)?.to_string(); let aws_secret_access_key = - if let Some(aws_secret_access_key) = options.get("aws_secret_access_key") { + if let Some(aws_secret_access_key) = server.options.get("aws_secret_access_key") { aws_secret_access_key.clone() } else { - let aws_secret_access_key = options + let aws_secret_access_key = server.options .get("api_key_id") .expect("`api_key_id` must be set if `aws_secret_access_key` is not"); get_vault_secret(aws_secret_access_key).ok_or(CognitoFdwError::SecretNotFound( @@ -122,7 +122,7 @@ impl ForeignDataWrapper for CognitoFdw { let config = aws_config::load_defaults(BehaviorVersion::latest()).await; let mut builder = config.to_builder(); - if let Some(endpoint_url) = options.get("endpoint_url") { + if let Some(endpoint_url) = server.options.get("endpoint_url") { if !endpoint_url.is_empty() { builder.set_endpoint_url(Some(endpoint_url.clone())); } diff --git a/wrappers/src/fdw/firebase_fdw/firebase_fdw.rs b/wrappers/src/fdw/firebase_fdw/firebase_fdw.rs index 6a750e68..b0ac14fa 100644 --- a/wrappers/src/fdw/firebase_fdw/firebase_fdw.rs +++ b/wrappers/src/fdw/firebase_fdw/firebase_fdw.rs @@ -211,23 +211,23 @@ impl FirebaseFdw { } impl ForeignDataWrapper for FirebaseFdw { - fn new(options: &HashMap) -> FirebaseFdwResult { + fn new(server: ForeignServer) -> FirebaseFdwResult { let mut ret = Self { rt: create_async_runtime()?, - project_id: require_option("project_id", options)?.to_string(), + project_id: require_option("project_id", &server.options)?.to_string(), client: None, scan_result: Vec::default(), }; // get oauth2 access token if it is directly defined in options - let token = if let Some(access_token) = options.get("access_token") { + let token = if let Some(access_token) = server.options.get("access_token") { access_token.to_owned() } else { // otherwise, get it from the options or Vault - let sa_key = match options.get("sa_key") { + let sa_key = match server.options.get("sa_key") { Some(sa_key) => sa_key.to_owned(), None => { - let sa_key_id = require_option("sa_key_id", options)?; + let sa_key_id = require_option("sa_key_id", &server.options)?; match get_vault_secret(sa_key_id) { Some(sa_key) => sa_key, None => return Ok(ret), diff --git a/wrappers/src/fdw/helloworld_fdw/helloworld_fdw.rs b/wrappers/src/fdw/helloworld_fdw/helloworld_fdw.rs index c45c4a45..62d97c71 100644 --- a/wrappers/src/fdw/helloworld_fdw/helloworld_fdw.rs +++ b/wrappers/src/fdw/helloworld_fdw/helloworld_fdw.rs @@ -42,7 +42,7 @@ impl ForeignDataWrapper for HelloWorldFdw { // You can do any initalization in this new() function, like saving connection // info or API url in an variable, but don't do any heavy works like making a // database connection or API call. - fn new(_options: &HashMap) -> HelloWorldFdwResult { + fn new(_server: ForeignServer) -> HelloWorldFdwResult { Ok(Self { row_cnt: 0, tgt_cols: Vec::new(), diff --git a/wrappers/src/fdw/logflare_fdw/logflare_fdw.rs b/wrappers/src/fdw/logflare_fdw/logflare_fdw.rs index 7550764b..cb86b4b7 100644 --- a/wrappers/src/fdw/logflare_fdw/logflare_fdw.rs +++ b/wrappers/src/fdw/logflare_fdw/logflare_fdw.rs @@ -193,8 +193,8 @@ impl LogflareFdw { } impl ForeignDataWrapper for LogflareFdw { - fn new(options: &HashMap) -> LogflareFdwResult { - let base_url = options + fn new(server: ForeignServer) -> LogflareFdwResult { + let base_url = server.options .get("api_url") .map(|t| t.to_owned()) .map(|s| { @@ -205,10 +205,10 @@ impl ForeignDataWrapper for LogflareFdw { } }) .unwrap_or_else(|| LogflareFdw::BASE_URL.to_string()); - let client = match options.get("api_key") { + let client = match server.options.get("api_key") { Some(api_key) => Some(create_client(api_key)), None => { - let key_id = require_option("api_key_id", options)?; + let key_id = require_option("api_key_id", &server.options)?; get_vault_secret(key_id).map(|api_key| create_client(&api_key)) } } diff --git a/wrappers/src/fdw/mssql_fdw/mssql_fdw.rs b/wrappers/src/fdw/mssql_fdw/mssql_fdw.rs index a073294c..eb1c3595 100644 --- a/wrappers/src/fdw/mssql_fdw/mssql_fdw.rs +++ b/wrappers/src/fdw/mssql_fdw/mssql_fdw.rs @@ -170,12 +170,12 @@ impl MssqlFdw { } impl ForeignDataWrapper for MssqlFdw { - fn new(options: &HashMap) -> MssqlFdwResult { + fn new(server: ForeignServer) -> MssqlFdwResult { let rt = create_async_runtime()?; - let conn_str = match options.get("conn_string") { + let conn_str = match server.options.get("conn_string") { Some(conn_str) => conn_str.to_owned(), None => { - let conn_str_id = require_option("conn_string_id", options)?; + let conn_str_id = require_option("conn_string_id", &server.options)?; get_vault_secret(conn_str_id).unwrap_or_default() } }; diff --git a/wrappers/src/fdw/redis_fdw/redis_fdw.rs b/wrappers/src/fdw/redis_fdw/redis_fdw.rs index 2c53e897..ed6e2de7 100644 --- a/wrappers/src/fdw/redis_fdw/redis_fdw.rs +++ b/wrappers/src/fdw/redis_fdw/redis_fdw.rs @@ -240,11 +240,11 @@ impl RedisFdw { } impl ForeignDataWrapper for RedisFdw { - fn new(options: &HashMap) -> RedisFdwResult { - let conn_url = match options.get("conn_url") { + fn new(server: ForeignServer) -> RedisFdwResult { + let conn_url = match server.options.get("conn_url") { Some(url) => url.to_owned(), None => { - let conn_url_id = require_option("conn_url_id", options)?; + let conn_url_id = require_option("conn_url_id", &server.options)?; get_vault_secret(conn_url_id).unwrap_or_default() } }; diff --git a/wrappers/src/fdw/s3_fdw/s3_fdw.rs b/wrappers/src/fdw/s3_fdw/s3_fdw.rs index 0b2831f8..920af80e 100644 --- a/wrappers/src/fdw/s3_fdw/s3_fdw.rs +++ b/wrappers/src/fdw/s3_fdw/s3_fdw.rs @@ -112,7 +112,7 @@ impl S3Fdw { } impl ForeignDataWrapper for S3Fdw { - fn new(options: &HashMap) -> S3FdwResult { + fn new(server: ForeignServer) -> S3FdwResult { // cannot use create_async_runtime() as the runtime needs to be created // for multiple threads let rt = tokio::runtime::Runtime::new() @@ -128,27 +128,27 @@ impl ForeignDataWrapper for S3Fdw { }; // get is_mock flag - let is_mock: bool = options.get("is_mock") == Some(&"true".to_string()); + let is_mock: bool = server.options.get("is_mock") == Some(&"true".to_string()); // get credentials let creds = if is_mock { // LocalStack uses hardcoded credentials Some(("test".to_string(), "test".to_string())) } else { - match options.get("vault_access_key_id") { + match server.options.get("vault_access_key_id") { Some(vault_access_key_id) => { // if using credentials stored in Vault let vault_secret_access_key = - require_option("vault_secret_access_key", options)?; + require_option("vault_secret_access_key", &server.options)?; get_vault_secret(vault_access_key_id) .zip(get_vault_secret(vault_secret_access_key)) } None => { // if using credentials directly specified let aws_access_key_id = - require_option("aws_access_key_id", options)?.to_string(); + require_option("aws_access_key_id", &server.options)?.to_string(); let aws_secret_access_key = - require_option("aws_secret_access_key", options)?.to_string(); + require_option("aws_secret_access_key", &server.options)?.to_string(); Some((aws_access_key_id, aws_secret_access_key)) } } @@ -163,7 +163,7 @@ impl ForeignDataWrapper for S3Fdw { let region = if is_mock { default_region } else { - options + server.options .get("aws_region") .map(|t| t.to_owned()) .unwrap_or(default_region) @@ -177,7 +177,7 @@ impl ForeignDataWrapper for S3Fdw { let mut config_loader = aws_config::defaults(BehaviorVersion::latest()); // endpoint_url not supported as env var in rust https://github.com/awslabs/aws-sdk-rust/issues/932 - if let Some(endpoint_url) = options.get("endpoint_url") { + if let Some(endpoint_url) = server.options.get("endpoint_url") { config_loader = config_loader.endpoint_url(endpoint_url); } diff --git a/wrappers/src/fdw/stripe_fdw/stripe_fdw.rs b/wrappers/src/fdw/stripe_fdw/stripe_fdw.rs index 5bb14a30..5e46a25c 100644 --- a/wrappers/src/fdw/stripe_fdw/stripe_fdw.rs +++ b/wrappers/src/fdw/stripe_fdw/stripe_fdw.rs @@ -620,8 +620,8 @@ impl StripeFdw { } impl ForeignDataWrapper for StripeFdw { - fn new(options: &HashMap) -> StripeFdwResult { - let base_url = options + fn new(server: ForeignServer) -> StripeFdwResult { + let base_url = server.options .get("api_url") .map(|t| t.to_owned()) // Ensure trailing slash is always present, otherwise /v1 will get obliterated when @@ -634,11 +634,11 @@ impl ForeignDataWrapper for StripeFdw { } }) .unwrap_or_else(|| "https://api.stripe.com/v1/".to_string()); - let api_version = options.get("api_version").map(|t| t.as_str()); - let client = match options.get("api_key") { + let api_version = server.options.get("api_version").map(|t| t.as_str()); + let client = match server.options.get("api_key") { Some(api_key) => Some(create_client(api_key, api_version)), None => { - let key_id = require_option("api_key_id", options)?; + let key_id = require_option("api_key_id", &server.options)?; get_vault_secret(key_id).map(|api_key| create_client(&api_key, api_version)) } } diff --git a/wrappers/src/fdw/wasm_fdw/wasm_fdw.rs b/wrappers/src/fdw/wasm_fdw/wasm_fdw.rs index 4fdb2bac..b02b3f8d 100644 --- a/wrappers/src/fdw/wasm_fdw/wasm_fdw.rs +++ b/wrappers/src/fdw/wasm_fdw/wasm_fdw.rs @@ -132,11 +132,11 @@ impl WasmFdw { } impl ForeignDataWrapper for WasmFdw { - fn new(options: &HashMap) -> WasmFdwResult { - let pkg_url = require_option("fdw_package_url", options)?; - let pkg_name = require_option("fdw_package_name", options)?; - let pkg_version = require_option("fdw_package_version", options)?; - let pkg_checksum = options.get("fdw_package_checksum").map(|t| t.as_str()); + fn new(server: ForeignServer) -> WasmFdwResult { + let pkg_url = require_option("fdw_package_url", &server.options)?; + let pkg_name = require_option("fdw_package_name", &server.options)?; + let pkg_version = require_option("fdw_package_version", &server.options)?; + let pkg_checksum = server.options.get("fdw_package_checksum").map(|t| t.as_str()); let rt = create_async_runtime()?; @@ -151,7 +151,7 @@ impl ForeignDataWrapper for WasmFdw { Wrappers::add_to_linker(&mut linker, |host: &mut FdwHost| host)?; let mut fdw_host = FdwHost::new(rt); - fdw_host.svr_opts.clone_from(options); + fdw_host.svr_opts.clone_from(&server.options); let mut store = Store::new(&engine, fdw_host); let (bindings, _) = Wrappers::instantiate(&mut store, &component, &linker)?; From 8a0bdffde7c05970c8c17b501f110fd71aa5a365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lapeyre?= Date: Mon, 5 Aug 2024 14:32:48 +0200 Subject: [PATCH 2/4] Format code --- supabase-wrappers/src/instance.rs | 16 ++++++++++------ wrappers/src/fdw/airtable_fdw/airtable_fdw.rs | 3 ++- wrappers/src/fdw/auth0_fdw/auth0_fdw.rs | 3 ++- wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs | 6 ++++-- wrappers/src/fdw/cognito_fdw/cognito_fdw.rs | 3 ++- wrappers/src/fdw/logflare_fdw/logflare_fdw.rs | 3 ++- wrappers/src/fdw/s3_fdw/s3_fdw.rs | 3 ++- wrappers/src/fdw/stripe_fdw/stripe_fdw.rs | 3 ++- wrappers/src/fdw/wasm_fdw/wasm_fdw.rs | 5 ++++- 9 files changed, 30 insertions(+), 15 deletions(-) diff --git a/supabase-wrappers/src/instance.rs b/supabase-wrappers/src/instance.rs index 04d3047b..131e642d 100644 --- a/supabase-wrappers/src/instance.rs +++ b/supabase-wrappers/src/instance.rs @@ -9,7 +9,7 @@ pub struct ForeignServer { pub server_name: String, pub server_type: String, pub server_version: String, - pub options: HashMap + pub options: HashMap, } // create a fdw instance from its id @@ -21,11 +21,15 @@ pub(super) unsafe fn create_fdw_instance_from_server_id< ) -> W { let to_string = |raw: *mut std::ffi::c_char| -> String { let c_str = CStr::from_ptr(raw); - c_str.to_str().map_err(|_| { - OptionsError::OptionValueIsInvalidUtf8( - String::from_utf8_lossy(c_str.to_bytes()).to_string(), - ) - }).report_unwrap().to_string() + c_str + .to_str() + .map_err(|_| { + OptionsError::OptionValueIsInvalidUtf8( + String::from_utf8_lossy(c_str.to_bytes()).to_string(), + ) + }) + .report_unwrap() + .to_string() }; let fserver = pg_sys::GetForeignServer(fserver_id); let server = ForeignServer { diff --git a/wrappers/src/fdw/airtable_fdw/airtable_fdw.rs b/wrappers/src/fdw/airtable_fdw/airtable_fdw.rs index fc2f003a..f0417ec9 100644 --- a/wrappers/src/fdw/airtable_fdw/airtable_fdw.rs +++ b/wrappers/src/fdw/airtable_fdw/airtable_fdw.rs @@ -91,7 +91,8 @@ impl AirtableFdw { // TODO Add support for INSERT, UPDATE, DELETE impl ForeignDataWrapper for AirtableFdw { fn new(server: ForeignServer) -> AirtableFdwResult { - let base_url = server.options + let base_url = server + .options .get("api_url") .map(|t| t.to_owned()) .unwrap_or_else(|| "https://api.airtable.com/v0".to_string()); diff --git a/wrappers/src/fdw/auth0_fdw/auth0_fdw.rs b/wrappers/src/fdw/auth0_fdw/auth0_fdw.rs index fff39ed2..5745b2e8 100644 --- a/wrappers/src/fdw/auth0_fdw/auth0_fdw.rs +++ b/wrappers/src/fdw/auth0_fdw/auth0_fdw.rs @@ -99,7 +99,8 @@ impl ForeignDataWrapper for Auth0Fdw { let api_key = if let Some(api_key) = server.options.get("api_key") { api_key.clone() } else { - let api_key_id = server.options + let api_key_id = server + .options .get("api_key_id") .expect("`api_key_id` must be set if `api_key` is not"); get_vault_secret(api_key_id).ok_or(Auth0FdwError::SecretNotFound(api_key_id.clone()))? diff --git a/wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs b/wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs index bd9005d7..fbd9dc86 100644 --- a/wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs +++ b/wrappers/src/fdw/bigquery_fdw/bigquery_fdw.rs @@ -160,13 +160,15 @@ impl ForeignDataWrapper for BigQueryFdw { }; // Is authentication mocked - let mock_auth: bool = server.options + let mock_auth: bool = server + .options .get("mock_auth") .map(|t| t.to_owned()) .unwrap_or_else(|| "false".to_string()) == *"true"; - let api_endpoint = server.options + let api_endpoint = server + .options .get("api_endpoint") .map(|t| t.to_owned()) .unwrap_or_else(|| "https://bigquery.googleapis.com/bigquery/v2".to_string()); diff --git a/wrappers/src/fdw/cognito_fdw/cognito_fdw.rs b/wrappers/src/fdw/cognito_fdw/cognito_fdw.rs index 7a400064..9644d44e 100644 --- a/wrappers/src/fdw/cognito_fdw/cognito_fdw.rs +++ b/wrappers/src/fdw/cognito_fdw/cognito_fdw.rs @@ -106,7 +106,8 @@ impl ForeignDataWrapper for CognitoFdw { if let Some(aws_secret_access_key) = server.options.get("aws_secret_access_key") { aws_secret_access_key.clone() } else { - let aws_secret_access_key = server.options + let aws_secret_access_key = server + .options .get("api_key_id") .expect("`api_key_id` must be set if `aws_secret_access_key` is not"); get_vault_secret(aws_secret_access_key).ok_or(CognitoFdwError::SecretNotFound( diff --git a/wrappers/src/fdw/logflare_fdw/logflare_fdw.rs b/wrappers/src/fdw/logflare_fdw/logflare_fdw.rs index cb86b4b7..b240f17b 100644 --- a/wrappers/src/fdw/logflare_fdw/logflare_fdw.rs +++ b/wrappers/src/fdw/logflare_fdw/logflare_fdw.rs @@ -194,7 +194,8 @@ impl LogflareFdw { impl ForeignDataWrapper for LogflareFdw { fn new(server: ForeignServer) -> LogflareFdwResult { - let base_url = server.options + let base_url = server + .options .get("api_url") .map(|t| t.to_owned()) .map(|s| { diff --git a/wrappers/src/fdw/s3_fdw/s3_fdw.rs b/wrappers/src/fdw/s3_fdw/s3_fdw.rs index 920af80e..286b4d20 100644 --- a/wrappers/src/fdw/s3_fdw/s3_fdw.rs +++ b/wrappers/src/fdw/s3_fdw/s3_fdw.rs @@ -163,7 +163,8 @@ impl ForeignDataWrapper for S3Fdw { let region = if is_mock { default_region } else { - server.options + server + .options .get("aws_region") .map(|t| t.to_owned()) .unwrap_or(default_region) diff --git a/wrappers/src/fdw/stripe_fdw/stripe_fdw.rs b/wrappers/src/fdw/stripe_fdw/stripe_fdw.rs index 5e46a25c..f02b5be0 100644 --- a/wrappers/src/fdw/stripe_fdw/stripe_fdw.rs +++ b/wrappers/src/fdw/stripe_fdw/stripe_fdw.rs @@ -621,7 +621,8 @@ impl StripeFdw { impl ForeignDataWrapper for StripeFdw { fn new(server: ForeignServer) -> StripeFdwResult { - let base_url = server.options + let base_url = server + .options .get("api_url") .map(|t| t.to_owned()) // Ensure trailing slash is always present, otherwise /v1 will get obliterated when diff --git a/wrappers/src/fdw/wasm_fdw/wasm_fdw.rs b/wrappers/src/fdw/wasm_fdw/wasm_fdw.rs index b02b3f8d..c1162127 100644 --- a/wrappers/src/fdw/wasm_fdw/wasm_fdw.rs +++ b/wrappers/src/fdw/wasm_fdw/wasm_fdw.rs @@ -136,7 +136,10 @@ impl ForeignDataWrapper for WasmFdw { let pkg_url = require_option("fdw_package_url", &server.options)?; let pkg_name = require_option("fdw_package_name", &server.options)?; let pkg_version = require_option("fdw_package_version", &server.options)?; - let pkg_checksum = server.options.get("fdw_package_checksum").map(|t| t.as_str()); + let pkg_checksum = server + .options + .get("fdw_package_checksum") + .map(|t| t.as_str()); let rt = create_async_runtime()?; From 477da019b6446b75345b3fa00a60742b2e309ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lapeyre?= Date: Mon, 5 Aug 2024 14:46:39 +0200 Subject: [PATCH 3/4] Update doc --- supabase-wrappers/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/supabase-wrappers/src/lib.rs b/supabase-wrappers/src/lib.rs index 6299c8dc..32435a0f 100644 --- a/supabase-wrappers/src/lib.rs +++ b/supabase-wrappers/src/lib.rs @@ -90,8 +90,8 @@ //! type HelloWorldFdwResult = Result; //! //! impl ForeignDataWrapper for HelloWorldFdw { -//! fn new(options: &HashMap) -> HelloWorldFdwResult { -//! // 'options' is the key-value pairs defined in `CREATE SERVER` SQL, for example, +//! fn new(server: ForeignServer) -> HelloWorldFdwResult { +//! // 'server.options' is the key-value pairs defined in `CREATE SERVER` SQL, for example, //! // //! // create server my_helloworld_server //! // foreign data wrapper wrappers_helloworld @@ -172,7 +172,7 @@ //! } //! //! impl ForeignDataWrapper for HelloWorldFdw { -//! fn new(options: &HashMap) -> Result { +//! fn new(server: ForeignServer) -> Result { //! Ok(Self { //! row_cnt: 0, //! tgt_cols: Vec::new(), From 256d3ee628da35519d305942878ffc5a6a462efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Lapeyre?= Date: Mon, 5 Aug 2024 15:46:49 +0200 Subject: [PATCH 4/4] server_type and server_version are optional --- supabase-wrappers/src/instance.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/supabase-wrappers/src/instance.rs b/supabase-wrappers/src/instance.rs index 131e642d..47ca970d 100644 --- a/supabase-wrappers/src/instance.rs +++ b/supabase-wrappers/src/instance.rs @@ -7,8 +7,8 @@ use pgrx::prelude::*; pub struct ForeignServer { pub server_name: String, - pub server_type: String, - pub server_version: String, + pub server_type: Option, + pub server_version: Option, pub options: HashMap, } @@ -19,9 +19,12 @@ pub(super) unsafe fn create_fdw_instance_from_server_id< >( fserver_id: pg_sys::Oid, ) -> W { - let to_string = |raw: *mut std::ffi::c_char| -> String { + let to_string = |raw: *mut std::ffi::c_char| -> Option { + if raw.is_null() { + return None; + } let c_str = CStr::from_ptr(raw); - c_str + let value = c_str .to_str() .map_err(|_| { OptionsError::OptionValueIsInvalidUtf8( @@ -29,11 +32,12 @@ pub(super) unsafe fn create_fdw_instance_from_server_id< ) }) .report_unwrap() - .to_string() + .to_string(); + Some(value) }; let fserver = pg_sys::GetForeignServer(fserver_id); let server = ForeignServer { - server_name: to_string((*fserver).servername), + server_name: to_string((*fserver).servername).unwrap(), server_type: to_string((*fserver).servertype), server_version: to_string((*fserver).serverversion), options: options_to_hashmap((*fserver).options).report_unwrap(),