use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use arrow_array::RecordBatch;
use lance::dataset::ReadParams;
use lance_namespace::LanceNamespace;
use lance_namespace::models::{
CreateNamespaceRequest, CreateNamespaceResponse, DescribeNamespaceRequest,
DescribeNamespaceResponse, DropNamespaceRequest, DropNamespaceResponse, ListNamespacesRequest,
ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
};
use crate::data::scannable::Scannable;
use crate::error::Result;
use crate::job::Job;
use crate::listing::{ListingOptions, ListingPage};
use crate::materialized_view::CreateMaterializedViewRequest;
use crate::secrets::SecretInfo;
use crate::table::{BaseTable, WriteOptions};
use crate::view::ViewDescription;
pub mod listing;
pub mod namespace;
pub(crate) mod read_freshness;
pub trait DatabaseOptions {
fn serialize_into_map(&self, map: &mut HashMap<String, String>);
}
#[derive(Clone, Debug, Default)]
pub struct TableNamesRequest {
pub namespace_path: Vec<String>,
pub start_after: Option<String>,
pub limit: Option<u32>,
}
#[derive(Clone)]
pub struct OpenTableRequest {
pub name: String,
pub namespace_path: Vec<String>,
pub index_cache_size: Option<u32>,
pub lance_read_params: Option<ReadParams>,
pub location: Option<String>,
pub namespace_client: Option<Arc<dyn LanceNamespace>>,
pub managed_versioning: Option<bool>,
}
impl std::fmt::Debug for OpenTableRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpenTableRequest")
.field("name", &self.name)
.field("namespace_path", &self.namespace_path)
.field("index_cache_size", &self.index_cache_size)
.field("lance_read_params", &self.lance_read_params)
.field("location", &self.location)
.field("namespace_client", &self.namespace_client)
.field("managed_versioning", &self.managed_versioning)
.finish()
}
}
pub type TableBuilderCallback = Box<dyn FnOnce(OpenTableRequest) -> OpenTableRequest + Send>;
#[derive(Default)]
pub enum CreateTableMode {
#[default]
Create,
ExistOk(TableBuilderCallback),
Overwrite,
}
impl CreateTableMode {
pub fn exist_ok(
callback: impl FnOnce(OpenTableRequest) -> OpenTableRequest + Send + 'static,
) -> Self {
Self::ExistOk(Box::new(callback))
}
}
pub struct CreateTableRequest {
pub name: String,
pub namespace_path: Vec<String>,
pub data: Box<dyn Scannable>,
pub mode: CreateTableMode,
pub write_options: WriteOptions,
pub location: Option<String>,
pub namespace_client: Option<Arc<dyn LanceNamespace>>,
}
impl CreateTableRequest {
pub fn new(name: String, data: Box<dyn Scannable>) -> Self {
Self {
name,
namespace_path: vec![],
data,
mode: CreateTableMode::default(),
write_options: WriteOptions::default(),
location: None,
namespace_client: None,
}
}
}
#[derive(Clone, Debug)]
pub struct CloneTableRequest {
pub target_table_name: String,
pub target_namespace_path: Vec<String>,
pub source_uri: String,
pub source_version: Option<u64>,
pub source_tag: Option<String>,
pub is_shallow: bool,
pub namespace_client: Option<Arc<dyn LanceNamespace>>,
}
impl CloneTableRequest {
pub fn new(target_table_name: String, source_uri: String) -> Self {
Self {
target_table_name,
target_namespace_path: vec![],
source_uri,
source_version: None,
source_tag: None,
is_shallow: true,
namespace_client: None,
}
}
}
#[derive(Clone, Debug)]
pub struct ExecuteQueryRequest {
pub query: String,
pub default_namespace_path: Vec<String>,
pub parameters: Option<RecordBatch>,
}
impl ExecuteQueryRequest {
pub fn new(query: impl Into<String>) -> Self {
Self {
query: query.into(),
default_namespace_path: vec!["public".to_string()],
parameters: None,
}
}
}
pub enum ReadConsistency {
Manual,
Eventual(Duration),
Strong,
}
#[derive(Debug, Clone)]
pub struct JobInfo {
pub job_id: String,
pub table: String,
pub job_type: String,
pub state: String,
pub created_at_millis: i64,
}
#[derive(Debug, Clone)]
pub struct JobDescription {
pub job_id: String,
pub job_type: String,
pub state: String,
pub creation_ms: i64,
pub spec: serde_json::Value,
pub result: Option<serde_json::Value>,
pub failure: Option<crate::error::JobFailure>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PauseJobStatus {
Pausing,
AlreadyPaused,
Committing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResumeJobStatus {
Resumed,
StillPausing,
NotPaused,
}
fn job_op_not_supported<T>(what: &str) -> Result<T> {
Err(crate::error::Error::NotSupported {
message: format!("{} is not supported by this database", what),
})
}
fn function_catalog_not_supported<T>() -> Result<T> {
Err(crate::error::Error::NotSupported {
message: "Function catalog operations are not supported by this database".to_string(),
})
}
fn secret_catalog_not_supported<T>() -> Result<T> {
Err(crate::error::Error::NotSupported {
message: "Secret operations are not supported by this database".to_string(),
})
}
fn view_ops_not_supported<T>() -> Result<T> {
Err(crate::error::Error::NotSupported {
message: "View operations are not supported by this database".to_string(),
})
}
#[async_trait::async_trait]
pub trait Database:
Send + Sync + std::any::Any + std::fmt::Debug + std::fmt::Display + 'static
{
fn uri(&self) -> &str;
async fn read_consistency(&self) -> Result<ReadConsistency>;
async fn list_namespaces(
&self,
request: ListNamespacesRequest,
) -> Result<ListNamespacesResponse>;
async fn create_namespace(
&self,
request: CreateNamespaceRequest,
) -> Result<CreateNamespaceResponse>;
async fn drop_namespace(&self, request: DropNamespaceRequest) -> Result<DropNamespaceResponse>;
async fn describe_namespace(
&self,
request: DescribeNamespaceRequest,
) -> Result<DescribeNamespaceResponse>;
#[deprecated(note = "Use list_tables instead")]
async fn table_names(&self, request: TableNamesRequest) -> Result<Vec<String>>;
async fn list_tables(&self, request: ListTablesRequest) -> Result<ListTablesResponse>;
async fn create_table(&self, request: CreateTableRequest) -> Result<Arc<dyn BaseTable>>;
async fn clone_table(&self, request: CloneTableRequest) -> Result<Arc<dyn BaseTable>>;
async fn create_function_async(
&self,
_request: crate::function::FunctionRegistrationRequest,
_namespace_path: &[String],
) -> Result<crate::job::Job<crate::function::FunctionVersion>> {
function_catalog_not_supported()
}
#[doc(hidden)]
async fn create_materialized_view_async(
&self,
_request: CreateMaterializedViewRequest,
) -> Result<Job> {
job_op_not_supported("remote materialized-view creation")
}
#[doc(hidden)]
async fn drop_materialized_view_async(
&self,
_name: &str,
_namespace_path: &[String],
) -> Result<Job> {
job_op_not_supported("remote materialized-view drop")
}
#[doc(hidden)]
async fn list_materialized_views(
&self,
namespace_path: &[String],
options: ListingOptions,
) -> Result<ListingPage<String>> {
let response = self
.list_tables(ListTablesRequest {
id: Some(namespace_path.to_vec()),
page_token: options.page_token,
limit: options.page_limit.map(|limit| limit as i32),
..Default::default()
})
.await?;
let mut names = Vec::new();
for name in response.tables {
let Ok(table) = self
.open_table(OpenTableRequest {
name: name.clone(),
namespace_path: namespace_path.to_vec(),
index_cache_size: None,
lance_read_params: None,
location: None,
namespace_client: None,
managed_versioning: None,
})
.await
else {
continue;
};
let schema = table.schema().await?;
if crate::materialized_view::read_definition(schema.metadata())?.is_some() {
names.push(name);
}
}
Ok(ListingPage {
items: names,
page_token: response.page_token,
})
}
async fn get_function(
&self,
_name: &str,
_version: &str,
_namespace_path: &[String],
) -> Result<crate::function::FunctionVersion> {
function_catalog_not_supported()
}
async fn list_functions(
&self,
_namespace_path: &[String],
_options: ListingOptions,
) -> Result<ListingPage<crate::function::FunctionVersion>> {
function_catalog_not_supported()
}
async fn drop_function(
&self,
_name: &str,
_version: &str,
_namespace_path: &[String],
) -> Result<bool> {
function_catalog_not_supported()
}
async fn drop_function_async(
&self,
name: &str,
version: &str,
namespace_path: &[String],
) -> Result<(bool, crate::job::Job)> {
let dropped = self.drop_function(name, version, namespace_path).await?;
Ok((dropped, crate::job::Job::new_done()))
}
async fn create_secret(
&self,
_name: &str,
_value: &str,
_namespace_path: &[String],
) -> Result<()> {
secret_catalog_not_supported()
}
async fn alter_secret(
&self,
_name: &str,
_value: &str,
_namespace_path: &[String],
) -> Result<()> {
secret_catalog_not_supported()
}
async fn list_secrets(
&self,
_namespace_path: &[String],
_options: ListingOptions,
) -> Result<ListingPage<String>> {
secret_catalog_not_supported()
}
async fn drop_secret(&self, _name: &str, _namespace_path: &[String]) -> Result<()> {
secret_catalog_not_supported()
}
async fn describe_secret(&self, _name: &str, _namespace_path: &[String]) -> Result<SecretInfo> {
secret_catalog_not_supported()
}
async fn create_view(
&self,
_name: &str,
_query: &str,
_namespace_path: &[String],
) -> Result<ViewDescription> {
view_ops_not_supported()
}
async fn describe_view(
&self,
_name: &str,
_namespace_path: &[String],
) -> Result<ViewDescription> {
view_ops_not_supported()
}
async fn drop_view(&self, _name: &str, _namespace_path: &[String]) -> Result<()> {
view_ops_not_supported()
}
#[doc(hidden)]
async fn drop_view_async(&self, _name: &str, _namespace_path: &[String]) -> Result<Job> {
view_ops_not_supported()
}
async fn list_views(
&self,
_namespace_path: &[String],
_options: ListingOptions,
) -> Result<ListingPage<String>> {
view_ops_not_supported()
}
async fn open_job(&self, _job_id: &str) -> Result<crate::job::Job> {
job_op_not_supported("open_job")
}
async fn list_jobs(&self, _options: ListingOptions) -> Result<ListingPage<JobInfo>> {
job_op_not_supported("list_jobs")
}
async fn cancel_job(&self, _job_id: &str) -> Result<bool> {
job_op_not_supported("cancel_job")
}
async fn pause_job(&self, _job_id: &str) -> Result<PauseJobStatus> {
job_op_not_supported("pause_job")
}
async fn resume_job(&self, _job_id: &str) -> Result<ResumeJobStatus> {
job_op_not_supported("resume_job")
}
async fn execute_query_async(
&self,
_request: ExecuteQueryRequest,
) -> Result<crate::sql::Query> {
Err(crate::error::Error::NotSupported {
message: "SQL is not supported by this database".to_string(),
})
}
async fn describe_query(&self, _query_id: uuid::Uuid) -> Result<crate::sql::QueryDescription> {
Err(crate::error::Error::NotSupported {
message: "SQL is not supported by this database".to_string(),
})
}
async fn open_table(&self, request: OpenTableRequest) -> Result<Arc<dyn BaseTable>>;
async fn rename_table(
&self,
cur_name: &str,
new_name: &str,
cur_namespace_path: &[String],
new_namespace_path: &[String],
) -> Result<()>;
async fn drop_table(&self, name: &str, namespace_path: &[String]) -> Result<()>;
async fn drop_table_async(
&self,
name: &str,
namespace_path: &[String],
) -> Result<crate::job::Job> {
self.drop_table(name, namespace_path).await?;
Ok(crate::job::Job::new_done())
}
async fn drop_all_tables(&self, namespace_path: &[String]) -> Result<()>;
fn as_any(&self) -> &dyn std::any::Any;
async fn namespace_client(&self) -> Result<Arc<dyn LanceNamespace>>;
async fn namespace_client_config(&self) -> Result<(String, HashMap<String, String>)>;
}