mirror of
https://github.com/lancedb/lancedb.git
synced 2026-09-04 12:38:38 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a7b29dfabb |
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.24.0"
|
current_version = "0.23.1"
|
||||||
parse = """(?x)
|
parse = """(?x)
|
||||||
(?P<major>0|[1-9]\\d*)\\.
|
(?P<major>0|[1-9]\\d*)\\.
|
||||||
(?P<minor>0|[1-9]\\d*)\\.
|
(?P<minor>0|[1-9]\\d*)\\.
|
||||||
|
|||||||
@@ -75,13 +75,6 @@ jobs:
|
|||||||
VERSION="${VERSION#v}"
|
VERSION="${VERSION#v}"
|
||||||
BRANCH_NAME="codex/update-lance-${VERSION//[^a-zA-Z0-9]/-}"
|
BRANCH_NAME="codex/update-lance-${VERSION//[^a-zA-Z0-9]/-}"
|
||||||
|
|
||||||
# Use "chore" for beta/rc versions, "feat" for stable releases
|
|
||||||
if [[ "${VERSION}" == *beta* ]] || [[ "${VERSION}" == *rc* ]]; then
|
|
||||||
COMMIT_TYPE="chore"
|
|
||||||
else
|
|
||||||
COMMIT_TYPE="feat"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<EOF >/tmp/codex-prompt.txt
|
cat <<EOF >/tmp/codex-prompt.txt
|
||||||
You are running inside the lancedb repository on a GitHub Actions runner. Update the Lance dependency to version ${VERSION} and prepare a pull request for maintainers to review.
|
You are running inside the lancedb repository on a GitHub Actions runner. Update the Lance dependency to version ${VERSION} and prepare a pull request for maintainers to review.
|
||||||
|
|
||||||
@@ -91,10 +84,10 @@ jobs:
|
|||||||
3. After clippy succeeds, run "cargo fmt --all" to format the workspace.
|
3. After clippy succeeds, run "cargo fmt --all" to format the workspace.
|
||||||
4. Ensure the repository is clean except for intentional changes. Inspect "git status --short" and "git diff" to confirm the dependency update and any required fixes.
|
4. Ensure the repository is clean except for intentional changes. Inspect "git status --short" and "git diff" to confirm the dependency update and any required fixes.
|
||||||
5. Create and switch to a new branch named "${BRANCH_NAME}" (replace any duplicated hyphens if necessary).
|
5. Create and switch to a new branch named "${BRANCH_NAME}" (replace any duplicated hyphens if necessary).
|
||||||
6. Stage all relevant files with "git add -A". Commit using the message "${COMMIT_TYPE}: update lance dependency to v${VERSION}".
|
6. Stage all relevant files with "git add -A". Commit using the message "chore: update lance dependency to v${VERSION}".
|
||||||
7. Push the branch to origin. If the branch already exists, force-push your changes.
|
7. Push the branch to origin. If the branch already exists, force-push your changes.
|
||||||
8. env "GH_TOKEN" is available, use "gh" tools for github related operations like creating pull request.
|
8. env "GH_TOKEN" is available, use "gh" tools for github related operations like creating pull request.
|
||||||
9. Create a pull request targeting "main" with title "${COMMIT_TYPE}: update lance dependency to v${VERSION}". First, write the PR body to /tmp/pr-body.md using a heredoc (cat <<'EOF' > /tmp/pr-body.md). The body should summarize the dependency bump, clippy/fmt verification, and link the triggering tag (${TAG}). Then run "gh pr create --body-file /tmp/pr-body.md".
|
9. Create a pull request targeting "main" with title "chore: update lance dependency to v${VERSION}". In the body, summarize the dependency bump, clippy/fmt verification, and link the triggering tag (${TAG}).
|
||||||
10. After creating the PR, display the PR URL, "git status --short", and a concise summary of the commands run and their results.
|
10. After creating the PR, display the PR URL, "git status --short", and a concise summary of the commands run and their results.
|
||||||
|
|
||||||
Constraints:
|
Constraints:
|
||||||
|
|||||||
@@ -48,8 +48,6 @@ jobs:
|
|||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: cargo clippy --profile ci --workspace --tests --all-features -- -D warnings
|
run: cargo clippy --profile ci --workspace --tests --all-features -- -D warnings
|
||||||
- name: Run clippy (without remote feature)
|
|
||||||
run: cargo clippy --profile ci --workspace --tests -- -D warnings
|
|
||||||
|
|
||||||
build-no-lock:
|
build-no-lock:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@@ -183,7 +181,7 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
msrv: ["1.88.0"] # This should match up with rust-version in Cargo.toml
|
msrv: ["1.78.0"] # This should match up with rust-version in Cargo.toml
|
||||||
env:
|
env:
|
||||||
# Need up-to-date compilers for kernels
|
# Need up-to-date compilers for kernels
|
||||||
CC: clang-18
|
CC: clang-18
|
||||||
@@ -214,6 +212,4 @@ jobs:
|
|||||||
cargo update -p aws-sdk-sts --precise 1.51.0
|
cargo update -p aws-sdk-sts --precise 1.51.0
|
||||||
cargo update -p home --precise 0.5.9
|
cargo update -p home --precise 0.5.9
|
||||||
- name: cargo +${{ matrix.msrv }} check
|
- name: cargo +${{ matrix.msrv }} check
|
||||||
env:
|
|
||||||
RUSTUP_TOOLCHAIN: ${{ matrix.msrv }}
|
|
||||||
run: cargo check --profile ci --workspace --tests --benches --all-features
|
run: cargo check --profile ci --workspace --tests --benches --all-features
|
||||||
|
|||||||
Generated
+352
-485
File diff suppressed because it is too large
Load Diff
+29
-29
@@ -12,40 +12,40 @@ repository = "https://github.com/lancedb/lancedb"
|
|||||||
description = "Serverless, low-latency vector database for AI applications"
|
description = "Serverless, low-latency vector database for AI applications"
|
||||||
keywords = ["lancedb", "lance", "database", "vector", "search"]
|
keywords = ["lancedb", "lance", "database", "vector", "search"]
|
||||||
categories = ["database-implementations"]
|
categories = ["database-implementations"]
|
||||||
rust-version = "1.88.0"
|
rust-version = "1.78.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
lance = { "version" = "=1.0.4", default-features = false, "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", default-features = false, "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", default-features = false, "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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.4", "tag" = "v1.0.4", "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",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`:
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-core</artifactId>
|
<artifactId>lancedb-core</artifactId>
|
||||||
<version>0.24.0</version>
|
<version>0.23.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-parent</artifactId>
|
<artifactId>lancedb-parent</artifactId>
|
||||||
<version>0.24.0-final.0</version>
|
<version>0.23.1-final.0</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.lancedb</groupId>
|
<groupId>com.lancedb</groupId>
|
||||||
<artifactId>lancedb-parent</artifactId>
|
<artifactId>lancedb-parent</artifactId>
|
||||||
<version>0.24.0-final.0</version>
|
<version>0.23.1-final.0</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>${project.artifactId}</name>
|
<name>${project.artifactId}</name>
|
||||||
<description>LanceDB Java SDK Parent POM</description>
|
<description>LanceDB Java SDK Parent POM</description>
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb-nodejs"
|
name = "lancedb-nodejs"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
version = "0.24.0"
|
version = "0.23.1"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-darwin-arm64",
|
"name": "@lancedb/lancedb-darwin-arm64",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"os": ["darwin"],
|
"os": ["darwin"],
|
||||||
"cpu": ["arm64"],
|
"cpu": ["arm64"],
|
||||||
"main": "lancedb.darwin-arm64.node",
|
"main": "lancedb.darwin-arm64.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-darwin-x64",
|
"name": "@lancedb/lancedb-darwin-x64",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"os": ["darwin"],
|
"os": ["darwin"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.darwin-x64.node",
|
"main": "lancedb.darwin-x64.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-linux-arm64-gnu",
|
"name": "@lancedb/lancedb-linux-arm64-gnu",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"os": ["linux"],
|
"os": ["linux"],
|
||||||
"cpu": ["arm64"],
|
"cpu": ["arm64"],
|
||||||
"main": "lancedb.linux-arm64-gnu.node",
|
"main": "lancedb.linux-arm64-gnu.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-linux-arm64-musl",
|
"name": "@lancedb/lancedb-linux-arm64-musl",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"os": ["linux"],
|
"os": ["linux"],
|
||||||
"cpu": ["arm64"],
|
"cpu": ["arm64"],
|
||||||
"main": "lancedb.linux-arm64-musl.node",
|
"main": "lancedb.linux-arm64-musl.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-linux-x64-gnu",
|
"name": "@lancedb/lancedb-linux-x64-gnu",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"os": ["linux"],
|
"os": ["linux"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.linux-x64-gnu.node",
|
"main": "lancedb.linux-x64-gnu.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-linux-x64-musl",
|
"name": "@lancedb/lancedb-linux-x64-musl",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"os": ["linux"],
|
"os": ["linux"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.linux-x64-musl.node",
|
"main": "lancedb.linux-x64-musl.node",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-win32-arm64-msvc",
|
"name": "@lancedb/lancedb-win32-arm64-msvc",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb-win32-x64-msvc",
|
"name": "@lancedb/lancedb-win32-x64-msvc",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"os": ["win32"],
|
"os": ["win32"],
|
||||||
"cpu": ["x64"],
|
"cpu": ["x64"],
|
||||||
"main": "lancedb.win32-x64-msvc.node",
|
"main": "lancedb.win32-x64-msvc.node",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@lancedb/lancedb",
|
"name": "@lancedb/lancedb",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@lancedb/lancedb",
|
"name": "@lancedb/lancedb",
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64",
|
"x64",
|
||||||
"arm64"
|
"arm64"
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
"ann"
|
"ann"
|
||||||
],
|
],
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "0.24.0",
|
"version": "0.23.1",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/index.js",
|
".": "./dist/index.js",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.27.1"
|
current_version = "0.26.1"
|
||||||
parse = """(?x)
|
parse = """(?x)
|
||||||
(?P<major>0|[1-9]\\d*)\\.
|
(?P<major>0|[1-9]\\d*)\\.
|
||||||
(?P<minor>0|[1-9]\\d*)\\.
|
(?P<minor>0|[1-9]\\d*)\\.
|
||||||
|
|||||||
+6
-6
@@ -1,28 +1,28 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb-python"
|
name = "lancedb-python"
|
||||||
version = "0.27.1"
|
version = "0.26.1"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "Python bindings for LanceDB"
|
description = "Python bindings for LanceDB"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
keywords.workspace = true
|
keywords.workspace = true
|
||||||
categories.workspace = true
|
categories.workspace = true
|
||||||
rust-version = "1.88.0"
|
rust-version = "1.75.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "_lancedb"
|
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",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -179,7 +179,6 @@ class Table:
|
|||||||
cleanup_since_ms: Optional[int] = None,
|
cleanup_since_ms: Optional[int] = None,
|
||||||
delete_unverified: Optional[bool] = None,
|
delete_unverified: Optional[bool] = None,
|
||||||
) -> OptimizeStats: ...
|
) -> OptimizeStats: ...
|
||||||
async def uri(self) -> str: ...
|
|
||||||
@property
|
@property
|
||||||
def tags(self) -> Tags: ...
|
def tags(self) -> Tags: ...
|
||||||
def query(self) -> Query: ...
|
def query(self) -> Query: ...
|
||||||
|
|||||||
@@ -655,14 +655,6 @@ class RemoteTable(Table):
|
|||||||
def stats(self):
|
def stats(self):
|
||||||
return LOOP.run(self._table.stats())
|
return LOOP.run(self._table.stats())
|
||||||
|
|
||||||
@property
|
|
||||||
def uri(self) -> str:
|
|
||||||
"""The table URI (storage location).
|
|
||||||
|
|
||||||
For remote tables, this fetches the location from the server via describe.
|
|
||||||
"""
|
|
||||||
return LOOP.run(self._table.uri())
|
|
||||||
|
|
||||||
def take_offsets(self, offsets: list[int]) -> LanceTakeQueryBuilder:
|
def take_offsets(self, offsets: list[int]) -> LanceTakeQueryBuilder:
|
||||||
return LanceTakeQueryBuilder(self._table.take_offsets(offsets))
|
return LanceTakeQueryBuilder(self._table.take_offsets(offsets))
|
||||||
|
|
||||||
|
|||||||
@@ -2218,10 +2218,6 @@ class LanceTable(Table):
|
|||||||
def stats(self) -> TableStatistics:
|
def stats(self) -> TableStatistics:
|
||||||
return LOOP.run(self._table.stats())
|
return LOOP.run(self._table.stats())
|
||||||
|
|
||||||
@property
|
|
||||||
def uri(self) -> str:
|
|
||||||
return LOOP.run(self._table.uri())
|
|
||||||
|
|
||||||
def create_scalar_index(
|
def create_scalar_index(
|
||||||
self,
|
self,
|
||||||
column: str,
|
column: str,
|
||||||
@@ -3610,20 +3606,6 @@ class AsyncTable:
|
|||||||
"""
|
"""
|
||||||
return await self._inner.stats()
|
return await self._inner.stats()
|
||||||
|
|
||||||
async def uri(self) -> str:
|
|
||||||
"""
|
|
||||||
Get the table URI (storage location).
|
|
||||||
|
|
||||||
For remote tables, this fetches the location from the server via describe.
|
|
||||||
For local tables, this returns the dataset URI.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
The full storage location of the table (e.g., S3/GCS path).
|
|
||||||
"""
|
|
||||||
return await self._inner.uri()
|
|
||||||
|
|
||||||
async def add(
|
async def add(
|
||||||
self,
|
self,
|
||||||
data: DATA,
|
data: DATA,
|
||||||
|
|||||||
@@ -2,27 +2,12 @@
|
|||||||
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
# SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from lancedb.db import AsyncConnection, DBConnection
|
from lancedb.db import AsyncConnection, DBConnection
|
||||||
import lancedb
|
import lancedb
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
|
|
||||||
|
|
||||||
def pandas_string_type():
|
|
||||||
"""Return the PyArrow string type that pandas uses for string columns.
|
|
||||||
|
|
||||||
pandas 3.0+ uses large_string for string columns, pandas 2.x uses string.
|
|
||||||
"""
|
|
||||||
import pandas as pd
|
|
||||||
import pyarrow as pa
|
|
||||||
|
|
||||||
version = tuple(int(x) for x in pd.__version__.split(".")[:2])
|
|
||||||
if version >= (3, 0):
|
|
||||||
return pa.large_utf8()
|
|
||||||
return pa.utf8()
|
|
||||||
|
|
||||||
|
|
||||||
# Use an in-memory database for most tests.
|
# Use an in-memory database for most tests.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mem_db() -> DBConnection:
|
def mem_db() -> DBConnection:
|
||||||
|
|||||||
@@ -268,8 +268,6 @@ async def test_create_table_from_iterator_async(mem_db_async: lancedb.AsyncConne
|
|||||||
|
|
||||||
|
|
||||||
def test_create_exist_ok(tmp_db: lancedb.DBConnection):
|
def test_create_exist_ok(tmp_db: lancedb.DBConnection):
|
||||||
from conftest import pandas_string_type
|
|
||||||
|
|
||||||
data = pd.DataFrame(
|
data = pd.DataFrame(
|
||||||
{
|
{
|
||||||
"vector": [[3.1, 4.1], [5.9, 26.5]],
|
"vector": [[3.1, 4.1], [5.9, 26.5]],
|
||||||
@@ -288,11 +286,10 @@ def test_create_exist_ok(tmp_db: lancedb.DBConnection):
|
|||||||
assert tbl.schema == tbl2.schema
|
assert tbl.schema == tbl2.schema
|
||||||
assert len(tbl) == len(tbl2)
|
assert len(tbl) == len(tbl2)
|
||||||
|
|
||||||
# pandas 3.0+ uses large_string, pandas 2.x uses string
|
|
||||||
schema = pa.schema(
|
schema = pa.schema(
|
||||||
[
|
[
|
||||||
pa.field("vector", pa.list_(pa.float32(), list_size=2)),
|
pa.field("vector", pa.list_(pa.float32(), list_size=2)),
|
||||||
pa.field("item", pandas_string_type()),
|
pa.field("item", pa.utf8()),
|
||||||
pa.field("price", pa.float64()),
|
pa.field("price", pa.float64()),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -302,7 +299,7 @@ def test_create_exist_ok(tmp_db: lancedb.DBConnection):
|
|||||||
bad_schema = pa.schema(
|
bad_schema = pa.schema(
|
||||||
[
|
[
|
||||||
pa.field("vector", pa.list_(pa.float32(), list_size=2)),
|
pa.field("vector", pa.list_(pa.float32(), list_size=2)),
|
||||||
pa.field("item", pandas_string_type()),
|
pa.field("item", pa.utf8()),
|
||||||
pa.field("price", pa.float64()),
|
pa.field("price", pa.float64()),
|
||||||
pa.field("extra", pa.float32()),
|
pa.field("extra", pa.float32()),
|
||||||
]
|
]
|
||||||
@@ -368,8 +365,6 @@ async def test_create_mode_async(tmp_db_async: lancedb.AsyncConnection):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_create_exist_ok_async(tmp_db_async: lancedb.AsyncConnection):
|
async def test_create_exist_ok_async(tmp_db_async: lancedb.AsyncConnection):
|
||||||
from conftest import pandas_string_type
|
|
||||||
|
|
||||||
data = pd.DataFrame(
|
data = pd.DataFrame(
|
||||||
{
|
{
|
||||||
"vector": [[3.1, 4.1], [5.9, 26.5]],
|
"vector": [[3.1, 4.1], [5.9, 26.5]],
|
||||||
@@ -387,11 +382,10 @@ async def test_create_exist_ok_async(tmp_db_async: lancedb.AsyncConnection):
|
|||||||
assert tbl.name == tbl2.name
|
assert tbl.name == tbl2.name
|
||||||
assert await tbl.schema() == await tbl2.schema()
|
assert await tbl.schema() == await tbl2.schema()
|
||||||
|
|
||||||
# pandas 3.0+ uses large_string, pandas 2.x uses string
|
|
||||||
schema = pa.schema(
|
schema = pa.schema(
|
||||||
[
|
[
|
||||||
pa.field("vector", pa.list_(pa.float32(), list_size=2)),
|
pa.field("vector", pa.list_(pa.float32(), list_size=2)),
|
||||||
pa.field("item", pandas_string_type()),
|
pa.field("item", pa.utf8()),
|
||||||
pa.field("price", pa.float64()),
|
pa.field("price", pa.float64()),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -601,8 +595,6 @@ def test_open_table_sync(tmp_db: lancedb.DBConnection):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_open_table(tmp_path):
|
async def test_open_table(tmp_path):
|
||||||
from conftest import pandas_string_type
|
|
||||||
|
|
||||||
db = await lancedb.connect_async(tmp_path)
|
db = await lancedb.connect_async(tmp_path)
|
||||||
data = pd.DataFrame(
|
data = pd.DataFrame(
|
||||||
{
|
{
|
||||||
@@ -622,11 +614,10 @@ async def test_open_table(tmp_path):
|
|||||||
)
|
)
|
||||||
is not None
|
is not None
|
||||||
)
|
)
|
||||||
# pandas 3.0+ uses large_string, pandas 2.x uses string
|
|
||||||
assert await tbl.schema() == pa.schema(
|
assert await tbl.schema() == pa.schema(
|
||||||
{
|
{
|
||||||
"vector": pa.list_(pa.float32(), list_size=2),
|
"vector": pa.list_(pa.float32(), list_size=2),
|
||||||
"item": pandas_string_type(),
|
"item": pa.utf8(),
|
||||||
"price": pa.float64(),
|
"price": pa.float64(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ import pytest
|
|||||||
from lance_namespace import (
|
from lance_namespace import (
|
||||||
CreateEmptyTableRequest,
|
CreateEmptyTableRequest,
|
||||||
CreateEmptyTableResponse,
|
CreateEmptyTableResponse,
|
||||||
DeclareTableRequest,
|
|
||||||
DeclareTableResponse,
|
|
||||||
DescribeTableRequest,
|
DescribeTableRequest,
|
||||||
DescribeTableResponse,
|
DescribeTableResponse,
|
||||||
LanceNamespace,
|
LanceNamespace,
|
||||||
@@ -162,19 +160,6 @@ class TrackingNamespace(LanceNamespace):
|
|||||||
|
|
||||||
return modified
|
return modified
|
||||||
|
|
||||||
def declare_table(self, request: DeclareTableRequest) -> DeclareTableResponse:
|
|
||||||
"""Track declare_table calls and inject rotating credentials."""
|
|
||||||
with self.lock:
|
|
||||||
self.create_call_count += 1
|
|
||||||
count = self.create_call_count
|
|
||||||
|
|
||||||
response = self.inner.declare_table(request)
|
|
||||||
response.storage_options = self._modify_storage_options(
|
|
||||||
response.storage_options, count
|
|
||||||
)
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
def create_empty_table(
|
def create_empty_table(
|
||||||
self, request: CreateEmptyTableRequest
|
self, request: CreateEmptyTableRequest
|
||||||
) -> CreateEmptyTableResponse:
|
) -> CreateEmptyTableResponse:
|
||||||
|
|||||||
@@ -1967,9 +1967,3 @@ def test_add_table_with_empty_embeddings(tmp_path):
|
|||||||
on_bad_vectors="drop",
|
on_bad_vectors="drop",
|
||||||
)
|
)
|
||||||
assert table.count_rows() == 1
|
assert table.count_rows() == 1
|
||||||
|
|
||||||
|
|
||||||
def test_table_uri(tmp_path):
|
|
||||||
db = lancedb.connect(tmp_path)
|
|
||||||
table = db.create_table("my_table", data=[{"x": 0}])
|
|
||||||
assert table.uri == str(tmp_path / "my_table.lance")
|
|
||||||
|
|||||||
@@ -528,19 +528,12 @@ def test_sanitize_data(
|
|||||||
else:
|
else:
|
||||||
expected_schema = schema
|
expected_schema = schema
|
||||||
else:
|
else:
|
||||||
from conftest import pandas_string_type
|
|
||||||
|
|
||||||
# polars uses large_string, pandas 3.0+ uses large_string, others use string
|
|
||||||
if isinstance(data, pl.DataFrame):
|
|
||||||
text_type = pa.large_utf8()
|
|
||||||
elif isinstance(data, pd.DataFrame):
|
|
||||||
text_type = pandas_string_type()
|
|
||||||
else:
|
|
||||||
text_type = pa.string()
|
|
||||||
expected_schema = pa.schema(
|
expected_schema = pa.schema(
|
||||||
{
|
{
|
||||||
"id": pa.int64(),
|
"id": pa.int64(),
|
||||||
"text": text_type,
|
"text": pa.large_utf8()
|
||||||
|
if isinstance(data, pl.DataFrame)
|
||||||
|
else pa.string(),
|
||||||
"vector": pa.list_(pa.float32(), 10),
|
"vector": pa.list_(pa.float32(), 10),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
+12
-5
@@ -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())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-22
@@ -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 {
|
||||||
@@ -304,10 +306,9 @@ impl Connection {
|
|||||||
},
|
},
|
||||||
page_token,
|
page_token,
|
||||||
limit: limit.map(|l| l as i32),
|
limit: limit.map(|l| l as i32),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
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)?;
|
||||||
@@ -327,25 +328,25 @@ impl Connection {
|
|||||||
let py = self_.py();
|
let py = self_.py();
|
||||||
future_into_py(py, async move {
|
future_into_py(py, async move {
|
||||||
use lance_namespace::models::CreateNamespaceRequest;
|
use lance_namespace::models::CreateNamespaceRequest;
|
||||||
// Mode is now a string field
|
let mode_value = mode.and_then(|m| match m.to_lowercase().as_str() {
|
||||||
let mode_str = mode.and_then(|m| match m.to_lowercase().as_str() {
|
|
||||||
"create" => Some("Create".to_string()),
|
"create" => Some("Create".to_string()),
|
||||||
"exist_ok" => Some("ExistOk".to_string()),
|
"exist_ok" => Some("ExistOk".to_string()),
|
||||||
"overwrite" => Some("Overwrite".to_string()),
|
"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_str,
|
mode: mode_value,
|
||||||
properties,
|
properties,
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
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())
|
||||||
@@ -364,29 +365,29 @@ impl Connection {
|
|||||||
let py = self_.py();
|
let py = self_.py();
|
||||||
future_into_py(py, async move {
|
future_into_py(py, async move {
|
||||||
use lance_namespace::models::DropNamespaceRequest;
|
use lance_namespace::models::DropNamespaceRequest;
|
||||||
// Mode and Behavior are now string fields
|
let mode_value = mode.and_then(|m| match m.to_uppercase().as_str() {
|
||||||
let mode_str = mode.and_then(|m| match m.to_uppercase().as_str() {
|
|
||||||
"SKIP" => Some("Skip".to_string()),
|
"SKIP" => Some("Skip".to_string()),
|
||||||
"FAIL" => Some("Fail".to_string()),
|
"FAIL" => Some("Fail".to_string()),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
let behavior_str = 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("Restrict".to_string()),
|
"RESTRICT" => Some("Restrict".to_string()),
|
||||||
"CASCADE" => Some("Cascade".to_string()),
|
"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_str,
|
mode: mode_value,
|
||||||
behavior: behavior_str,
|
behavior: behavior_value,
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
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)?;
|
||||||
@@ -405,15 +406,16 @@ 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 {
|
||||||
Some(namespace)
|
Some(namespace)
|
||||||
},
|
},
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
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())
|
||||||
@@ -433,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 {
|
||||||
@@ -440,10 +444,9 @@ impl Connection {
|
|||||||
},
|
},
|
||||||
page_token,
|
page_token,
|
||||||
limit: limit.map(|l| l as i32),
|
limit: limit.map(|l| l as i32),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
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
@@ -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(),
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
-10
@@ -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)?;
|
||||||
@@ -497,11 +497,6 @@ impl Table {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uri(self_: PyRef<'_, Self>) -> PyResult<Bound<'_, PyAny>> {
|
|
||||||
let inner = self_.inner_ref()?.clone();
|
|
||||||
future_into_py(self_.py(), async move { inner.uri().await.infer_error() })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn __repr__(&self) -> String {
|
pub fn __repr__(&self) -> String {
|
||||||
match &self.inner {
|
match &self.inner {
|
||||||
None => format!("ClosedTable({})", self.name),
|
None => format!("ClosedTable({})", self.name),
|
||||||
@@ -521,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| {
|
||||||
@@ -872,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);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lancedb"
|
name = "lancedb"
|
||||||
version = "0.24.0"
|
version = "0.23.1"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description = "LanceDB: A serverless, low-latency vector database for AI applications"
|
description = "LanceDB: A serverless, low-latency vector database for AI applications"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
+60
-128
@@ -36,42 +36,10 @@ use crate::remote::{
|
|||||||
};
|
};
|
||||||
use crate::table::{TableDefinition, WriteOptions};
|
use crate::table::{TableDefinition, WriteOptions};
|
||||||
use crate::Table;
|
use crate::Table;
|
||||||
use lance::io::ObjectStoreParams;
|
|
||||||
pub use lance_encoding::version::LanceFileVersion;
|
pub use lance_encoding::version::LanceFileVersion;
|
||||||
#[cfg(feature = "remote")]
|
#[cfg(feature = "remote")]
|
||||||
use lance_io::object_store::StorageOptions;
|
use lance_io::object_store::StorageOptions;
|
||||||
use lance_io::object_store::{StorageOptionsAccessor, StorageOptionsProvider};
|
use lance_io::object_store::StorageOptionsProvider;
|
||||||
|
|
||||||
fn merge_storage_options(
|
|
||||||
store_params: &mut ObjectStoreParams,
|
|
||||||
pairs: impl IntoIterator<Item = (String, String)>,
|
|
||||||
) {
|
|
||||||
let mut options = store_params.storage_options().cloned().unwrap_or_default();
|
|
||||||
for (key, value) in pairs {
|
|
||||||
options.insert(key, value);
|
|
||||||
}
|
|
||||||
let provider = store_params
|
|
||||||
.storage_options_accessor
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|accessor| accessor.provider().cloned());
|
|
||||||
let accessor = if let Some(provider) = provider {
|
|
||||||
StorageOptionsAccessor::with_initial_and_provider(options, provider)
|
|
||||||
} else {
|
|
||||||
StorageOptionsAccessor::with_static_options(options)
|
|
||||||
};
|
|
||||||
store_params.storage_options_accessor = Some(Arc::new(accessor));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_storage_options_provider(
|
|
||||||
store_params: &mut ObjectStoreParams,
|
|
||||||
provider: Arc<dyn StorageOptionsProvider>,
|
|
||||||
) {
|
|
||||||
let accessor = match store_params.storage_options().cloned() {
|
|
||||||
Some(options) => StorageOptionsAccessor::with_initial_and_provider(options, provider),
|
|
||||||
None => StorageOptionsAccessor::with_provider(provider),
|
|
||||||
};
|
|
||||||
store_params.storage_options_accessor = Some(Arc::new(accessor));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A builder for configuring a [`Connection::table_names`] operation
|
/// A builder for configuring a [`Connection::table_names`] operation
|
||||||
pub struct TableNamesBuilder {
|
pub struct TableNamesBuilder {
|
||||||
@@ -278,14 +246,16 @@ impl<const HAS_DATA: bool> CreateTableBuilder<HAS_DATA> {
|
|||||||
///
|
///
|
||||||
/// See available options at <https://lancedb.com/docs/storage/>
|
/// See available options at <https://lancedb.com/docs/storage/>
|
||||||
pub fn storage_option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
|
pub fn storage_option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
|
||||||
let store_params = self
|
let store_options = self
|
||||||
.request
|
.request
|
||||||
.write_options
|
.write_options
|
||||||
.lance_write_params
|
.lance_write_params
|
||||||
.get_or_insert(Default::default())
|
.get_or_insert(Default::default())
|
||||||
.store_params
|
.store_params
|
||||||
|
.get_or_insert(Default::default())
|
||||||
|
.storage_options
|
||||||
.get_or_insert(Default::default());
|
.get_or_insert(Default::default());
|
||||||
merge_storage_options(store_params, [(key.into(), value.into())]);
|
store_options.insert(key.into(), value.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,17 +269,19 @@ impl<const HAS_DATA: bool> CreateTableBuilder<HAS_DATA> {
|
|||||||
mut self,
|
mut self,
|
||||||
pairs: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
|
pairs: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let store_params = self
|
let store_options = self
|
||||||
.request
|
.request
|
||||||
.write_options
|
.write_options
|
||||||
.lance_write_params
|
.lance_write_params
|
||||||
.get_or_insert(Default::default())
|
.get_or_insert(Default::default())
|
||||||
.store_params
|
.store_params
|
||||||
|
.get_or_insert(Default::default())
|
||||||
|
.storage_options
|
||||||
.get_or_insert(Default::default());
|
.get_or_insert(Default::default());
|
||||||
let updates = pairs
|
|
||||||
.into_iter()
|
for (key, value) in pairs {
|
||||||
.map(|(key, value)| (key.into(), value.into()));
|
store_options.insert(key.into(), value.into());
|
||||||
merge_storage_options(store_params, updates);
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,21 +318,23 @@ impl<const HAS_DATA: bool> CreateTableBuilder<HAS_DATA> {
|
|||||||
/// This has no effect in LanceDB Cloud.
|
/// This has no effect in LanceDB Cloud.
|
||||||
#[deprecated(since = "0.15.1", note = "Use `database_options` instead")]
|
#[deprecated(since = "0.15.1", note = "Use `database_options` instead")]
|
||||||
pub fn enable_v2_manifest_paths(mut self, use_v2_manifest_paths: bool) -> Self {
|
pub fn enable_v2_manifest_paths(mut self, use_v2_manifest_paths: bool) -> Self {
|
||||||
let store_params = self
|
let storage_options = self
|
||||||
.request
|
.request
|
||||||
.write_options
|
.write_options
|
||||||
.lance_write_params
|
.lance_write_params
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.store_params
|
.store_params
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.storage_options
|
||||||
.get_or_insert_with(Default::default);
|
.get_or_insert_with(Default::default);
|
||||||
let value = if use_v2_manifest_paths {
|
|
||||||
"true".to_string()
|
storage_options.insert(
|
||||||
} else {
|
OPT_NEW_TABLE_V2_MANIFEST_PATHS.to_string(),
|
||||||
"false".to_string()
|
if use_v2_manifest_paths {
|
||||||
};
|
"true".to_string()
|
||||||
merge_storage_options(
|
} else {
|
||||||
store_params,
|
"false".to_string()
|
||||||
[(OPT_NEW_TABLE_V2_MANIFEST_PATHS.to_string(), value)],
|
},
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -370,19 +344,19 @@ impl<const HAS_DATA: bool> CreateTableBuilder<HAS_DATA> {
|
|||||||
/// The default is `LanceFileVersion::Stable`.
|
/// The default is `LanceFileVersion::Stable`.
|
||||||
#[deprecated(since = "0.15.1", note = "Use `database_options` instead")]
|
#[deprecated(since = "0.15.1", note = "Use `database_options` instead")]
|
||||||
pub fn data_storage_version(mut self, data_storage_version: LanceFileVersion) -> Self {
|
pub fn data_storage_version(mut self, data_storage_version: LanceFileVersion) -> Self {
|
||||||
let store_params = self
|
let storage_options = self
|
||||||
.request
|
.request
|
||||||
.write_options
|
.write_options
|
||||||
.lance_write_params
|
.lance_write_params
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.store_params
|
.store_params
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.storage_options
|
||||||
.get_or_insert_with(Default::default);
|
.get_or_insert_with(Default::default);
|
||||||
merge_storage_options(
|
|
||||||
store_params,
|
storage_options.insert(
|
||||||
[(
|
OPT_NEW_TABLE_STORAGE_VERSION.to_string(),
|
||||||
OPT_NEW_TABLE_STORAGE_VERSION.to_string(),
|
data_storage_version.to_string(),
|
||||||
data_storage_version.to_string(),
|
|
||||||
)],
|
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -407,14 +381,13 @@ impl<const HAS_DATA: bool> CreateTableBuilder<HAS_DATA> {
|
|||||||
/// This allows tables to automatically refresh cloud storage credentials
|
/// This allows tables to automatically refresh cloud storage credentials
|
||||||
/// when they expire, enabling long-running operations on remote storage.
|
/// when they expire, enabling long-running operations on remote storage.
|
||||||
pub fn storage_options_provider(mut self, provider: Arc<dyn StorageOptionsProvider>) -> Self {
|
pub fn storage_options_provider(mut self, provider: Arc<dyn StorageOptionsProvider>) -> Self {
|
||||||
let store_params = self
|
self.request
|
||||||
.request
|
|
||||||
.write_options
|
.write_options
|
||||||
.lance_write_params
|
.lance_write_params
|
||||||
.get_or_insert(Default::default())
|
.get_or_insert(Default::default())
|
||||||
.store_params
|
.store_params
|
||||||
.get_or_insert(Default::default());
|
.get_or_insert(Default::default())
|
||||||
set_storage_options_provider(store_params, provider);
|
.storage_options_provider = Some(provider);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -477,13 +450,15 @@ impl OpenTableBuilder {
|
|||||||
///
|
///
|
||||||
/// See available options at <https://lancedb.com/docs/storage/>
|
/// See available options at <https://lancedb.com/docs/storage/>
|
||||||
pub fn storage_option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
|
pub fn storage_option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
|
||||||
let store_params = self
|
let storage_options = self
|
||||||
.request
|
.request
|
||||||
.lance_read_params
|
.lance_read_params
|
||||||
.get_or_insert(Default::default())
|
.get_or_insert(Default::default())
|
||||||
.store_options
|
.store_options
|
||||||
|
.get_or_insert(Default::default())
|
||||||
|
.storage_options
|
||||||
.get_or_insert(Default::default());
|
.get_or_insert(Default::default());
|
||||||
merge_storage_options(store_params, [(key.into(), value.into())]);
|
storage_options.insert(key.into(), value.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,16 +472,18 @@ impl OpenTableBuilder {
|
|||||||
mut self,
|
mut self,
|
||||||
pairs: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
|
pairs: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let store_params = self
|
let storage_options = self
|
||||||
.request
|
.request
|
||||||
.lance_read_params
|
.lance_read_params
|
||||||
.get_or_insert(Default::default())
|
.get_or_insert(Default::default())
|
||||||
.store_options
|
.store_options
|
||||||
|
.get_or_insert(Default::default())
|
||||||
|
.storage_options
|
||||||
.get_or_insert(Default::default());
|
.get_or_insert(Default::default());
|
||||||
let updates = pairs
|
|
||||||
.into_iter()
|
for (key, value) in pairs {
|
||||||
.map(|(key, value)| (key.into(), value.into()));
|
storage_options.insert(key.into(), value.into());
|
||||||
merge_storage_options(store_params, updates);
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,13 +507,12 @@ impl OpenTableBuilder {
|
|||||||
/// This allows tables to automatically refresh cloud storage credentials
|
/// This allows tables to automatically refresh cloud storage credentials
|
||||||
/// when they expire, enabling long-running operations on remote storage.
|
/// when they expire, enabling long-running operations on remote storage.
|
||||||
pub fn storage_options_provider(mut self, provider: Arc<dyn StorageOptionsProvider>) -> Self {
|
pub fn storage_options_provider(mut self, provider: Arc<dyn StorageOptionsProvider>) -> Self {
|
||||||
let store_params = self
|
self.request
|
||||||
.request
|
|
||||||
.lance_read_params
|
.lance_read_params
|
||||||
.get_or_insert(Default::default())
|
.get_or_insert(Default::default())
|
||||||
.store_options
|
.store_options
|
||||||
.get_or_insert(Default::default());
|
.get_or_insert(Default::default())
|
||||||
set_storage_options_provider(store_params, provider);
|
.storage_options_provider = Some(provider);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -892,10 +868,6 @@ pub struct ConnectBuilder {
|
|||||||
embedding_registry: Option<Arc<dyn EmbeddingRegistry>>,
|
embedding_registry: Option<Arc<dyn EmbeddingRegistry>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "remote")]
|
|
||||||
const ENV_VARS_TO_STORAGE_OPTS: [(&str, &str); 1] =
|
|
||||||
[("AZURE_STORAGE_ACCOUNT_NAME", "azure_storage_account_name")];
|
|
||||||
|
|
||||||
impl ConnectBuilder {
|
impl ConnectBuilder {
|
||||||
/// Create a new [`ConnectOptions`] with the given database URI.
|
/// Create a new [`ConnectOptions`] with the given database URI.
|
||||||
pub fn new(uri: &str) -> Self {
|
pub fn new(uri: &str) -> Self {
|
||||||
@@ -1079,27 +1051,11 @@ impl ConnectBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "remote")]
|
|
||||||
fn apply_env_defaults(
|
|
||||||
env_var_to_remote_storage_option: &[(&str, &str)],
|
|
||||||
options: &mut HashMap<String, String>,
|
|
||||||
) {
|
|
||||||
for (env_key, opt_key) in env_var_to_remote_storage_option {
|
|
||||||
if let Ok(env_value) = std::env::var(env_key) {
|
|
||||||
if !options.contains_key(*opt_key) {
|
|
||||||
options.insert((*opt_key).to_string(), env_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "remote")]
|
#[cfg(feature = "remote")]
|
||||||
fn execute_remote(self) -> Result<Connection> {
|
fn execute_remote(self) -> Result<Connection> {
|
||||||
use crate::remote::db::RemoteDatabaseOptions;
|
use crate::remote::db::RemoteDatabaseOptions;
|
||||||
|
|
||||||
let mut merged_options = self.request.options.clone();
|
let options = RemoteDatabaseOptions::parse_from_map(&self.request.options)?;
|
||||||
Self::apply_env_defaults(&ENV_VARS_TO_STORAGE_OPTS, &mut merged_options);
|
|
||||||
let options = RemoteDatabaseOptions::parse_from_map(&merged_options)?;
|
|
||||||
|
|
||||||
let region = options.region.ok_or_else(|| Error::InvalidInput {
|
let region = options.region.ok_or_else(|| Error::InvalidInput {
|
||||||
message: "A region is required when connecting to LanceDb Cloud".to_string(),
|
message: "A region is required when connecting to LanceDb Cloud".to_string(),
|
||||||
@@ -1321,6 +1277,8 @@ mod test_utils {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::fs::create_dir_all;
|
||||||
|
|
||||||
use crate::database::listing::{ListingDatabaseOptions, NewTableConfig};
|
use crate::database::listing::{ListingDatabaseOptions, NewTableConfig};
|
||||||
use crate::query::QueryBase;
|
use crate::query::QueryBase;
|
||||||
use crate::query::{ExecutableQuery, QueryExecutionOptions};
|
use crate::query::{ExecutableQuery, QueryExecutionOptions};
|
||||||
@@ -1344,23 +1302,6 @@ mod tests {
|
|||||||
assert_eq!(tc.connection.uri(), tc.uri);
|
assert_eq!(tc.connection.uri(), tc.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "remote")]
|
|
||||||
#[test]
|
|
||||||
fn test_apply_env_defaults() {
|
|
||||||
let env_key = "TEST_APPLY_ENV_DEFAULTS_ENVIRONMENT_VARIABLE_ENV_KEY";
|
|
||||||
let env_val = "TEST_APPLY_ENV_DEFAULTS_ENVIRONMENT_VARIABLE_ENV_VAL";
|
|
||||||
let opts_key = "test_apply_env_defaults_environment_variable_opts_key";
|
|
||||||
std::env::set_var(env_key, env_val);
|
|
||||||
|
|
||||||
let mut options = HashMap::new();
|
|
||||||
ConnectBuilder::apply_env_defaults(&[(env_key, opts_key)], &mut options);
|
|
||||||
assert_eq!(Some(&env_val.to_string()), options.get(opts_key));
|
|
||||||
|
|
||||||
options.insert(opts_key.to_string(), "EXPLICIT-VALUE".to_string());
|
|
||||||
ConnectBuilder::apply_env_defaults(&[(env_key, opts_key)], &mut options);
|
|
||||||
assert_eq!(Some(&"EXPLICIT-VALUE".to_string()), options.get(opts_key));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_connect_relative() {
|
async fn test_connect_relative() {
|
||||||
@@ -1585,27 +1526,18 @@ mod tests {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn drop_table() {
|
async fn drop_table() {
|
||||||
let tc = new_test_connection().await.unwrap();
|
let tmp_dir = tempdir().unwrap();
|
||||||
let db = tc.connection;
|
|
||||||
|
|
||||||
if tc.is_remote {
|
let uri = tmp_dir.path().to_str().unwrap();
|
||||||
// All the typical endpoints such as s3:///, file-object-store:///, etc. treat drop_table
|
let db = connect(uri).execute().await.unwrap();
|
||||||
// as idempotent.
|
|
||||||
assert!(db.drop_table("invalid_table", &[]).await.is_ok());
|
|
||||||
} else {
|
|
||||||
// The behavior of drop_table when using a file:/// endpoint differs from all other
|
|
||||||
// object providers, in that it returns an error when deleting a non-existent table.
|
|
||||||
assert!(matches!(
|
|
||||||
db.drop_table("invalid_table", &[]).await,
|
|
||||||
Err(crate::Error::TableNotFound { .. }),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let schema = Arc::new(Schema::new(vec![Field::new("x", DataType::Int32, false)]));
|
// drop non-exist table
|
||||||
db.create_empty_table("table1", schema.clone())
|
assert!(matches!(
|
||||||
.execute()
|
db.drop_table("invalid_table", &[]).await,
|
||||||
.await
|
Err(crate::Error::TableNotFound { .. }),
|
||||||
.unwrap();
|
));
|
||||||
|
|
||||||
|
create_dir_all(tmp_dir.path().join("table1.lance")).unwrap();
|
||||||
db.drop_table("table1", &[]).await.unwrap();
|
db.drop_table("table1", &[]).await.unwrap();
|
||||||
|
|
||||||
let tables = db.table_names().execute().await.unwrap();
|
let tables = db.table_names().execute().await.unwrap();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use lance::dataset::{builder::DatasetBuilder, ReadParams, WriteMode};
|
|||||||
use lance::io::{ObjectStore, ObjectStoreParams, WrappingObjectStore};
|
use lance::io::{ObjectStore, ObjectStoreParams, WrappingObjectStore};
|
||||||
use lance_datafusion::utils::StreamingWriteSource;
|
use lance_datafusion::utils::StreamingWriteSource;
|
||||||
use lance_encoding::version::LanceFileVersion;
|
use lance_encoding::version::LanceFileVersion;
|
||||||
use lance_io::object_store::{StorageOptionsAccessor, StorageOptionsProvider};
|
use lance_io::object_store::StorageOptionsProvider;
|
||||||
use lance_table::io::commit::commit_handler_from_url;
|
use lance_table::io::commit::commit_handler_from_url;
|
||||||
use object_store::local::LocalFileSystem;
|
use object_store::local::LocalFileSystem;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
@@ -356,13 +356,7 @@ impl ListingDatabase {
|
|||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| Arc::new(lance::session::Session::default()));
|
.unwrap_or_else(|| Arc::new(lance::session::Session::default()));
|
||||||
let os_params = ObjectStoreParams {
|
let os_params = ObjectStoreParams {
|
||||||
storage_options_accessor: if options.storage_options.is_empty() {
|
storage_options: Some(options.storage_options.clone()),
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Arc::new(StorageOptionsAccessor::with_static_options(
|
|
||||||
options.storage_options.clone(),
|
|
||||||
)))
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let (object_store, base_path) = ObjectStore::from_uri_and_params(
|
let (object_store, base_path) = ObjectStore::from_uri_and_params(
|
||||||
@@ -469,20 +463,9 @@ impl ListingDatabase {
|
|||||||
validate_table_name(name)?;
|
validate_table_name(name)?;
|
||||||
|
|
||||||
let mut uri = self.uri.clone();
|
let mut uri = self.uri.clone();
|
||||||
// If the URI does not end with a path separator, add one
|
// If the URI does not end with a slash, add one
|
||||||
// Use forward slash for URIs (http://, s3://, gs://, file://, etc.)
|
if !uri.ends_with('/') {
|
||||||
// Use platform-specific separator for local paths without scheme
|
uri.push('/');
|
||||||
let has_scheme = uri.contains("://");
|
|
||||||
let ends_with_separator = uri.ends_with('/') || uri.ends_with('\\');
|
|
||||||
|
|
||||||
if !ends_with_separator {
|
|
||||||
if has_scheme {
|
|
||||||
// URIs always use forward slash
|
|
||||||
uri.push('/');
|
|
||||||
} else {
|
|
||||||
// Local path without scheme - use platform separator
|
|
||||||
uri.push(std::path::MAIN_SEPARATOR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Append the table name with the lance file extension
|
// Append the table name with the lance file extension
|
||||||
uri.push_str(&format!("{}.{}", name, LANCE_FILE_EXTENSION));
|
uri.push_str(&format!("{}.{}", name, LANCE_FILE_EXTENSION));
|
||||||
@@ -498,13 +481,7 @@ impl ListingDatabase {
|
|||||||
|
|
||||||
async fn drop_tables(&self, names: Vec<String>) -> Result<()> {
|
async fn drop_tables(&self, names: Vec<String>) -> Result<()> {
|
||||||
let object_store_params = ObjectStoreParams {
|
let object_store_params = ObjectStoreParams {
|
||||||
storage_options_accessor: if self.storage_options.is_empty() {
|
storage_options: Some(self.storage_options.clone()),
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Arc::new(StorageOptionsAccessor::with_static_options(
|
|
||||||
self.storage_options.clone(),
|
|
||||||
)))
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut uri = self.uri.clone();
|
let mut uri = self.uri.clone();
|
||||||
@@ -553,7 +530,7 @@ impl ListingDatabase {
|
|||||||
.lance_write_params
|
.lance_write_params
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|p| p.store_params.as_ref())
|
.and_then(|p| p.store_params.as_ref())
|
||||||
.and_then(|sp| sp.storage_options());
|
.and_then(|sp| sp.storage_options.as_ref());
|
||||||
|
|
||||||
let storage_version_override = storage_options
|
let storage_version_override = storage_options
|
||||||
.and_then(|opts| opts.get(OPT_NEW_TABLE_STORAGE_VERSION))
|
.and_then(|opts| opts.get(OPT_NEW_TABLE_STORAGE_VERSION))
|
||||||
@@ -604,20 +581,21 @@ impl ListingDatabase {
|
|||||||
// will cause a new connection to be created, and that connection will
|
// will cause a new connection to be created, and that connection will
|
||||||
// be dropped from the cache when python GCs the table object, which
|
// be dropped from the cache when python GCs the table object, which
|
||||||
// confounds reuse across tables.
|
// confounds reuse across tables.
|
||||||
if !self.storage_options.is_empty() || self.storage_options_provider.is_some() {
|
if !self.storage_options.is_empty() {
|
||||||
let store_params = write_params
|
let storage_options = write_params
|
||||||
.store_params
|
.store_params
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.storage_options
|
||||||
.get_or_insert_with(Default::default);
|
.get_or_insert_with(Default::default);
|
||||||
let mut storage_options = store_params.storage_options().cloned().unwrap_or_default();
|
self.inherit_storage_options(storage_options);
|
||||||
if !self.storage_options.is_empty() {
|
}
|
||||||
self.inherit_storage_options(&mut storage_options);
|
|
||||||
}
|
// Set storage options provider if available
|
||||||
let accessor = if let Some(ref provider) = self.storage_options_provider {
|
if self.storage_options_provider.is_some() {
|
||||||
StorageOptionsAccessor::with_initial_and_provider(storage_options, provider.clone())
|
write_params
|
||||||
} else {
|
.store_params
|
||||||
StorageOptionsAccessor::with_static_options(storage_options)
|
.get_or_insert_with(Default::default)
|
||||||
};
|
.storage_options_provider = self.storage_options_provider.clone();
|
||||||
store_params.storage_options_accessor = Some(Arc::new(accessor));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write_params.data_storage_version = self
|
write_params.data_storage_version = self
|
||||||
@@ -903,13 +881,7 @@ impl Database for ListingDatabase {
|
|||||||
validate_table_name(&request.target_table_name)?;
|
validate_table_name(&request.target_table_name)?;
|
||||||
|
|
||||||
let storage_params = ObjectStoreParams {
|
let storage_params = ObjectStoreParams {
|
||||||
storage_options_accessor: if self.storage_options.is_empty() {
|
storage_options: Some(self.storage_options.clone()),
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Arc::new(StorageOptionsAccessor::with_static_options(
|
|
||||||
self.storage_options.clone(),
|
|
||||||
)))
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let read_params = ReadParams {
|
let read_params = ReadParams {
|
||||||
@@ -973,28 +945,25 @@ impl Database for ListingDatabase {
|
|||||||
// will cause a new connection to be created, and that connection will
|
// will cause a new connection to be created, and that connection will
|
||||||
// be dropped from the cache when python GCs the table object, which
|
// be dropped from the cache when python GCs the table object, which
|
||||||
// confounds reuse across tables.
|
// confounds reuse across tables.
|
||||||
if !self.storage_options.is_empty() || self.storage_options_provider.is_some() {
|
if !self.storage_options.is_empty() {
|
||||||
let store_params = request
|
let storage_options = request
|
||||||
.lance_read_params
|
.lance_read_params
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.store_options
|
.store_options
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.storage_options
|
||||||
.get_or_insert_with(Default::default);
|
.get_or_insert_with(Default::default);
|
||||||
let mut storage_options = store_params.storage_options().cloned().unwrap_or_default();
|
self.inherit_storage_options(storage_options);
|
||||||
if !self.storage_options.is_empty() {
|
}
|
||||||
self.inherit_storage_options(&mut storage_options);
|
|
||||||
}
|
// Set storage options provider if available
|
||||||
// Preserve request-level provider if no connection-level provider exists
|
if self.storage_options_provider.is_some() {
|
||||||
let request_provider = store_params
|
request
|
||||||
.storage_options_accessor
|
.lance_read_params
|
||||||
.as_ref()
|
.get_or_insert_with(Default::default)
|
||||||
.and_then(|a| a.provider().cloned());
|
.store_options
|
||||||
let provider = self.storage_options_provider.clone().or(request_provider);
|
.get_or_insert_with(Default::default)
|
||||||
let accessor = if let Some(provider) = provider {
|
.storage_options_provider = self.storage_options_provider.clone();
|
||||||
StorageOptionsAccessor::with_initial_and_provider(storage_options, provider)
|
|
||||||
} else {
|
|
||||||
StorageOptionsAccessor::with_static_options(storage_options)
|
|
||||||
};
|
|
||||||
store_params.storage_options_accessor = Some(Arc::new(accessor));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some ReadParams are exposed in the OpenTableBuilder, but we also
|
// Some ReadParams are exposed in the OpenTableBuilder, but we also
|
||||||
@@ -1102,7 +1071,6 @@ mod tests {
|
|||||||
use crate::table::{Table, TableDefinition};
|
use crate::table::{Table, TableDefinition};
|
||||||
use arrow_array::{Int32Array, RecordBatch, StringArray};
|
use arrow_array::{Int32Array, RecordBatch, StringArray};
|
||||||
use arrow_schema::{DataType, Field, Schema};
|
use arrow_schema::{DataType, Field, Schema};
|
||||||
use std::path::PathBuf;
|
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
async fn setup_database() -> (tempfile::TempDir, ListingDatabase) {
|
async fn setup_database() -> (tempfile::TempDir, ListingDatabase) {
|
||||||
@@ -1901,9 +1869,7 @@ mod tests {
|
|||||||
let write_options = WriteOptions {
|
let write_options = WriteOptions {
|
||||||
lance_write_params: Some(lance::dataset::WriteParams {
|
lance_write_params: Some(lance::dataset::WriteParams {
|
||||||
store_params: Some(lance::io::ObjectStoreParams {
|
store_params: Some(lance::io::ObjectStoreParams {
|
||||||
storage_options_accessor: Some(Arc::new(
|
storage_options: Some(storage_options),
|
||||||
StorageOptionsAccessor::with_static_options(storage_options),
|
|
||||||
)),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -1977,9 +1943,7 @@ mod tests {
|
|||||||
let write_options = WriteOptions {
|
let write_options = WriteOptions {
|
||||||
lance_write_params: Some(lance::dataset::WriteParams {
|
lance_write_params: Some(lance::dataset::WriteParams {
|
||||||
store_params: Some(lance::io::ObjectStoreParams {
|
store_params: Some(lance::io::ObjectStoreParams {
|
||||||
storage_options_accessor: Some(Arc::new(
|
storage_options: Some(storage_options),
|
||||||
StorageOptionsAccessor::with_static_options(storage_options),
|
|
||||||
)),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -2082,19 +2046,6 @@ mod tests {
|
|||||||
assert_eq!(db_options.new_table_config.enable_stable_row_ids, None);
|
assert_eq!(db_options.new_table_config.enable_stable_row_ids, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_table_uri() {
|
|
||||||
let (_tempdir, db) = setup_database().await;
|
|
||||||
|
|
||||||
let mut pb = PathBuf::new();
|
|
||||||
pb.push(db.uri.clone());
|
|
||||||
pb.push("test.lance");
|
|
||||||
|
|
||||||
let expected = pb.to_str().unwrap();
|
|
||||||
let uri = db.table_uri("test").ok().unwrap();
|
|
||||||
assert_eq!(uri, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_namespace_client() {
|
async fn test_namespace_client() {
|
||||||
let (_tempdir, db) = setup_database().await;
|
let (_tempdir, db) = setup_database().await;
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ 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,
|
||||||
DeclareTableRequest, DescribeNamespaceRequest, DescribeNamespaceResponse,
|
DescribeNamespaceRequest, DescribeNamespaceResponse, DescribeTableRequest,
|
||||||
DescribeTableRequest, DropNamespaceRequest, DropNamespaceResponse, DropTableRequest,
|
DropNamespaceRequest, DropNamespaceResponse, DropTableRequest, ListNamespacesRequest,
|
||||||
ListNamespacesRequest, ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
|
ListNamespacesResponse, ListTablesRequest, ListTablesResponse,
|
||||||
},
|
},
|
||||||
LanceNamespace,
|
LanceNamespace,
|
||||||
};
|
};
|
||||||
use lance_namespace_impls::ConnectBuilder;
|
use lance_namespace_impls::ConnectBuilder;
|
||||||
use log::warn;
|
|
||||||
|
|
||||||
use crate::database::ReadConsistency;
|
use crate::database::ReadConsistency;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
@@ -135,10 +134,11 @@ 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),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self.namespace.list_tables(ns_request).await?;
|
let response = self.namespace.list_tables(ns_request).await?;
|
||||||
@@ -154,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()),
|
||||||
..Default::default()
|
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;
|
||||||
@@ -172,8 +177,9 @@ 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()),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
self.namespace
|
self.namespace
|
||||||
.drop_table(drop_request)
|
.drop_table(drop_request)
|
||||||
@@ -205,53 +211,31 @@ 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());
|
||||||
|
|
||||||
// Try declare_table first, falling back to create_empty_table for backwards
|
let create_empty_request = DeclareTableRequest {
|
||||||
// compatibility with older namespace clients that don't support declare_table
|
identity: None,
|
||||||
let declare_request = DeclareTableRequest {
|
context: None,
|
||||||
id: Some(table_id.clone()),
|
id: Some(table_id.clone()),
|
||||||
..Default::default()
|
location: None,
|
||||||
|
vend_credentials: if self.storage_options.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(true)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let location = match self.namespace.declare_table(declare_request).await {
|
let create_empty_response = self
|
||||||
Ok(response) => response.location.ok_or_else(|| Error::Runtime {
|
.namespace
|
||||||
message: "Table location is missing from declare_table response".to_string(),
|
.declare_table(create_empty_request)
|
||||||
})?,
|
.await
|
||||||
Err(e) => {
|
.map_err(|e| Error::Runtime {
|
||||||
// Check if the error is "not supported" and try create_empty_table as fallback
|
message: format!("Failed to declare table: {}", e),
|
||||||
let err_str = e.to_string().to_lowercase();
|
})?;
|
||||||
if err_str.contains("not supported") || err_str.contains("not implemented") {
|
|
||||||
warn!(
|
|
||||||
"declare_table is not supported by the namespace client, \
|
|
||||||
falling back to deprecated create_empty_table. \
|
|
||||||
create_empty_table is deprecated and will be removed in Lance 3.0.0. \
|
|
||||||
Please upgrade your namespace client to support declare_table."
|
|
||||||
);
|
|
||||||
#[allow(deprecated)]
|
|
||||||
let create_empty_request = CreateEmptyTableRequest {
|
|
||||||
id: Some(table_id.clone()),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
let location = create_empty_response
|
||||||
let create_response = self
|
.location
|
||||||
.namespace
|
.ok_or_else(|| Error::Runtime {
|
||||||
.create_empty_table(create_empty_request)
|
message: "Table location is missing from create_empty_table response".to_string(),
|
||||||
.await
|
})?;
|
||||||
.map_err(|e| Error::Runtime {
|
|
||||||
message: format!("Failed to create empty table: {}", e),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
create_response.location.ok_or_else(|| Error::Runtime {
|
|
||||||
message: "Table location is missing from create_empty_table response"
|
|
||||||
.to_string(),
|
|
||||||
})?
|
|
||||||
} else {
|
|
||||||
return Err(Error::Runtime {
|
|
||||||
message: format!("Failed to declare table: {}", e),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let native_table = NativeTable::create_from_namespace(
|
let native_table = NativeTable::create_from_namespace(
|
||||||
self.namespace.clone(),
|
self.namespace.clone(),
|
||||||
@@ -309,8 +293,9 @@ impl Database for LanceNamespaceDatabase {
|
|||||||
table_id.push(name.to_string());
|
table_id.push(name.to_string());
|
||||||
|
|
||||||
let drop_request = DropTableRequest {
|
let drop_request = DropTableRequest {
|
||||||
|
identity: None,
|
||||||
|
context: None,
|
||||||
id: Some(table_id),
|
id: Some(table_id),
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
self.namespace
|
self.namespace
|
||||||
.drop_table(drop_request)
|
.drop_table(drop_request)
|
||||||
@@ -465,8 +450,11 @@ 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()]),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -525,8 +513,11 @@ 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()]),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -588,8 +579,11 @@ 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()]),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -671,8 +665,11 @@ 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()]),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -726,8 +723,11 @@ 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()]),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -806,8 +806,11 @@ 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()]),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -839,8 +842,11 @@ 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()]),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ impl Shuffler {
|
|||||||
// This is kind of an annoying limitation but if we allow runt clumps from batches then
|
// This is kind of an annoying limitation but if we allow runt clumps from batches then
|
||||||
// clumps will get unaligned and we will mess up the clumps when we do the in-memory
|
// clumps will get unaligned and we will mess up the clumps when we do the in-memory
|
||||||
// shuffle step. If this is a problem we can probably figure out a better way to do this.
|
// shuffle step. If this is a problem we can probably figure out a better way to do this.
|
||||||
if !is_last && !(batch.num_rows() as u64).is_multiple_of(clump_size) {
|
if !is_last && batch.num_rows() as u64 % clump_size != 0 {
|
||||||
return Err(Error::Runtime {
|
return Err(Error::Runtime {
|
||||||
message: format!(
|
message: format!(
|
||||||
"Expected batch size ({}) to be divisible by clump size ({})",
|
"Expected batch size ({}) to be divisible by clump size ({})",
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
|
||||||
|
|
||||||
use std::sync::{
|
use std::{
|
||||||
atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
|
iter,
|
||||||
Arc,
|
sync::{
|
||||||
|
atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use arrow_array::{Array, BooleanArray, RecordBatch, UInt64Array};
|
use arrow_array::{Array, BooleanArray, RecordBatch, UInt64Array};
|
||||||
@@ -155,7 +158,7 @@ impl Splitter {
|
|||||||
remaining_in_split
|
remaining_in_split
|
||||||
};
|
};
|
||||||
|
|
||||||
split_ids.extend(std::iter::repeat_n(split_id as u64, rows_to_add as usize));
|
split_ids.extend(iter::repeat(split_id as u64).take(rows_to_add as usize));
|
||||||
if done {
|
if done {
|
||||||
// Quit early if we've run out of splits
|
// Quit early if we've run out of splits
|
||||||
break;
|
break;
|
||||||
@@ -659,7 +662,7 @@ mod tests {
|
|||||||
assert_eq!(split_batch.num_rows(), total_split_sizes as usize);
|
assert_eq!(split_batch.num_rows(), total_split_sizes as usize);
|
||||||
let mut expected = Vec::with_capacity(total_split_sizes as usize);
|
let mut expected = Vec::with_capacity(total_split_sizes as usize);
|
||||||
for (i, size) in expected_split_sizes.iter().enumerate() {
|
for (i, size) in expected_split_sizes.iter().enumerate() {
|
||||||
expected.extend(std::iter::repeat_n(i as u64, *size as usize));
|
expected.extend(iter::repeat(i as u64).take(*size as usize));
|
||||||
}
|
}
|
||||||
let expected = Arc::new(UInt64Array::from(expected)) as Arc<dyn Array>;
|
let expected = Arc::new(UInt64Array::from(expected)) as Arc<dyn Array>;
|
||||||
|
|
||||||
|
|||||||
@@ -297,10 +297,10 @@ impl IvfPqIndexBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn suggested_num_sub_vectors(dim: u32) -> u32 {
|
pub(crate) fn suggested_num_sub_vectors(dim: u32) -> u32 {
|
||||||
if dim.is_multiple_of(16) {
|
if dim % 16 == 0 {
|
||||||
// Should be more aggressive than this default.
|
// Should be more aggressive than this default.
|
||||||
dim / 16
|
dim / 16
|
||||||
} else if dim.is_multiple_of(8) {
|
} else if dim % 8 == 0 {
|
||||||
dim / 8
|
dim / 8
|
||||||
} else {
|
} else {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
|
|||||||
@@ -51,15 +51,17 @@
|
|||||||
//! - `s3://bucket/path/to/database` or `gs://bucket/path/to/database` - database on cloud object store
|
//! - `s3://bucket/path/to/database` or `gs://bucket/path/to/database` - database on cloud object store
|
||||||
//! - `db://dbname` - Lance Cloud
|
//! - `db://dbname` - Lance Cloud
|
||||||
//!
|
//!
|
||||||
//! You can also use [`ConnectBuilder`] to configure the connection to the database.
|
//! You can also use [`ConnectOptions`] to configure the connection to the database.
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
|
//! use object_store::aws::AwsCredential;
|
||||||
//! # tokio::runtime::Runtime::new().unwrap().block_on(async {
|
//! # tokio::runtime::Runtime::new().unwrap().block_on(async {
|
||||||
//! let db = lancedb::connect("data/sample-lancedb")
|
//! let db = lancedb::connect("data/sample-lancedb")
|
||||||
//! .storage_options([
|
//! .aws_creds(AwsCredential {
|
||||||
//! ("aws_access_key_id", "some_key"),
|
//! key_id: "some_key".to_string(),
|
||||||
//! ("aws_secret_access_key", "some_secret"),
|
//! secret_key: "some_secret".to_string(),
|
||||||
//! ])
|
//! token: None,
|
||||||
|
//! })
|
||||||
//! .execute()
|
//! .execute()
|
||||||
//! .await
|
//! .await
|
||||||
//! .unwrap();
|
//! .unwrap();
|
||||||
|
|||||||
@@ -1717,8 +1717,11 @@ 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()),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -1742,8 +1745,11 @@ 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()),
|
||||||
..Default::default()
|
page_token: None,
|
||||||
|
limit: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to list tables");
|
.expect("Failed to list tables");
|
||||||
@@ -1753,8 +1759,11 @@ 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()),
|
||||||
..Default::default()
|
page_token: None,
|
||||||
|
limit: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1793,8 +1802,11 @@ 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()),
|
||||||
..Default::default()
|
mode: None,
|
||||||
|
properties: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create namespace");
|
.expect("Failed to create namespace");
|
||||||
@@ -1818,8 +1830,11 @@ 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()),
|
||||||
..Default::default()
|
page_token: None,
|
||||||
|
limit: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -204,7 +204,6 @@ pub struct RemoteTable<S: HttpSend = Sender> {
|
|||||||
server_version: ServerVersion,
|
server_version: ServerVersion,
|
||||||
|
|
||||||
version: RwLock<Option<u64>>,
|
version: RwLock<Option<u64>>,
|
||||||
location: RwLock<Option<String>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: HttpSend> RemoteTable<S> {
|
impl<S: HttpSend> RemoteTable<S> {
|
||||||
@@ -222,7 +221,6 @@ impl<S: HttpSend> RemoteTable<S> {
|
|||||||
identifier,
|
identifier,
|
||||||
server_version,
|
server_version,
|
||||||
version: RwLock::new(None),
|
version: RwLock::new(None),
|
||||||
location: RwLock::new(None),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,7 +639,6 @@ impl<S: HttpSend> RemoteTable<S> {
|
|||||||
struct TableDescription {
|
struct TableDescription {
|
||||||
version: u64,
|
version: u64,
|
||||||
schema: JsonSchema,
|
schema: JsonSchema,
|
||||||
location: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: HttpSend> std::fmt::Display for RemoteTable<S> {
|
impl<S: HttpSend> std::fmt::Display for RemoteTable<S> {
|
||||||
@@ -670,7 +667,6 @@ mod test_utils {
|
|||||||
identifier: name,
|
identifier: name,
|
||||||
server_version: version.map(ServerVersion).unwrap_or_default(),
|
server_version: version.map(ServerVersion).unwrap_or_default(),
|
||||||
version: RwLock::new(None),
|
version: RwLock::new(None),
|
||||||
location: RwLock::new(None),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1465,28 +1461,8 @@ impl<S: HttpSend> BaseTable for RemoteTable<S> {
|
|||||||
message: "table_definition is not supported on LanceDB cloud.".into(),
|
message: "table_definition is not supported on LanceDB cloud.".into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
async fn uri(&self) -> Result<String> {
|
fn dataset_uri(&self) -> &str {
|
||||||
// Check if we already have the location cached
|
"NOT_SUPPORTED"
|
||||||
{
|
|
||||||
let location = self.location.read().await;
|
|
||||||
if let Some(ref loc) = *location {
|
|
||||||
return Ok(loc.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch from server via describe
|
|
||||||
let description = self.describe().await?;
|
|
||||||
let location = description.location.ok_or_else(|| Error::NotSupported {
|
|
||||||
message: "Table URI not supported by the server".into(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Cache the location for future use
|
|
||||||
{
|
|
||||||
let mut cached_location = self.location.write().await;
|
|
||||||
*cached_location = Some(location.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(location)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn storage_options(&self) -> Option<HashMap<String, String>> {
|
async fn storage_options(&self) -> Option<HashMap<String, String>> {
|
||||||
@@ -3356,69 +3332,4 @@ mod tests {
|
|||||||
let result = table.drop_columns(&["old_col1", "old_col2"]).await.unwrap();
|
let result = table.drop_columns(&["old_col1", "old_col2"]).await.unwrap();
|
||||||
assert_eq!(result.version, 5);
|
assert_eq!(result.version, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_uri() {
|
|
||||||
let table = Table::new_with_handler("my_table", |request| {
|
|
||||||
assert_eq!(request.method(), "POST");
|
|
||||||
assert_eq!(request.url().path(), "/v1/table/my_table/describe/");
|
|
||||||
|
|
||||||
http::Response::builder()
|
|
||||||
.status(200)
|
|
||||||
.body(r#"{"version": 1, "schema": {"fields": []}, "location": "s3://bucket/path/to/table"}"#)
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
let uri = table.uri().await.unwrap();
|
|
||||||
assert_eq!(uri, "s3://bucket/path/to/table");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_uri_missing_location() {
|
|
||||||
let table = Table::new_with_handler("my_table", |request| {
|
|
||||||
assert_eq!(request.method(), "POST");
|
|
||||||
assert_eq!(request.url().path(), "/v1/table/my_table/describe/");
|
|
||||||
|
|
||||||
// Server returns response without location field
|
|
||||||
http::Response::builder()
|
|
||||||
.status(200)
|
|
||||||
.body(r#"{"version": 1, "schema": {"fields": []}}"#)
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
let result = table.uri().await;
|
|
||||||
assert!(result.is_err());
|
|
||||||
assert!(matches!(&result, Err(Error::NotSupported { .. })));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_uri_caching() {
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
let call_count = Arc::new(AtomicUsize::new(0));
|
|
||||||
let call_count_clone = call_count.clone();
|
|
||||||
|
|
||||||
let table = Table::new_with_handler("my_table", move |request| {
|
|
||||||
assert_eq!(request.url().path(), "/v1/table/my_table/describe/");
|
|
||||||
call_count_clone.fetch_add(1, Ordering::SeqCst);
|
|
||||||
|
|
||||||
http::Response::builder()
|
|
||||||
.status(200)
|
|
||||||
.body(
|
|
||||||
r#"{"version": 1, "schema": {"fields": []}, "location": "gs://bucket/table"}"#,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
// First call should fetch from server
|
|
||||||
let uri1 = table.uri().await.unwrap();
|
|
||||||
assert_eq!(uri1, "gs://bucket/table");
|
|
||||||
assert_eq!(call_count.load(Ordering::SeqCst), 1);
|
|
||||||
|
|
||||||
// Second call should use cached value
|
|
||||||
let uri2 = table.uri().await.unwrap();
|
|
||||||
assert_eq!(uri2, "gs://bucket/table");
|
|
||||||
assert_eq!(call_count.load(Ordering::SeqCst), 1); // Still 1, no new call
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-35
@@ -40,7 +40,7 @@ use lance_index::vector::pq::PQBuildParams;
|
|||||||
use lance_index::vector::sq::builder::SQBuildParams;
|
use lance_index::vector::sq::builder::SQBuildParams;
|
||||||
use lance_index::DatasetIndexExt;
|
use lance_index::DatasetIndexExt;
|
||||||
use lance_index::IndexType;
|
use lance_index::IndexType;
|
||||||
use lance_io::object_store::{LanceNamespaceStorageOptionsProvider, StorageOptionsAccessor};
|
use lance_io::object_store::LanceNamespaceStorageOptionsProvider;
|
||||||
use lance_namespace::models::{
|
use lance_namespace::models::{
|
||||||
QueryTableRequest as NsQueryTableRequest, QueryTableRequestColumns,
|
QueryTableRequest as NsQueryTableRequest, QueryTableRequestColumns,
|
||||||
QueryTableRequestFullTextQuery, QueryTableRequestVector, StringFtsQuery,
|
QueryTableRequestFullTextQuery, QueryTableRequestVector, StringFtsQuery,
|
||||||
@@ -608,8 +608,8 @@ pub trait BaseTable: std::fmt::Display + std::fmt::Debug + Send + Sync {
|
|||||||
async fn list_versions(&self) -> Result<Vec<Version>>;
|
async fn list_versions(&self) -> Result<Vec<Version>>;
|
||||||
/// Get the table definition.
|
/// Get the table definition.
|
||||||
async fn table_definition(&self) -> Result<TableDefinition>;
|
async fn table_definition(&self) -> Result<TableDefinition>;
|
||||||
/// Get the table URI (storage location)
|
/// Get the table URI
|
||||||
async fn uri(&self) -> Result<String>;
|
fn dataset_uri(&self) -> &str;
|
||||||
/// Get the storage options used when opening this table, if any.
|
/// Get the storage options used when opening this table, if any.
|
||||||
async fn storage_options(&self) -> Option<HashMap<String, String>>;
|
async fn storage_options(&self) -> Option<HashMap<String, String>>;
|
||||||
/// Poll until the columns are fully indexed. Will return Error::Timeout if the columns
|
/// Poll until the columns are fully indexed. Will return Error::Timeout if the columns
|
||||||
@@ -1317,12 +1317,11 @@ impl Table {
|
|||||||
self.inner.list_indices().await
|
self.inner.list_indices().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the table URI (storage location)
|
/// Get the underlying dataset URI
|
||||||
///
|
///
|
||||||
/// Returns the full storage location of the table (e.g., S3/GCS path).
|
/// Warning: This is an internal API and the return value is subject to change.
|
||||||
/// For remote tables, this fetches the location from the server via describe.
|
pub fn dataset_uri(&self) -> &str {
|
||||||
pub async fn uri(&self) -> Result<String> {
|
self.inner.dataset_uri()
|
||||||
self.inner.uri().await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the storage options used when opening this table, if any.
|
/// Get the storage options used when opening this table, if any.
|
||||||
@@ -1425,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,
|
||||||
@@ -1666,14 +1667,18 @@ impl NativeTable {
|
|||||||
|
|
||||||
// Use DatasetBuilder::from_namespace which automatically fetches location
|
// Use DatasetBuilder::from_namespace which automatically fetches location
|
||||||
// and storage options from the namespace
|
// and storage options from the namespace
|
||||||
let builder = DatasetBuilder::from_namespace(namespace_client.clone(), table_id)
|
let builder = DatasetBuilder::from_namespace(
|
||||||
.await
|
namespace_client.clone(),
|
||||||
.map_err(|e| match e {
|
table_id,
|
||||||
lance::Error::Namespace { source, .. } => Error::Runtime {
|
false, // Don't ignore namespace storage options
|
||||||
message: format!("Failed to get table info from namespace: {:?}", source),
|
)
|
||||||
},
|
.await
|
||||||
source => Error::Lance { source },
|
.map_err(|e| match e {
|
||||||
})?;
|
lance::Error::Namespace { source, .. } => Error::Runtime {
|
||||||
|
message: format!("Failed to get table info from namespace: {:?}", source),
|
||||||
|
},
|
||||||
|
source => Error::Lance { source },
|
||||||
|
})?;
|
||||||
|
|
||||||
let dataset = builder
|
let dataset = builder
|
||||||
.with_read_params(params)
|
.with_read_params(params)
|
||||||
@@ -1877,13 +1882,7 @@ impl NativeTable {
|
|||||||
let store_params = params
|
let store_params = params
|
||||||
.store_params
|
.store_params
|
||||||
.get_or_insert_with(ObjectStoreParams::default);
|
.get_or_insert_with(ObjectStoreParams::default);
|
||||||
let accessor = match store_params.storage_options().cloned() {
|
store_params.storage_options_provider = Some(storage_options_provider);
|
||||||
Some(options) => {
|
|
||||||
StorageOptionsAccessor::with_initial_and_provider(options, storage_options_provider)
|
|
||||||
}
|
|
||||||
None => StorageOptionsAccessor::with_provider(storage_options_provider),
|
|
||||||
};
|
|
||||||
store_params.storage_options_accessor = Some(Arc::new(accessor));
|
|
||||||
|
|
||||||
// Patch the params if we have a write store wrapper
|
// Patch the params if we have a write store wrapper
|
||||||
let params = match write_store_wrapper.clone() {
|
let params = match write_store_wrapper.clone() {
|
||||||
@@ -2059,7 +2058,7 @@ impl NativeTable {
|
|||||||
return provided;
|
return provided;
|
||||||
}
|
}
|
||||||
let suggested = suggested_num_sub_vectors(dim);
|
let suggested = suggested_num_sub_vectors(dim);
|
||||||
if num_bits.is_some_and(|num_bits| num_bits == 4) && !suggested.is_multiple_of(2) {
|
if num_bits.is_some_and(|num_bits| num_bits == 4) && suggested % 2 != 0 {
|
||||||
// num_sub_vectors must be even when 4 bits are used
|
// num_sub_vectors must be even when 4 bits are used
|
||||||
suggested + 1
|
suggested + 1
|
||||||
} else {
|
} else {
|
||||||
@@ -2389,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),
|
||||||
@@ -2407,7 +2408,7 @@ impl NativeTable {
|
|||||||
with_row_id: Some(vq.base.with_row_id),
|
with_row_id: Some(vq.base.with_row_id),
|
||||||
bypass_vector_index: Some(!vq.use_index),
|
bypass_vector_index: Some(!vq.use_index),
|
||||||
full_text_query,
|
full_text_query,
|
||||||
..Default::default()
|
version: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
AnyQuery::Query(q) => {
|
AnyQuery::Query(q) => {
|
||||||
@@ -2462,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,
|
||||||
@@ -2469,11 +2472,18 @@ impl NativeTable {
|
|||||||
columns,
|
columns,
|
||||||
prefilter: Some(q.prefilter),
|
prefilter: Some(q.prefilter),
|
||||||
offset: q.offset.map(|o| o as i32),
|
offset: q.offset.map(|o| o as i32),
|
||||||
|
ef: None,
|
||||||
|
refine_factor: None,
|
||||||
|
distance_type: None,
|
||||||
|
nprobes: None,
|
||||||
vector_column: None, // No vector column for plain queries
|
vector_column: None, // No vector column for plain queries
|
||||||
with_row_id: Some(q.with_row_id),
|
with_row_id: Some(q.with_row_id),
|
||||||
bypass_vector_index: Some(true), // No vector index for plain queries
|
bypass_vector_index: Some(true), // No vector index for plain queries
|
||||||
full_text_query,
|
full_text_query,
|
||||||
..Default::default()
|
version: None,
|
||||||
|
fast_search: None,
|
||||||
|
lower_bound: None,
|
||||||
|
upper_bound: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3226,8 +3236,8 @@ impl BaseTable for NativeTable {
|
|||||||
Ok(results.into_iter().flatten().collect())
|
Ok(results.into_iter().flatten().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn uri(&self) -> Result<String> {
|
fn dataset_uri(&self) -> &str {
|
||||||
Ok(self.uri.clone())
|
self.uri.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn storage_options(&self) -> Option<HashMap<String, String>> {
|
async fn storage_options(&self) -> Option<HashMap<String, String>> {
|
||||||
@@ -3235,7 +3245,7 @@ impl BaseTable for NativeTable {
|
|||||||
.get()
|
.get()
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|dataset| dataset.initial_storage_options().cloned())
|
.and_then(|dataset| dataset.storage_options().cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn index_stats(&self, index_name: &str) -> Result<Option<IndexStatistics>> {
|
async fn index_stats(&self, index_name: &str) -> Result<Option<IndexStatistics>> {
|
||||||
@@ -3400,6 +3410,7 @@ pub struct FragmentSummaryStats {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::iter;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -4016,7 +4027,7 @@ mod tests {
|
|||||||
schema.clone(),
|
schema.clone(),
|
||||||
vec![
|
vec![
|
||||||
Arc::new(Int32Array::from_iter_values(offset..(offset + 10))),
|
Arc::new(Int32Array::from_iter_values(offset..(offset + 10))),
|
||||||
Arc::new(Int32Array::from_iter_values(std::iter::repeat_n(age, 10))),
|
Arc::new(Int32Array::from_iter_values(iter::repeat(age).take(10))),
|
||||||
],
|
],
|
||||||
)],
|
)],
|
||||||
schema,
|
schema,
|
||||||
@@ -5151,8 +5162,8 @@ mod tests {
|
|||||||
ns_request
|
ns_request
|
||||||
.columns
|
.columns
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.column_names.as_ref()),
|
.and_then(|columns| columns.column_names.clone()),
|
||||||
Some(&vec!["id".to_string()])
|
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()));
|
||||||
@@ -5198,8 +5209,8 @@ mod tests {
|
|||||||
ns_request
|
ns_request
|
||||||
.columns
|
.columns
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.column_names.as_ref()),
|
.and_then(|columns| columns.column_names.clone()),
|
||||||
Some(&vec!["id".to_string()])
|
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));
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
|
iter::repeat,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -267,10 +268,9 @@ fn create_some_records() -> Result<impl IntoArrow> {
|
|||||||
schema.clone(),
|
schema.clone(),
|
||||||
vec![
|
vec![
|
||||||
Arc::new(Int32Array::from_iter_values(0..TOTAL as i32)),
|
Arc::new(Int32Array::from_iter_values(0..TOTAL as i32)),
|
||||||
Arc::new(StringArray::from_iter(std::iter::repeat_n(
|
Arc::new(StringArray::from_iter(
|
||||||
Some("hello world".to_string()),
|
repeat(Some("hello world".to_string())).take(TOTAL),
|
||||||
TOTAL,
|
)),
|
||||||
))),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.unwrap()]
|
.unwrap()]
|
||||||
|
|||||||
Reference in New Issue
Block a user