Skip to content
4 changes: 2 additions & 2 deletions crates/store/re_protos/proto/rerun/v1alpha1/cloud.proto
Original file line number Diff line number Diff line change
Expand Up @@ -862,9 +862,9 @@ message CreateTableEntryRequest {

// Information about the table to register.
//
// This must be encoded message of one one of the following supported types:
// This must be encoded message of one of the following supported types:
// - LanceTable
google.protobuf.Any provider_details = 2;
optional google.protobuf.Any provider_details = 2;

// Schema of the table to create
rerun.common.v1alpha1.Schema schema = 3;
Expand Down
17 changes: 9 additions & 8 deletions crates/store/re_protos/src/v1alpha1/rerun.cloud.v1alpha1.ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ impl TryFrom<crate::cloud::v1alpha1::CreateDatasetEntryResponse> for CreateDatas
pub struct CreateTableEntryRequest {
pub name: String,
pub schema: Schema,
pub provider_details: ProviderDetails,
pub provider_details: Option<ProviderDetails>,
}

impl TryFrom<CreateTableEntryRequest> for crate::cloud::v1alpha1::CreateTableEntryRequest {
Expand All @@ -784,7 +784,10 @@ impl TryFrom<CreateTableEntryRequest> for crate::cloud::v1alpha1::CreateTableEnt
Ok(Self {
name: value.name,
schema: Some((&value.schema).try_into()?),
provider_details: Some((&value.provider_details).try_into()?),
provider_details: value
.provider_details
.map(|d| (&d).try_into())
.transpose()?,
})
}
}
Expand All @@ -803,12 +806,10 @@ impl TryFrom<crate::cloud::v1alpha1::CreateTableEntryRequest> for CreateTableEnt
"schema"
))?
.try_into()?,
provider_details: ProviderDetails::try_from(&value.provider_details.ok_or(
missing_field!(
crate::cloud::v1alpha1::CreateTableEntryRequest,
"provider_details"
),
)?)?,
provider_details: value
.provider_details
.map(|v| ProviderDetails::try_from(&v))
.transpose()?,
})
}
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions crates/store/re_redap_client/src/connection_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,12 +837,11 @@ where
pub async fn create_table_entry(
&mut self,
name: &str,
url: &Url,
url: Option<Url>,
schema: SchemaRef,
) -> ApiResult<TableEntry> {
let provider_details = ProviderDetails::LanceTable(LanceTable {
table_url: url.clone(),
});
let provider_details =
url.map(|url| ProviderDetails::LanceTable(LanceTable { table_url: url }));
let request = CreateTableEntryRequest {
name: name.to_owned(),
schema: schema.as_ref().clone(),
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_redap_tests/src/tests/create_dataset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ pub async fn create_dataset_tests(service: impl RerunCloudService) {
let create_table_request = CreateTableEntryRequest {
name: table_name.to_owned(),
schema: schema.clone(),
provider_details,
provider_details: Some(provider_details),
}
.try_into()
.expect("Unable to create table request");
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_redap_tests/src/tests/create_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub async fn create_table_entry(service: impl RerunCloudService) {
let create_table_request = CreateTableEntryRequest {
name: table_name.to_owned(),
schema: schema.clone(),
provider_details,
provider_details: Some(provider_details),
}
.try_into()
.expect("Unable to create table request");
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_redap_tests/src/tests/update_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ async fn create_table_entry(
CreateTableEntryRequest {
name: table_name.to_owned(),
schema: schema.clone(),
provider_details,
provider_details: Some(provider_details),
}
.try_into()
.unwrap(),
Expand Down
44 changes: 39 additions & 5 deletions crates/store/re_server/src/rerun_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use re_arrow_util::RecordBatchExt as _;
use re_chunk_store::{Chunk, ChunkStore, ChunkStoreHandle};
use re_log_encoding::ToTransport as _;
use re_log_types::{EntityPath, EntryId, StoreId, StoreKind};
use re_protos::cloud::v1alpha1::ext::LanceTable;
use re_protos::cloud::v1alpha1::ext::{
self, CreateDatasetEntryRequest, CreateDatasetEntryResponse, CreateTableEntryRequest,
CreateTableEntryResponse, DataSource, EntryDetailsUpdate, ProviderDetails, QueryDatasetRequest,
Expand All @@ -39,14 +40,28 @@ use tokio_stream::StreamExt as _;
use tonic::{Code, Request, Response, Status};

use crate::OnError;
use re_tuid::Tuid;

#[derive(Debug, Default)]
pub struct RerunCloudHandlerSettings {}
#[derive(Debug)]
pub struct RerunCloudHandlerSettings {
storage_dir: tempfile::TempDir,
}

impl Default for RerunCloudHandlerSettings {
fn default() -> Self {
Self {
storage_dir: create_data_dir().expect("Failed to create data directory"),
}
}
}

fn create_data_dir() -> Result<tempfile::TempDir, crate::store::Error> {
Ok(tempfile::Builder::new().prefix("rerun-data-").tempdir()?)
}

#[derive(Default)]
pub struct RerunCloudHandlerBuilder {
settings: RerunCloudHandlerSettings,

store: InMemoryStore,
}

Expand Down Expand Up @@ -119,7 +134,6 @@ impl RerunCloudHandlerBuilder {
const DUMMY_TASK_ID: &str = "task_00000000DEADBEEF";

pub struct RerunCloudHandler {
#[expect(dead_code)]
settings: RerunCloudHandlerSettings,

store: tokio::sync::RwLock<InMemoryStore>,
Expand Down Expand Up @@ -1509,7 +1523,27 @@ impl RerunCloudService for RerunCloudHandler {

let schema = Arc::new(request.schema);

let table = match &request.provider_details {
let details = if let Some(details) = request.provider_details {
details
} else {
Comment on lines +1526 to +1528
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this looks like a `unwrap_or_else(|| {})

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Now I used this approach because of the url::Url::from_directory_path(...)? in the else branch: exiting the enclosing function when an error happens in the closure would be more convoluted than this if/else…

// Create a directory in the storage directory. We use a tuid to avoid collisions
// and avoid any sanitization issue with the provided table name.
let table_path = self
.settings
.storage_dir
.path()
.join(format!("lance-{}", Tuid::new()));
ProviderDetails::LanceTable(LanceTable {
table_url: url::Url::from_directory_path(table_path).map_err(|_err| {
Status::internal(format!(
"Failed to create table directory in {:?}",
self.settings.storage_dir.path()
))
})?,
})
};

let table = match details {
ProviderDetails::LanceTable(table) => {
store
.create_table_entry(table_name, &table.table_url, schema)
Expand Down
2 changes: 1 addition & 1 deletion rerun_py/rerun_bindings/rerun_bindings.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1513,7 +1513,7 @@ class CatalogClientInternal:

def create_dataset(self, name: str) -> DatasetEntryInternal: ...
def register_table(self, name: str, url: str) -> TableEntryInternal: ...
def create_table(self, name: str, schema: pa.Schema, url: str) -> TableEntryInternal: ...
def create_table(self, name: str, schema: pa.Schema, url: str | None) -> TableEntryInternal: ...
def ctx(self) -> dfn.SessionContext: ...

# ---
Expand Down
2 changes: 1 addition & 1 deletion rerun_py/rerun_sdk/rerun/catalog/_catalog_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ def register_table(self, name: str, url: str) -> TableEntry:

return TableEntry(self._internal.register_table(name, url))

def create_table(self, name: str, schema: pa.Schema, url: str) -> TableEntry:
def create_table(self, name: str, schema: pa.Schema, url: str | None = None) -> TableEntry:
"""
Create and register a new table.

Expand Down
11 changes: 7 additions & 4 deletions rerun_py/src/catalog/catalog_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ impl PyCatalogClientInternal {
py: Python<'_>,
name: String,
schema: PyArrowType<Schema>,
url: String,
url: Option<String>,
) -> PyResult<Py<PyTableEntryInternal>> {
let connection = self_.borrow_mut(py).connection.clone();

Expand All @@ -250,11 +250,14 @@ impl PyCatalogClientInternal {
.map_err(|err| PyValueError::new_err(format!("Invalid table name. {err}")))?;

let url = url
.parse::<url::Url>()
.map_err(|err| PyValueError::new_err(format!("Invalid URL: {err}")))?;
.map(|url| {
url.parse::<url::Url>()
.map_err(|err| PyValueError::new_err(format!("Invalid URL: {err}")))
})
.transpose()?;

let schema = Arc::new(schema.0);
let table_entry = connection.create_table_entry(py, name, schema, &url)?;
let table_entry = connection.create_table_entry(py, name, schema, url)?;

self_.borrow(py).update_catalog_providers(py, false)?;

Expand Down
2 changes: 1 addition & 1 deletion rerun_py/src/catalog/connection_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl ConnectionHandle {
py: Python<'_>,
name: String,
schema: SchemaRef,
url: &url::Url,
url: Option<url::Url>,
) -> PyResult<TableEntry> {
let entry_id = wait_for_future(
py,
Expand Down
2 changes: 1 addition & 1 deletion rerun_py/tests/e2e_redap_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def create_dataset(self, name: str) -> DatasetEntry:
self._created_entries.append(entry)
return entry

def create_table(self, name: str, schema: pa.Schema, url: str) -> TableEntry:
def create_table(self, name: str, schema: pa.Schema, url: str | None = None) -> TableEntry:
"""Create a table with automatic cleanup. Mirrors CatalogClient.create_table()."""
prefixed_name = self.apply_prefix(name)
entry = self._client.create_table(prefixed_name, schema, url)
Expand Down
22 changes: 8 additions & 14 deletions rerun_py/tests/e2e_redap_tests/test_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
import pytest

if TYPE_CHECKING:
from pathlib import Path

from syrupy import SnapshotAssertion

from e2e_redap_tests.conftest import EntryFactory


@pytest.mark.creates_table
def test_entries_without_hidden(entry_factory: EntryFactory, tmp_path: Path, snapshot: SnapshotAssertion) -> None:
def test_entries_without_hidden(entry_factory: EntryFactory, snapshot: SnapshotAssertion) -> None:
"""Test that entries(), datasets(), and tables() exclude hidden entries by default."""
client = entry_factory.client

Expand All @@ -25,7 +23,7 @@ def test_entries_without_hidden(entry_factory: EntryFactory, tmp_path: Path, sna

# Create test entries
entry_factory.create_dataset("test_dataset")
entry_factory.create_table("test_table", pa.schema([pa.field("col", pa.int32())]), tmp_path.as_uri())
entry_factory.create_table("test_table", pa.schema([pa.field("col", pa.int32())]))

# Get entries after - should only show user-created entries (no hidden)
datasets_after = {d.name for d in client.datasets()}
Expand All @@ -44,9 +42,7 @@ def test_entries_without_hidden(entry_factory: EntryFactory, tmp_path: Path, sna


@pytest.mark.creates_table
def test_entries_with_hidden(
entry_factory: EntryFactory, tmp_path: Path, snapshot_redact_id: SnapshotAssertion
) -> None:
def test_entries_with_hidden(entry_factory: EntryFactory, snapshot_redact_id: SnapshotAssertion) -> None:
"""Test that entries(), datasets(), and tables() include hidden entries when include_hidden=True."""
client = entry_factory.client

Expand All @@ -57,7 +53,7 @@ def test_entries_with_hidden(

# Create test entries
entry_factory.create_dataset("test_dataset")
entry_factory.create_table("test_table", pa.schema([pa.field("col", pa.int32())]), tmp_path.as_uri())
entry_factory.create_table("test_table", pa.schema([pa.field("col", pa.int32())]))

# Get entries after with hidden - should include blueprint datasets and system tables
datasets_after = {d.name for d in client.datasets(include_hidden=True)}
Expand All @@ -76,7 +72,7 @@ def test_entries_with_hidden(


@pytest.mark.creates_table
def test_entry_names_without_hidden(entry_factory: EntryFactory, tmp_path: Path, snapshot: SnapshotAssertion) -> None:
def test_entry_names_without_hidden(entry_factory: EntryFactory, snapshot: SnapshotAssertion) -> None:
"""Test that entry_names(), dataset_names(), and table_names() exclude hidden entries by default."""
client = entry_factory.client

Expand All @@ -87,7 +83,7 @@ def test_entry_names_without_hidden(entry_factory: EntryFactory, tmp_path: Path,

# Create test entries
entry_factory.create_dataset("test_dataset")
entry_factory.create_table("test_table", pa.schema([pa.field("col", pa.int32())]), tmp_path.as_uri())
entry_factory.create_table("test_table", pa.schema([pa.field("col", pa.int32())]))

# Get names after - should only show user-created entries (no hidden)
dataset_names_after = set(client.dataset_names())
Expand All @@ -106,9 +102,7 @@ def test_entry_names_without_hidden(entry_factory: EntryFactory, tmp_path: Path,


@pytest.mark.creates_table
def test_entry_names_with_hidden(
entry_factory: EntryFactory, tmp_path: Path, snapshot_redact_id: SnapshotAssertion
) -> None:
def test_entry_names_with_hidden(entry_factory: EntryFactory, snapshot_redact_id: SnapshotAssertion) -> None:
"""Test that entry_names(), dataset_names(), and table_names() include hidden entries when include_hidden=True."""
client = entry_factory.client

Expand All @@ -119,7 +113,7 @@ def test_entry_names_with_hidden(

# Create test entries
entry_factory.create_dataset("test_dataset")
entry_factory.create_table("test_table", pa.schema([pa.field("col", pa.int32())]), tmp_path.as_uri())
entry_factory.create_table("test_table", pa.schema([pa.field("col", pa.int32())]))

# Get names after with hidden - should include blueprint datasets and system tables
dataset_names_after = set(client.dataset_names(include_hidden=True))
Expand Down