Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/ibis_hotdata/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,13 @@ def _managed_table_synced(
schema_name: str,
table_name: str,
) -> bool:
"""Return True only if the table exists and its last load has completed.

A table whose ``synced`` flag is False is still being loaded; we treat
it as writable (returns False) so that an in-progress load can be
retried without requiring ``overwrite=True``. Tables not present in the
information schema also return False (not yet created).
"""
for row in self._iterate_information_schema(
{"connection_id": connection_id, "schema": schema_name, "table": table_name},
include_columns=False,
Expand Down Expand Up @@ -613,9 +620,6 @@ def create_table(
if temp:
raise NotImplementedError("Hotdata does not support temporary tables.")

if obj is not None and schema is not None:
raise com.IbisInputError("create_table accepts only one of obj or schema")

data = self._local_table_to_parquet(obj, schema)
connection_id, schema_name = self._table_location(database)
if not overwrite and self._managed_table_synced(connection_id, schema_name, name):
Expand Down
2 changes: 0 additions & 2 deletions src/ibis_hotdata/managed.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@


def build_managed_config(schema: str, tables: list[str]) -> dict[str, Any]:
if not tables:
return {}
return {
"schemas": [
{
Expand Down
2 changes: 1 addition & 1 deletion src/ibis_hotdata/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ def dtype_from_hotdata_sql_type(sql_type: str | None, *, nullable: bool) -> dt.D
return dt.String(nullable=nullable)
try:
return PostgresType.from_string(sql_type.strip(), nullable=nullable)
except Exception:
except Exception: # ibis/sqlglot raise a variety of parse errors; fall back to String
return dt.String(nullable=nullable)
32 changes: 32 additions & 0 deletions tests/test_hotdata_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,38 @@ def on_create(req: Request) -> Response:
con.create_database("sales", schema="public", tables=["orders"])


def test_create_database_no_tables_still_sends_schema(httpserver: HTTPServer, srv: str):
def on_create(req: Request) -> Response:
body = req.get_json()
assert body == {
"name": "empty_db",
"source_type": "managed",
"config": {
"schemas": [{"name": "analytics", "tables": []}],
},
"skip_discovery": True,
}
return Response(
json.dumps(
{
"id": MANAGED_CONN,
"name": "empty_db",
"source_type": "managed",
"discovery_status": "skipped",
"tables_discovered": 0,
}
),
status=201,
content_type="application/json",
)

httpserver.expect_request("/v1/connections", method="GET").respond_with_json({"connections": []})
httpserver.expect_request("/v1/connections", method="POST").respond_with_handler(on_create)

con = ibis.hotdata.connect(api_url=srv, token="tok", workspace_id="ws", verify_ssl=False)
con.create_database("empty_db", schema="analytics")


def test_drop_table_deletes_managed_table(httpserver: HTTPServer, srv: str):
httpserver.expect_request("/v1/connections").respond_with_json(managed_connections_response())
httpserver.expect_request(
Expand Down
1 change: 1 addition & 0 deletions tests/test_hotdata_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def test_result_arrow_poll_handles_accepted_result(httpserver: HTTPServer):
)
out = client.execute_query("select 1", poll_interval_s=0, poll_timeout_s=5)
assert out["pa_table"].to_pydict() == {"n": [42]}
client.close()


def test_async_query_run_failure(httpserver: HTTPServer):
Expand Down
Loading