Skip to main content

lance_namespace_impls/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4//! Lance Namespace implementations.
5//!
6//! This crate provides various implementations of the Lance Namespace trait.
7//!
8//! ## Features
9//!
10//! - `rest`: REST API-based namespace implementation
11//! - `rest-adapter`: REST server adapter that exposes any namespace via HTTP
12//! - `dir-aws`, `dir-azure`, `dir-gcp`, `dir-oss`: Cloud storage backend support for directory namespace (via lance-io)
13//! - `credential-vendor-aws`, `credential-vendor-gcp`, `credential-vendor-azure`: Credential vending for cloud storage
14//!
15//! ## Implementations
16//!
17//! - `DirectoryNamespace`: Directory-based implementation (always available)
18//! - `RestNamespace`: REST API-based implementation (requires `rest` feature)
19//!
20//! ## Credential Vending
21//!
22//! The `credentials` module provides temporary credential vending for cloud storage:
23//! - AWS: STS AssumeRole with scoped IAM policies (requires `credential-vendor-aws` feature)
24//! - GCP: OAuth2 tokens with access boundaries (requires `credential-vendor-gcp` feature)
25//! - Azure: SAS tokens with user delegation keys (requires `credential-vendor-azure` feature)
26//!
27//! The credential vendor is automatically selected based on the table location URI scheme:
28//! - `s3://` for AWS
29//! - `gs://` for GCP
30//! - `az://` for Azure
31//!
32//! Configuration properties (prefixed with `credential_vendor.`, prefix is stripped):
33//!
34//! ```text
35//! # Required to enable credential vending
36//! credential_vendor.enabled = "true"
37//!
38//! # Common properties (apply to all providers)
39//! credential_vendor.permission = "read"          # read, write, or admin (default: read)
40//!
41//! # AWS-specific properties (for s3:// locations)
42//! credential_vendor.aws_role_arn = "arn:aws:iam::123456789012:role/MyRole"  # required for AWS
43//! credential_vendor.aws_duration_millis = "3600000"  # 1 hour (default, range: 15min-12hrs)
44//!
45//! # GCP-specific properties (for gs:// locations)
46//! # Note: GCP uses ADC; set GOOGLE_APPLICATION_CREDENTIALS env var for service account key
47//! # Note: GCP token duration cannot be configured; it's determined by the STS endpoint
48//! credential_vendor.gcp_service_account = "my-sa@project.iam.gserviceaccount.com"
49//! credential_vendor.gcp_workload_identity_provider = "projects/123456/locations/global/workloadIdentityPools/pool/providers/provider"
50//! credential_vendor.gcp_impersonation_service_account = "my-sa@project.iam.gserviceaccount.com"
51//!
52//! # Azure-specific properties (for az:// locations)
53//! credential_vendor.azure_account_name = "mystorageaccount"  # required for Azure
54//! credential_vendor.azure_tenant_id = "my-tenant-id"
55//! credential_vendor.azure_federated_client_id = "my-app-client-id"
56//! credential_vendor.azure_duration_millis = "3600000"  # 1 hour (default, up to 7 days)
57//! ```
58//!
59//! ## Usage
60//!
61//! The recommended way to connect to a namespace is using [`ConnectBuilder`]:
62//!
63//! ```no_run
64//! # use lance_namespace_impls::ConnectBuilder;
65//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
66//! let namespace = ConnectBuilder::new("dir")
67//!     .property("root", "/path/to/data")
68//!     .connect()
69//!     .await?;
70//! # Ok(())
71//! # }
72//! ```
73
74use std::collections::HashSet;
75
76use lance_namespace::NamespaceError;
77
78pub mod connect;
79pub mod context;
80pub mod credentials;
81pub mod dir;
82
83#[cfg(feature = "rest")]
84pub mod rest;
85
86#[cfg(feature = "rest-adapter")]
87pub mod rest_adapter;
88
89// Re-export connect builder
90pub use connect::ConnectBuilder;
91pub use context::{DynamicContextProvider, OperationInfo};
92pub use dir::{
93    DirectoryNamespace, DirectoryNamespaceBuilder, OpsMetrics, manifest::ManifestNamespace,
94};
95
96// Re-export credential vending
97pub use credentials::{
98    CredentialVendor, DEFAULT_CREDENTIAL_DURATION_MILLIS, VendedCredentials,
99    create_credential_vendor_for_location, detect_provider_from_uri, has_credential_vendor_config,
100    redact_credential,
101};
102
103#[cfg(feature = "credential-vendor-aws")]
104pub use credentials::aws::{AwsCredentialVendor, AwsCredentialVendorConfig};
105#[cfg(feature = "credential-vendor-aws")]
106pub use credentials::aws_props;
107
108#[cfg(feature = "credential-vendor-gcp")]
109pub use credentials::gcp::{GcpCredentialVendor, GcpCredentialVendorConfig};
110#[cfg(feature = "credential-vendor-gcp")]
111pub use credentials::gcp_props;
112
113#[cfg(feature = "credential-vendor-azure")]
114pub use credentials::azure::{AzureCredentialVendor, AzureCredentialVendorConfig};
115#[cfg(feature = "credential-vendor-azure")]
116pub use credentials::azure_props;
117
118#[cfg(feature = "rest")]
119pub use rest::{RestNamespace, RestNamespaceBuilder};
120
121#[cfg(feature = "rest-adapter")]
122pub use rest_adapter::{RestAdapter, RestAdapterConfig, RestAdapterHandle};
123
124/// Validate the `on` match key of a merge insert request.
125///
126/// The columns form a composite key, so an empty list matches nothing and a repeated
127/// column adds a redundant equality to the join.
128pub(crate) fn merge_insert_on_columns<'a>(
129    on: Option<&'a [String]>,
130    operation: &str,
131) -> lance_core::Result<&'a [String]> {
132    let on = on.ok_or_else(|| {
133        lance_core::Error::from(NamespaceError::InvalidInput {
134            message: format!("'on' field is required for {}", operation),
135        })
136    })?;
137
138    if on.is_empty() {
139        return Err(NamespaceError::InvalidInput {
140            message: format!("'on' field must name at least one column for {}", operation),
141        }
142        .into());
143    }
144
145    let mut seen = HashSet::with_capacity(on.len());
146    if let Some(duplicate) = on.iter().find(|column| !seen.insert(*column)) {
147        return Err(NamespaceError::InvalidInput {
148            message: format!(
149                "'on' field for {} names column '{}' more than once: {:?}",
150                operation, duplicate, on
151            ),
152        }
153        .into());
154    }
155
156    Ok(on)
157}