Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): add catalog and database kwargs to ibis.table #8801

Merged
merged 1 commit into from
Mar 28, 2024
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
31 changes: 30 additions & 1 deletion ibis/expr/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,15 +313,25 @@ def schema(
def table(
schema: SchemaLike | None = None,
name: str | None = None,
catalog: str | None = None,
database: str | None = None,
) -> ir.Table:
"""Create a table literal or an abstract table without data.

Ibis uses the word database to refer to a collection of tables, and the word
catalog to refer to a collection of databases. You can use a combination of
`catalog` and `database` to specify a hierarchical location for table.

Parameters
----------
schema
A schema for the table
name
Name for the table. One is generated if this value is `None`.
catalog
A collection of database.
database
A collection of tables. Required if catalog is not `None`.

Returns
-------
Expand All @@ -340,13 +350,32 @@ def table(
a int64
b string


Create a table with no data backing it in a specific location

>>> import ibis
>>> ibis.options.interactive = False
>>> t = ibis.table(schema=dict(a="int"), name="t", catalog="cat", database="db")
>>> t
UnboundTable: cat.db.t
a int64
"""
if name is None:
if isinstance(schema, type):
name = schema.__name__
else:
name = next(_table_names)
return ops.UnboundTable(name=name, schema=schema).to_expr()
if catalog is not None and database is None:
raise ValueError(
"A catalog-only namespace is invalid in Ibis, "
"please specify a database as well."
)

return ops.UnboundTable(
name=name,
schema=schema,
namespace=ops.Namespace(catalog=catalog, database=database),
).to_expr()


def memtable(
Expand Down
8 changes: 8 additions & 0 deletions ibis/expr/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ def _physical_table(op, name, **kwargs):
return f"{op.__class__.__name__}: {name}\n{schema}"


@fmt.register(ops.UnboundTable)
@fmt.register(ops.DatabaseTable)
def _unbound_table(op, name, **kwargs):
schema = render_schema(op.schema, indent_level=1)
name = ".".join(filter(None, op.namespace.args + (name,)))
return f"{op.__class__.__name__}: {name}\n{schema}"


@fmt.register(ops.InMemoryTable)
def _in_memory_table(op, data, **kwargs):
import rich.pretty
Expand Down
2 changes: 1 addition & 1 deletion ibis/expr/operations/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,8 @@ class PhysicalTable(Relation):

@public
class Namespace(Concrete):
database: Optional[str] = None
catalog: Optional[str] = None
database: Optional[str] = None


@public
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
UnboundTable: bork
a int64
b int64
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
UnboundTable: ork.bork.bork
a int64
b int64
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
UnboundTable: bork.bork
a int64
b int64
15 changes: 15 additions & 0 deletions ibis/expr/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,18 @@ def test_nested_name_property():
x = x + 1

assert x.op().name.count("Add") == n


def test_unbound_table_namespace():
t = ibis.table(name="bork", schema=(("a", "int"), ("b", "int")), database="bork")

assert t.op().namespace == ops.Namespace(database="bork")

t = ibis.table(
name="bork", schema=(("a", "int"), ("b", "int")), database="bork", catalog="ork"
)

assert t.op().namespace == ops.Namespace(catalog="ork", database="bork")

with pytest.raises(ValueError, match="A catalog-only namespace is invalid in Ibis"):
ibis.table(name="bork", schema=(("a", "int"), ("b", "int")), catalog="bork")
19 changes: 19 additions & 0 deletions ibis/expr/tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,25 @@ def test_format_in_memory_table(snapshot):
snapshot.assert_match(result, "repr.txt")


def test_format_unbound_table_namespace(snapshot):
t = ibis.table(name="bork", schema=(("a", "int"), ("b", "int")))

result = fmt(t)
snapshot.assert_match(result, "repr.txt")

t = ibis.table(name="bork", schema=(("a", "int"), ("b", "int")), database="bork")

result = fmt(t)
snapshot.assert_match(result, "reprdb.txt")

t = ibis.table(
name="bork", schema=(("a", "int"), ("b", "int")), catalog="ork", database="bork"
)

result = fmt(t)
snapshot.assert_match(result, "reprcatdb.txt")


def test_format_new_relational_operation(alltypes, snapshot):
class MyRelation(ops.Relation):
parent: ops.Relation
Expand Down
Loading