paleolimbot commented on code in PR #681: URL: https://github.com/apache/sedona-db/pull/681#discussion_r2896902418
########## c/sedona-gdal/src/register.rs: ########## @@ -0,0 +1,314 @@ +// 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. + +use crate::errors::GdalInitLibraryError; +use crate::gdal_api::GdalApi; +use std::path::PathBuf; +use std::sync::{Mutex, OnceLock}; + +/// Minimum GDAL version required by sedona-gdal. +#[cfg(feature = "gdal-sys")] +const MIN_GDAL_VERSION_MAJOR: i32 = 3; +#[cfg(feature = "gdal-sys")] +const MIN_GDAL_VERSION_MINOR: i32 = 4; + +/// Builder for the global [`GdalApi`]. +/// +/// Provides a way to configure how GDAL is loaded before the first use. +/// Use [`configure_global_gdal_api`] to install a builder, then the actual +/// loading happens lazily on the first call to [`get_global_gdal_api`]. +/// +/// # Examples +/// +/// ```no_run +/// use sedona_gdal::register::{GdalApiBuilder, configure_global_gdal_api}; +/// +/// // Configure with a specific shared library path +/// let builder = GdalApiBuilder::default() +/// .with_shared_library("/usr/lib/libgdal.so".into()); +/// configure_global_gdal_api(builder).unwrap(); +/// ``` +#[derive(Default)] +pub struct GdalApiBuilder { + shared_library: Option<PathBuf>, +} + +impl GdalApiBuilder { + /// Set the path to the GDAL shared library. + /// + /// If unset, GDAL symbols will be resolved from the current process image + /// (equivalent to `dlopen(NULL)`), which requires GDAL to already be linked + /// into the process (e.g. via `gdal-sys`). + /// + /// Note that the path is passed directly to `dlopen()`/`LoadLibrary()`, + /// which takes into account the working directory. As a security measure, + /// applications may wish to verify that the path is absolute. This should + /// not be specified from untrusted input. + pub fn with_shared_library(self, path: PathBuf) -> Self { + Self { + shared_library: Some(path), + } + } + + /// Build a [`GdalApi`] with the configured options. + /// + /// When `shared_library` is set, loads GDAL from that path. Otherwise, + /// resolves symbols from the current process (with an optional + /// compile-time version check when the `gdal-sys` feature is enabled). + pub fn build(&self) -> Result<GdalApi, GdalInitLibraryError> { + if let Some(shared_library) = &self.shared_library { + GdalApi::try_from_shared_library(shared_library.clone()) + } else { + #[cfg(feature = "gdal-sys")] + check_gdal_version()?; + GdalApi::try_from_current_process() + } + } +} + +/// Global builder configuration, protected by a [`Mutex`]. +/// +/// Set via [`configure_global_gdal_api`] before the first call to +/// [`get_global_gdal_api`]. Multiple calls to `configure_global_gdal_api` +/// are allowed as long as the API has not been initialized yet. +/// The same mutex also serves as the initialization guard for +/// [`get_global_gdal_api`], eliminating the need for a separate lock. +static GDAL_API_BUILDER: Mutex<Option<GdalApiBuilder>> = Mutex::new(None); + +static GDAL_API: OnceLock<GdalApi> = OnceLock::new(); + +/// Get a reference to the global GDAL API, initializing it if not already done. +/// +/// On first call, reads the builder set by [`configure_global_gdal_api`] (or uses +/// [`GdalApiBuilder::default()`] if none was configured) and calls its `build()` +/// method to create the [`GdalApi`]. The result is stored in a process-global +/// `OnceLock` and reused for all subsequent calls. +pub fn get_global_gdal_api() -> Result<&'static GdalApi, GdalInitLibraryError> { Review Comment: Probably a higher level global makes more sense later (and perhaps even later, a session-scoped higher level abstraction like the CrsEngine to prevent a mess of feature flags when doing raster joins). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
