Compare commits

...

1 Commits

Author SHA1 Message Date
lancedb automation a7b29dfabb chore: update lance dependency to v2.0.0-beta.9 2026-01-15 17:21:57 +00:00
15 changed files with 528 additions and 579 deletions
Generated
+351 -483
View File
File diff suppressed because it is too large Load Diff
+28 -28
View File
@@ -15,37 +15,37 @@ categories = ["database-implementations"]
rust-version = "1.78.0" rust-version = "1.78.0"
[workspace.dependencies] [workspace.dependencies]
lance = { "version" = "=1.0.2-rc.2", default-features = false, "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance = { "version" = "=2.0.0-beta.9", default-features = false, "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-core = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-core = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-datagen = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-datagen = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-file = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-file = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-io = { "version" = "=1.0.2-rc.2", default-features = false, "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-io = { "version" = "=2.0.0-beta.9", default-features = false, "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-index = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-index = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-linalg = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-linalg = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-namespace = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-namespace = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-namespace-impls = { "version" = "=1.0.2-rc.2", default-features = false, "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-namespace-impls = { "version" = "=2.0.0-beta.9", default-features = false, "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-table = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-table = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-testing = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-testing = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-datafusion = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-datafusion = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-encoding = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-encoding = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
lance-arrow = { "version" = "=1.0.2-rc.2", "tag" = "v1.0.2-rc.2", "git" = "https://github.com/lance-format/lance.git" } lance-arrow = { "version" = "=2.0.0-beta.9", "tag" = "v2.0.0-beta.9", "git" = "https://github.com/lance-format/lance.git" }
ahash = "0.8" ahash = "0.8"
# Note that this one does not include pyarrow # Note that this one does not include pyarrow
arrow = { version = "56.2", optional = false } arrow = { version = "57.2", optional = false }
arrow-array = "56.2" arrow-array = "57.2"
arrow-data = "56.2" arrow-data = "57.2"
arrow-ipc = "56.2" arrow-ipc = "57.2"
arrow-ord = "56.2" arrow-ord = "57.2"
arrow-schema = "56.2" arrow-schema = "57.2"
arrow-select = "56.2" arrow-select = "57.2"
arrow-cast = "56.2" arrow-cast = "57.2"
async-trait = "0" async-trait = "0"
datafusion = { version = "50.1", default-features = false } datafusion = { version = "51.0", default-features = false }
datafusion-catalog = "50.1" datafusion-catalog = "51.0"
datafusion-common = { version = "50.1", default-features = false } datafusion-common = { version = "51.0", default-features = false }
datafusion-execution = "50.1" datafusion-execution = "51.0"
datafusion-expr = "50.1" datafusion-expr = "51.0"
datafusion-physical-plan = "50.1" datafusion-physical-plan = "51.0"
env_logger = "0.11" env_logger = "0.11"
half = { "version" = "2.6.0", default-features = false, features = [ half = { "version" = "2.6.0", default-features = false, features = [
"num-traits", "num-traits",
+4 -4
View File
@@ -14,15 +14,15 @@ name = "_lancedb"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
arrow = { version = "56.2", features = ["pyarrow"] } arrow = { version = "57.2", features = ["pyarrow"] }
async-trait = "0.1" async-trait = "0.1"
lancedb = { path = "../rust/lancedb", default-features = false } lancedb = { path = "../rust/lancedb", default-features = false }
lance-core.workspace = true lance-core.workspace = true
lance-namespace.workspace = true lance-namespace.workspace = true
lance-io.workspace = true lance-io.workspace = true
env_logger.workspace = true env_logger.workspace = true
pyo3 = { version = "0.25", features = ["extension-module", "abi3-py39"] } pyo3 = { version = "0.26", features = ["extension-module", "abi3-py39"] }
pyo3-async-runtimes = { version = "0.25", features = [ pyo3-async-runtimes = { version = "0.26", features = [
"attributes", "attributes",
"tokio-runtime", "tokio-runtime",
] } ] }
@@ -32,7 +32,7 @@ snafu.workspace = true
tokio = { version = "1.40", features = ["sync"] } tokio = { version = "1.40", features = ["sync"] }
[build-dependencies] [build-dependencies]
pyo3-build-config = { version = "0.25", features = [ pyo3-build-config = { version = "0.26", features = [
"extension-module", "extension-module",
"abi3-py39", "abi3-py39",
] } ] }
+12 -5
View File
@@ -10,8 +10,7 @@ use arrow::{
use futures::stream::StreamExt; use futures::stream::StreamExt;
use lancedb::arrow::SendableRecordBatchStream; use lancedb::arrow::SendableRecordBatchStream;
use pyo3::{ use pyo3::{
exceptions::PyStopAsyncIteration, pyclass, pymethods, Bound, PyAny, PyObject, PyRef, PyResult, exceptions::PyStopAsyncIteration, pyclass, pymethods, Bound, Py, PyAny, PyRef, PyResult, Python,
Python,
}; };
use pyo3_async_runtimes::tokio::future_into_py; use pyo3_async_runtimes::tokio::future_into_py;
@@ -36,8 +35,11 @@ impl RecordBatchStream {
#[pymethods] #[pymethods]
impl RecordBatchStream { impl RecordBatchStream {
#[getter] #[getter]
pub fn schema(&self, py: Python) -> PyResult<PyObject> { pub fn schema(&self, py: Python) -> PyResult<Py<PyAny>> {
(*self.schema).clone().into_pyarrow(py) (*self.schema)
.clone()
.into_pyarrow(py)
.map(|obj| obj.unbind())
} }
pub fn __aiter__(self_: PyRef<'_, Self>) -> PyRef<'_, Self> { pub fn __aiter__(self_: PyRef<'_, Self>) -> PyRef<'_, Self> {
@@ -53,7 +55,12 @@ impl RecordBatchStream {
.next() .next()
.await .await
.ok_or_else(|| PyStopAsyncIteration::new_err(""))?; .ok_or_else(|| PyStopAsyncIteration::new_err(""))?;
Python::with_gil(|py| inner_next.infer_error()?.to_pyarrow(py)) Python::attach(|py| {
inner_next
.infer_error()?
.to_pyarrow(py)
.map(|obj| obj.unbind())
})
}) })
} }
} }
+34 -24
View File
@@ -12,7 +12,7 @@ use pyo3::{
exceptions::{PyRuntimeError, PyValueError}, exceptions::{PyRuntimeError, PyValueError},
pyclass, pyfunction, pymethods, pyclass, pyfunction, pymethods,
types::{PyDict, PyDictMethods}, types::{PyDict, PyDictMethods},
Bound, FromPyObject, Py, PyAny, PyObject, PyRef, PyResult, Python, Bound, FromPyObject, Py, PyAny, PyRef, PyResult, Python,
}; };
use pyo3_async_runtimes::tokio::future_into_py; use pyo3_async_runtimes::tokio::future_into_py;
@@ -114,7 +114,7 @@ impl Connection {
data: Bound<'_, PyAny>, data: Bound<'_, PyAny>,
namespace: Vec<String>, namespace: Vec<String>,
storage_options: Option<HashMap<String, String>>, storage_options: Option<HashMap<String, String>>,
storage_options_provider: Option<PyObject>, storage_options_provider: Option<Py<PyAny>>,
location: Option<String>, location: Option<String>,
) -> PyResult<Bound<'a, PyAny>> { ) -> PyResult<Bound<'a, PyAny>> {
let inner = self_.get_inner()?.clone(); let inner = self_.get_inner()?.clone();
@@ -152,7 +152,7 @@ impl Connection {
schema: Bound<'_, PyAny>, schema: Bound<'_, PyAny>,
namespace: Vec<String>, namespace: Vec<String>,
storage_options: Option<HashMap<String, String>>, storage_options: Option<HashMap<String, String>>,
storage_options_provider: Option<PyObject>, storage_options_provider: Option<Py<PyAny>>,
location: Option<String>, location: Option<String>,
) -> PyResult<Bound<'a, PyAny>> { ) -> PyResult<Bound<'a, PyAny>> {
let inner = self_.get_inner()?.clone(); let inner = self_.get_inner()?.clone();
@@ -187,7 +187,7 @@ impl Connection {
name: String, name: String,
namespace: Vec<String>, namespace: Vec<String>,
storage_options: Option<HashMap<String, String>>, storage_options: Option<HashMap<String, String>>,
storage_options_provider: Option<PyObject>, storage_options_provider: Option<Py<PyAny>>,
index_cache_size: Option<u32>, index_cache_size: Option<u32>,
location: Option<String>, location: Option<String>,
) -> PyResult<Bound<'_, PyAny>> { ) -> PyResult<Bound<'_, PyAny>> {
@@ -297,6 +297,8 @@ impl Connection {
future_into_py(py, async move { future_into_py(py, async move {
use lance_namespace::models::ListNamespacesRequest; use lance_namespace::models::ListNamespacesRequest;
let request = ListNamespacesRequest { let request = ListNamespacesRequest {
identity: None,
context: None,
id: if namespace.is_empty() { id: if namespace.is_empty() {
None None
} else { } else {
@@ -306,7 +308,7 @@ impl Connection {
limit: limit.map(|l| l as i32), limit: limit.map(|l| l as i32),
}; };
let response = inner.list_namespaces(request).await.infer_error()?; let response = inner.list_namespaces(request).await.infer_error()?;
Python::with_gil(|py| -> PyResult<Py<PyDict>> { Python::attach(|py| -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py); let dict = PyDict::new(py);
dict.set_item("namespaces", response.namespaces)?; dict.set_item("namespaces", response.namespaces)?;
dict.set_item("page_token", response.page_token)?; dict.set_item("page_token", response.page_token)?;
@@ -325,24 +327,26 @@ impl Connection {
let inner = self_.get_inner()?.clone(); let inner = self_.get_inner()?.clone();
let py = self_.py(); let py = self_.py();
future_into_py(py, async move { future_into_py(py, async move {
use lance_namespace::models::{create_namespace_request, CreateNamespaceRequest}; use lance_namespace::models::CreateNamespaceRequest;
let mode_enum = mode.and_then(|m| match m.to_lowercase().as_str() { let mode_value = mode.and_then(|m| match m.to_lowercase().as_str() {
"create" => Some(create_namespace_request::Mode::Create), "create" => Some("Create".to_string()),
"exist_ok" => Some(create_namespace_request::Mode::ExistOk), "exist_ok" => Some("ExistOk".to_string()),
"overwrite" => Some(create_namespace_request::Mode::Overwrite), "overwrite" => Some("Overwrite".to_string()),
_ => None, _ => None,
}); });
let request = CreateNamespaceRequest { let request = CreateNamespaceRequest {
identity: None,
context: None,
id: if namespace.is_empty() { id: if namespace.is_empty() {
None None
} else { } else {
Some(namespace) Some(namespace)
}, },
mode: mode_enum, mode: mode_value,
properties, properties,
}; };
let response = inner.create_namespace(request).await.infer_error()?; let response = inner.create_namespace(request).await.infer_error()?;
Python::with_gil(|py| -> PyResult<Py<PyDict>> { Python::attach(|py| -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py); let dict = PyDict::new(py);
dict.set_item("properties", response.properties)?; dict.set_item("properties", response.properties)?;
Ok(dict.unbind()) Ok(dict.unbind())
@@ -360,28 +364,30 @@ impl Connection {
let inner = self_.get_inner()?.clone(); let inner = self_.get_inner()?.clone();
let py = self_.py(); let py = self_.py();
future_into_py(py, async move { future_into_py(py, async move {
use lance_namespace::models::{drop_namespace_request, DropNamespaceRequest}; use lance_namespace::models::DropNamespaceRequest;
let mode_enum = mode.and_then(|m| match m.to_uppercase().as_str() { let mode_value = mode.and_then(|m| match m.to_uppercase().as_str() {
"SKIP" => Some(drop_namespace_request::Mode::Skip), "SKIP" => Some("Skip".to_string()),
"FAIL" => Some(drop_namespace_request::Mode::Fail), "FAIL" => Some("Fail".to_string()),
_ => None, _ => None,
}); });
let behavior_enum = behavior.and_then(|b| match b.to_uppercase().as_str() { let behavior_value = behavior.and_then(|b| match b.to_uppercase().as_str() {
"RESTRICT" => Some(drop_namespace_request::Behavior::Restrict), "RESTRICT" => Some("Restrict".to_string()),
"CASCADE" => Some(drop_namespace_request::Behavior::Cascade), "CASCADE" => Some("Cascade".to_string()),
_ => None, _ => None,
}); });
let request = DropNamespaceRequest { let request = DropNamespaceRequest {
identity: None,
context: None,
id: if namespace.is_empty() { id: if namespace.is_empty() {
None None
} else { } else {
Some(namespace) Some(namespace)
}, },
mode: mode_enum, mode: mode_value,
behavior: behavior_enum, behavior: behavior_value,
}; };
let response = inner.drop_namespace(request).await.infer_error()?; let response = inner.drop_namespace(request).await.infer_error()?;
Python::with_gil(|py| -> PyResult<Py<PyDict>> { Python::attach(|py| -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py); let dict = PyDict::new(py);
dict.set_item("properties", response.properties)?; dict.set_item("properties", response.properties)?;
dict.set_item("transaction_id", response.transaction_id)?; dict.set_item("transaction_id", response.transaction_id)?;
@@ -400,6 +406,8 @@ impl Connection {
future_into_py(py, async move { future_into_py(py, async move {
use lance_namespace::models::DescribeNamespaceRequest; use lance_namespace::models::DescribeNamespaceRequest;
let request = DescribeNamespaceRequest { let request = DescribeNamespaceRequest {
identity: None,
context: None,
id: if namespace.is_empty() { id: if namespace.is_empty() {
None None
} else { } else {
@@ -407,7 +415,7 @@ impl Connection {
}, },
}; };
let response = inner.describe_namespace(request).await.infer_error()?; let response = inner.describe_namespace(request).await.infer_error()?;
Python::with_gil(|py| -> PyResult<Py<PyDict>> { Python::attach(|py| -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py); let dict = PyDict::new(py);
dict.set_item("properties", response.properties)?; dict.set_item("properties", response.properties)?;
Ok(dict.unbind()) Ok(dict.unbind())
@@ -427,6 +435,8 @@ impl Connection {
future_into_py(py, async move { future_into_py(py, async move {
use lance_namespace::models::ListTablesRequest; use lance_namespace::models::ListTablesRequest;
let request = ListTablesRequest { let request = ListTablesRequest {
identity: None,
context: None,
id: if namespace.is_empty() { id: if namespace.is_empty() {
None None
} else { } else {
@@ -436,7 +446,7 @@ impl Connection {
limit: limit.map(|l| l as i32), limit: limit.map(|l| l as i32),
}; };
let response = inner.list_tables(request).await.infer_error()?; let response = inner.list_tables(request).await.infer_error()?;
Python::with_gil(|py| -> PyResult<Py<PyDict>> { Python::attach(|py| -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py); let dict = PyDict::new(py);
dict.set_item("tables", response.tables)?; dict.set_item("tables", response.tables)?;
dict.set_item("page_token", response.page_token)?; dict.set_item("page_token", response.page_token)?;
+2 -2
View File
@@ -40,7 +40,7 @@ impl<T> PythonErrorExt<T> for std::result::Result<T, LanceError> {
request_id, request_id,
source, source,
status_code, status_code,
} => Python::with_gil(|py| { } => Python::attach(|py| {
let message = err.to_string(); let message = err.to_string();
let http_err_cls = py let http_err_cls = py
.import(intern!(py, "lancedb.remote.errors"))? .import(intern!(py, "lancedb.remote.errors"))?
@@ -75,7 +75,7 @@ impl<T> PythonErrorExt<T> for std::result::Result<T, LanceError> {
max_read_failures, max_read_failures,
source, source,
status_code, status_code,
} => Python::with_gil(|py| { } => Python::attach(|py| {
let cause_err = http_from_rust_error( let cause_err = http_from_rust_error(
py, py,
source.as_ref(), source.as_ref(),
+2 -2
View File
@@ -12,7 +12,7 @@ pub struct PyHeaderProvider {
impl Clone for PyHeaderProvider { impl Clone for PyHeaderProvider {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Python::with_gil(|py| Self { Python::attach(|py| Self {
provider: self.provider.clone_ref(py), provider: self.provider.clone_ref(py),
}) })
} }
@@ -25,7 +25,7 @@ impl PyHeaderProvider {
/// Get headers from the Python provider (internal implementation) /// Get headers from the Python provider (internal implementation)
fn get_headers_internal(&self) -> Result<HashMap<String, String>, String> { fn get_headers_internal(&self) -> Result<HashMap<String, String>, String> {
Python::with_gil(|py| { Python::attach(|py| {
// Call the get_headers method // Call the get_headers method
let result = self.provider.call_method0(py, "get_headers"); let result = self.provider.call_method0(py, "get_headers");
+1 -1
View File
@@ -281,7 +281,7 @@ impl PyPermutationReader {
let reader = slf.reader.clone(); let reader = slf.reader.clone();
future_into_py(slf.py(), async move { future_into_py(slf.py(), async move {
let schema = reader.output_schema(selection).await.infer_error()?; let schema = reader.output_schema(selection).await.infer_error()?;
Python::with_gil(|py| schema.to_pyarrow(py)) Python::attach(|py| schema.as_ref().to_pyarrow(py).map(|obj| obj.unbind()))
}) })
} }
+4 -4
View File
@@ -453,7 +453,7 @@ impl Query {
let inner = self_.inner.clone(); let inner = self_.inner.clone();
future_into_py(self_.py(), async move { future_into_py(self_.py(), async move {
let schema = inner.output_schema().await.infer_error()?; let schema = inner.output_schema().await.infer_error()?;
Python::with_gil(|py| schema.to_pyarrow(py)) Python::attach(|py| schema.to_pyarrow(py).map(|obj| obj.unbind()))
}) })
} }
@@ -532,7 +532,7 @@ impl TakeQuery {
let inner = self_.inner.clone(); let inner = self_.inner.clone();
future_into_py(self_.py(), async move { future_into_py(self_.py(), async move {
let schema = inner.output_schema().await.infer_error()?; let schema = inner.output_schema().await.infer_error()?;
Python::with_gil(|py| schema.to_pyarrow(py)) Python::attach(|py| schema.to_pyarrow(py).map(|obj| obj.unbind()))
}) })
} }
@@ -627,7 +627,7 @@ impl FTSQuery {
let inner = self_.inner.clone(); let inner = self_.inner.clone();
future_into_py(self_.py(), async move { future_into_py(self_.py(), async move {
let schema = inner.output_schema().await.infer_error()?; let schema = inner.output_schema().await.infer_error()?;
Python::with_gil(|py| schema.to_pyarrow(py)) Python::attach(|py| schema.to_pyarrow(py).map(|obj| obj.unbind()))
}) })
} }
@@ -806,7 +806,7 @@ impl VectorQuery {
let inner = self_.inner.clone(); let inner = self_.inner.clone();
future_into_py(self_.py(), async move { future_into_py(self_.py(), async move {
let schema = inner.output_schema().await.infer_error()?; let schema = inner.output_schema().await.infer_error()?;
Python::with_gil(|py| schema.to_pyarrow(py)) Python::attach(|py| schema.to_pyarrow(py).map(|obj| obj.unbind()))
}) })
} }
+7 -7
View File
@@ -17,20 +17,20 @@ use pyo3::types::PyDict;
/// Internal wrapper around a Python object implementing StorageOptionsProvider /// Internal wrapper around a Python object implementing StorageOptionsProvider
pub struct PyStorageOptionsProvider { pub struct PyStorageOptionsProvider {
/// The Python object implementing fetch_storage_options() /// The Python object implementing fetch_storage_options()
inner: PyObject, inner: Py<PyAny>,
} }
impl Clone for PyStorageOptionsProvider { impl Clone for PyStorageOptionsProvider {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Python::with_gil(|py| Self { Python::attach(|py| Self {
inner: self.inner.clone_ref(py), inner: self.inner.clone_ref(py),
}) })
} }
} }
impl PyStorageOptionsProvider { impl PyStorageOptionsProvider {
pub fn new(obj: PyObject) -> PyResult<Self> { pub fn new(obj: Py<PyAny>) -> PyResult<Self> {
Python::with_gil(|py| { Python::attach(|py| {
// Verify the object has a fetch_storage_options method // Verify the object has a fetch_storage_options method
if !obj.bind(py).hasattr("fetch_storage_options")? { if !obj.bind(py).hasattr("fetch_storage_options")? {
return Err(pyo3::exceptions::PyTypeError::new_err( return Err(pyo3::exceptions::PyTypeError::new_err(
@@ -60,7 +60,7 @@ impl StorageOptionsProvider for PyStorageOptionsProviderWrapper {
let py_provider = self.py_provider.clone(); let py_provider = self.py_provider.clone();
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
Python::with_gil(|py| { Python::attach(|py| {
// Call the Python fetch_storage_options method // Call the Python fetch_storage_options method
let result = py_provider let result = py_provider
.inner .inner
@@ -119,7 +119,7 @@ impl StorageOptionsProvider for PyStorageOptionsProviderWrapper {
} }
fn provider_id(&self) -> String { fn provider_id(&self) -> String {
Python::with_gil(|py| { Python::attach(|py| {
// Call provider_id() method on the Python object // Call provider_id() method on the Python object
let obj = self.py_provider.inner.bind(py); let obj = self.py_provider.inner.bind(py);
obj.call_method0("provider_id") obj.call_method0("provider_id")
@@ -143,7 +143,7 @@ impl std::fmt::Debug for PyStorageOptionsProviderWrapper {
/// This is the main entry point for converting Python StorageOptionsProvider objects /// This is the main entry point for converting Python StorageOptionsProvider objects
/// to Rust trait objects that can be used by the Lance ecosystem. /// to Rust trait objects that can be used by the Lance ecosystem.
pub fn py_object_to_storage_options_provider( pub fn py_object_to_storage_options_provider(
py_obj: PyObject, py_obj: Py<PyAny>,
) -> PyResult<Arc<dyn StorageOptionsProvider>> { ) -> PyResult<Arc<dyn StorageOptionsProvider>> {
let py_provider = PyStorageOptionsProvider::new(py_obj)?; let py_provider = PyStorageOptionsProvider::new(py_obj)?;
Ok(Arc::new(PyStorageOptionsProviderWrapper::new(py_provider))) Ok(Arc::new(PyStorageOptionsProviderWrapper::new(py_provider)))
+5 -5
View File
@@ -287,7 +287,7 @@ impl Table {
let inner = self_.inner_ref()?.clone(); let inner = self_.inner_ref()?.clone();
future_into_py(self_.py(), async move { future_into_py(self_.py(), async move {
let schema = inner.schema().await.infer_error()?; let schema = inner.schema().await.infer_error()?;
Python::with_gil(|py| schema.to_pyarrow(py)) Python::attach(|py| schema.to_pyarrow(py).map(|obj| obj.unbind()))
}) })
} }
@@ -437,7 +437,7 @@ impl Table {
future_into_py(self_.py(), async move { future_into_py(self_.py(), async move {
let stats = inner.index_stats(&index_name).await.infer_error()?; let stats = inner.index_stats(&index_name).await.infer_error()?;
if let Some(stats) = stats { if let Some(stats) = stats {
Python::with_gil(|py| { Python::attach(|py| {
let dict = PyDict::new(py); let dict = PyDict::new(py);
dict.set_item("num_indexed_rows", stats.num_indexed_rows)?; dict.set_item("num_indexed_rows", stats.num_indexed_rows)?;
dict.set_item("num_unindexed_rows", stats.num_unindexed_rows)?; dict.set_item("num_unindexed_rows", stats.num_unindexed_rows)?;
@@ -467,7 +467,7 @@ impl Table {
let inner = self_.inner_ref()?.clone(); let inner = self_.inner_ref()?.clone();
future_into_py(self_.py(), async move { future_into_py(self_.py(), async move {
let stats = inner.stats().await.infer_error()?; let stats = inner.stats().await.infer_error()?;
Python::with_gil(|py| { Python::attach(|py| {
let dict = PyDict::new(py); let dict = PyDict::new(py);
dict.set_item("total_bytes", stats.total_bytes)?; dict.set_item("total_bytes", stats.total_bytes)?;
dict.set_item("num_rows", stats.num_rows)?; dict.set_item("num_rows", stats.num_rows)?;
@@ -516,7 +516,7 @@ impl Table {
let inner = self_.inner_ref()?.clone(); let inner = self_.inner_ref()?.clone();
future_into_py(self_.py(), async move { future_into_py(self_.py(), async move {
let versions = inner.list_versions().await.infer_error()?; let versions = inner.list_versions().await.infer_error()?;
let versions_as_dict = Python::with_gil(|py| { let versions_as_dict = Python::attach(|py| {
versions versions
.iter() .iter()
.map(|v| { .map(|v| {
@@ -867,7 +867,7 @@ impl Tags {
let tags = inner.tags().await.infer_error()?; let tags = inner.tags().await.infer_error()?;
let res = tags.list().await.infer_error()?; let res = tags.list().await.infer_error()?;
Python::with_gil(|py| { Python::attach(|py| {
let py_dict = PyDict::new(py); let py_dict = PyDict::new(py);
for (key, contents) in res { for (key, contents) in res {
let value_dict = PyDict::new(py); let value_dict = PyDict::new(py);
+36 -7
View File
@@ -9,7 +9,7 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use lance_namespace::{ use lance_namespace::{
models::{ models::{
CreateEmptyTableRequest, CreateNamespaceRequest, CreateNamespaceResponse, CreateNamespaceRequest, CreateNamespaceResponse, DeclareTableRequest,
DescribeNamespaceRequest, DescribeNamespaceResponse, DescribeTableRequest, DescribeNamespaceRequest, DescribeNamespaceResponse, DescribeTableRequest,
DropNamespaceRequest, DropNamespaceResponse, DropTableRequest, ListNamespacesRequest, DropNamespaceRequest, DropNamespaceResponse, DropTableRequest, ListNamespacesRequest,
ListNamespacesResponse, ListTablesRequest, ListTablesResponse, ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
@@ -134,6 +134,8 @@ impl Database for LanceNamespaceDatabase {
async fn table_names(&self, request: TableNamesRequest) -> Result<Vec<String>> { async fn table_names(&self, request: TableNamesRequest) -> Result<Vec<String>> {
let ns_request = ListTablesRequest { let ns_request = ListTablesRequest {
identity: None,
context: None,
id: Some(request.namespace), id: Some(request.namespace),
page_token: request.start_after, page_token: request.start_after,
limit: request.limit.map(|l| l as i32), limit: request.limit.map(|l| l as i32),
@@ -152,8 +154,13 @@ impl Database for LanceNamespaceDatabase {
let mut table_id = request.namespace.clone(); let mut table_id = request.namespace.clone();
table_id.push(request.name.clone()); table_id.push(request.name.clone());
let describe_request = DescribeTableRequest { let describe_request = DescribeTableRequest {
identity: None,
context: None,
id: Some(table_id.clone()), id: Some(table_id.clone()),
version: None, version: None,
with_table_uri: None,
load_detailed_metadata: None,
vend_credentials: None,
}; };
let describe_result = self.namespace.describe_table(describe_request).await; let describe_result = self.namespace.describe_table(describe_request).await;
@@ -170,6 +177,8 @@ impl Database for LanceNamespaceDatabase {
if describe_result.is_ok() { if describe_result.is_ok() {
// Drop the existing table - must succeed // Drop the existing table - must succeed
let drop_request = DropTableRequest { let drop_request = DropTableRequest {
identity: None,
context: None,
id: Some(table_id.clone()), id: Some(table_id.clone()),
}; };
self.namespace self.namespace
@@ -202,22 +211,24 @@ impl Database for LanceNamespaceDatabase {
let mut table_id = request.namespace.clone(); let mut table_id = request.namespace.clone();
table_id.push(request.name.clone()); table_id.push(request.name.clone());
let create_empty_request = CreateEmptyTableRequest { let create_empty_request = DeclareTableRequest {
identity: None,
context: None,
id: Some(table_id.clone()), id: Some(table_id.clone()),
location: None, location: None,
properties: if self.storage_options.is_empty() { vend_credentials: if self.storage_options.is_empty() {
None None
} else { } else {
Some(self.storage_options.clone()) Some(true)
}, },
}; };
let create_empty_response = self let create_empty_response = self
.namespace .namespace
.create_empty_table(create_empty_request) .declare_table(create_empty_request)
.await .await
.map_err(|e| Error::Runtime { .map_err(|e| Error::Runtime {
message: format!("Failed to create empty table: {}", e), message: format!("Failed to declare table: {}", e),
})?; })?;
let location = create_empty_response let location = create_empty_response
@@ -281,7 +292,11 @@ impl Database for LanceNamespaceDatabase {
let mut table_id = namespace.to_vec(); let mut table_id = namespace.to_vec();
table_id.push(name.to_string()); table_id.push(name.to_string());
let drop_request = DropTableRequest { id: Some(table_id) }; let drop_request = DropTableRequest {
identity: None,
context: None,
id: Some(table_id),
};
self.namespace self.namespace
.drop_table(drop_request) .drop_table(drop_request)
.await .await
@@ -435,6 +450,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(vec!["test_ns".into()]), id: Some(vec!["test_ns".into()]),
mode: None, mode: None,
properties: None, properties: None,
@@ -496,6 +513,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(vec!["test_ns".into()]), id: Some(vec!["test_ns".into()]),
mode: None, mode: None,
properties: None, properties: None,
@@ -560,6 +579,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(vec!["test_ns".into()]), id: Some(vec!["test_ns".into()]),
mode: None, mode: None,
properties: None, properties: None,
@@ -644,6 +665,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(vec!["test_ns".into()]), id: Some(vec!["test_ns".into()]),
mode: None, mode: None,
properties: None, properties: None,
@@ -700,6 +723,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(vec!["test_ns".into()]), id: Some(vec!["test_ns".into()]),
mode: None, mode: None,
properties: None, properties: None,
@@ -781,6 +806,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(vec!["test_ns".into()]), id: Some(vec!["test_ns".into()]),
mode: None, mode: None,
properties: None, properties: None,
@@ -815,6 +842,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(vec!["test_ns".into()]), id: Some(vec!["test_ns".into()]),
mode: None, mode: None,
properties: None, properties: None,
+10
View File
@@ -1717,6 +1717,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
let namespace = vec!["test_ns".to_string()]; let namespace = vec!["test_ns".to_string()];
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(namespace.clone()), id: Some(namespace.clone()),
mode: None, mode: None,
properties: None, properties: None,
@@ -1743,6 +1745,8 @@ mod tests {
// List tables in the child namespace // List tables in the child namespace
let list_response = conn let list_response = conn
.list_tables(ListTablesRequest { .list_tables(ListTablesRequest {
identity: None,
context: None,
id: Some(namespace.clone()), id: Some(namespace.clone()),
page_token: None, page_token: None,
limit: None, limit: None,
@@ -1755,6 +1759,8 @@ mod tests {
let namespace_client = conn.namespace_client().await.unwrap(); let namespace_client = conn.namespace_client().await.unwrap();
let list_response = namespace_client let list_response = namespace_client
.list_tables(ListTablesRequest { .list_tables(ListTablesRequest {
identity: None,
context: None,
id: Some(namespace.clone()), id: Some(namespace.clone()),
page_token: None, page_token: None,
limit: None, limit: None,
@@ -1796,6 +1802,8 @@ mod tests {
// Create a child namespace first // Create a child namespace first
let namespace = vec!["multi_table_ns".to_string()]; let namespace = vec!["multi_table_ns".to_string()];
conn.create_namespace(CreateNamespaceRequest { conn.create_namespace(CreateNamespaceRequest {
identity: None,
context: None,
id: Some(namespace.clone()), id: Some(namespace.clone()),
mode: None, mode: None,
properties: None, properties: None,
@@ -1822,6 +1830,8 @@ mod tests {
// List tables in the child namespace // List tables in the child namespace
let list_response = conn let list_response = conn
.list_tables(ListTablesRequest { .list_tables(ListTablesRequest {
identity: None,
context: None,
id: Some(namespace.clone()), id: Some(namespace.clone()),
page_token: None, page_token: None,
limit: None, limit: None,
+31 -7
View File
@@ -42,8 +42,8 @@ use lance_index::DatasetIndexExt;
use lance_index::IndexType; use lance_index::IndexType;
use lance_io::object_store::LanceNamespaceStorageOptionsProvider; use lance_io::object_store::LanceNamespaceStorageOptionsProvider;
use lance_namespace::models::{ use lance_namespace::models::{
QueryTableRequest as NsQueryTableRequest, QueryTableRequestFullTextQuery, QueryTableRequest as NsQueryTableRequest, QueryTableRequestColumns,
QueryTableRequestVector, StringFtsQuery, QueryTableRequestFullTextQuery, QueryTableRequestVector, StringFtsQuery,
}; };
use lance_namespace::LanceNamespace; use lance_namespace::LanceNamespace;
use lance_table::format::Manifest; use lance_table::format::Manifest;
@@ -1424,7 +1424,9 @@ impl Table {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let unioned = Arc::new(UnionExec::new(projected_plans)); let unioned = UnionExec::try_new(projected_plans).map_err(|e| Error::Runtime {
message: format!("Failed to union query plans: {}", e),
})?;
// We require 1 partition in the final output // We require 1 partition in the final output
let repartitioned = RepartitionExec::try_new( let repartitioned = RepartitionExec::try_new(
unioned, unioned,
@@ -2348,7 +2350,10 @@ impl NativeTable {
// Convert select to columns list // Convert select to columns list
let columns = match &vq.base.select { let columns = match &vq.base.select {
Select::All => None, Select::All => None,
Select::Columns(cols) => Some(cols.clone()), Select::Columns(cols) => Some(Box::new(QueryTableRequestColumns {
column_names: Some(cols.clone()),
column_aliases: None,
})),
Select::Dynamic(_) => { Select::Dynamic(_) => {
return Err(Error::NotSupported { return Err(Error::NotSupported {
message: message:
@@ -2383,6 +2388,8 @@ impl NativeTable {
}); });
Ok(NsQueryTableRequest { Ok(NsQueryTableRequest {
identity: None,
context: None,
id: None, // Will be set in namespace_query id: None, // Will be set in namespace_query
k: vq.base.limit.unwrap_or(10) as i32, k: vq.base.limit.unwrap_or(10) as i32,
vector: Box::new(vector), vector: Box::new(vector),
@@ -2421,7 +2428,10 @@ impl NativeTable {
let columns = match &q.select { let columns = match &q.select {
Select::All => None, Select::All => None,
Select::Columns(cols) => Some(cols.clone()), Select::Columns(cols) => Some(Box::new(QueryTableRequestColumns {
column_names: Some(cols.clone()),
column_aliases: None,
})),
Select::Dynamic(_) => { Select::Dynamic(_) => {
return Err(Error::NotSupported { return Err(Error::NotSupported {
message: "Dynamic columns are not supported for server-side query" message: "Dynamic columns are not supported for server-side query"
@@ -2453,6 +2463,8 @@ impl NativeTable {
}); });
Ok(NsQueryTableRequest { Ok(NsQueryTableRequest {
identity: None,
context: None,
id: None, // Will be set by caller id: None, // Will be set by caller
vector, vector,
k: q.limit.unwrap_or(10) as i32, k: q.limit.unwrap_or(10) as i32,
@@ -5146,7 +5158,13 @@ mod tests {
assert_eq!(ns_request.k, 10); assert_eq!(ns_request.k, 10);
assert_eq!(ns_request.offset, Some(5)); assert_eq!(ns_request.offset, Some(5));
assert_eq!(ns_request.filter, Some("id > 0".to_string())); assert_eq!(ns_request.filter, Some("id > 0".to_string()));
assert_eq!(ns_request.columns, Some(vec!["id".to_string()])); assert_eq!(
ns_request
.columns
.as_ref()
.and_then(|columns| columns.column_names.clone()),
Some(vec!["id".to_string()])
);
assert_eq!(ns_request.vector_column, Some("vector".to_string())); assert_eq!(ns_request.vector_column, Some("vector".to_string()));
assert_eq!(ns_request.distance_type, Some("l2".to_string())); assert_eq!(ns_request.distance_type, Some("l2".to_string()));
assert!(ns_request.vector.single_vector.is_some()); assert!(ns_request.vector.single_vector.is_some());
@@ -5187,7 +5205,13 @@ mod tests {
assert_eq!(ns_request.k, 20); assert_eq!(ns_request.k, 20);
assert_eq!(ns_request.offset, Some(5)); assert_eq!(ns_request.offset, Some(5));
assert_eq!(ns_request.filter, Some("id > 5".to_string())); assert_eq!(ns_request.filter, Some("id > 5".to_string()));
assert_eq!(ns_request.columns, Some(vec!["id".to_string()])); assert_eq!(
ns_request
.columns
.as_ref()
.and_then(|columns| columns.column_names.clone()),
Some(vec!["id".to_string()])
);
assert_eq!(ns_request.with_row_id, Some(true)); assert_eq!(ns_request.with_row_id, Some(true));
assert_eq!(ns_request.bypass_vector_index, Some(true)); assert_eq!(ns_request.bypass_vector_index, Some(true));
assert!(ns_request.vector_column.is_none()); // No vector column for plain queries assert!(ns_request.vector_column.is_none()); // No vector column for plain queries
+1
View File
@@ -101,6 +101,7 @@ impl DatasetRef {
refs::Ref::Version(_, Some(target_ver)) => version != target_ver, refs::Ref::Version(_, Some(target_ver)) => version != target_ver,
refs::Ref::Version(_, None) => true, // No specific version, always checkout refs::Ref::Version(_, None) => true, // No specific version, always checkout
refs::Ref::Tag(_) => true, // Always checkout for tags refs::Ref::Tag(_) => true, // Always checkout for tags
refs::Ref::VersionNumber(target_ver) => version != target_ver,
}; };
if should_checkout { if should_checkout {