Skip to content

Commit

Permalink
fix(oracle): ensure that metadata queries use SQL and not sqlplus-spe…
Browse files Browse the repository at this point in the history
…cific syntax
  • Loading branch information
cpcloud authored and gforsyth committed Sep 26, 2023
1 parent db09bc8 commit 2c1bf93
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 17 deletions.
55 changes: 40 additions & 15 deletions ibis/backends/oracle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,23 +175,48 @@ def current_database(self) -> str:
return self._scalar_query("SELECT * FROM global_name")

def _metadata(self, query: str) -> Iterable[tuple[str, dt.DataType]]:
table = f"__ibis_oracle_metadata_{util.guid()}"
from sqlalchemy_views import CreateView, DropView

name = util.gen_name("oracle_metadata")

view = sa.table(name)
create_view = CreateView(view, sa.text(query))
drop_view = DropView(view, if_exists=True)

t = sa.table(
"all_tab_columns",
sa.column("table_name"),
sa.column("column_name"),
sa.column("data_type"),
sa.column("data_precision"),
sa.column("data_scale"),
sa.column("nullable"),
)
metadata_query = sa.select(
t.c.column_name,
t.c.data_type,
t.c.data_precision,
t.c.data_scale,
(t.c.nullable == "Y").label("nullable"),
).where(t.c.table_name == name)

with self.begin() as con:
con.exec_driver_sql(
f"CREATE PRIVATE TEMPORARY TABLE {table} AS {query.strip(';')}"
)
result = con.exec_driver_sql(f"DESCRIBE {table}").mappings().all()
con.exec_driver_sql(f"DROP TABLE {table}")

fields = {}
for field in result:
name = field["Field"]
type_string = field["Type"]
is_nullable = field["Null"] == "YES"
fields[name] = OracleType.from_string(type_string, nullable=is_nullable)

return sch.Schema(fields)
con.execute(create_view)
try:
results = con.execute(metadata_query).fetchall()
finally:
# drop the view no matter what
con.execute(drop_view)

for name, type_string, precision, scale, nullable in results:
if precision is not None and scale is not None and precision != 0:
typ = dt.Decimal(precision=precision, scale=scale, nullable=nullable)
elif precision == 0:
# TODO: how to disambiguate between int and float here without inspecting the value?
typ = dt.float
else:
typ = OracleType.from_string(type_string, nullable=nullable)
yield name, typ

def _table_from_schema(
self,
Expand Down
1 change: 0 additions & 1 deletion ibis/backends/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ def test_query_schema(ddl_backend, expr_fn, expected):


@pytest.mark.notimpl(["datafusion", "polars", "mssql"])
@pytest.mark.notimpl(["oracle"], raises=sa.exc.DatabaseError)
@pytest.mark.never(["dask", "pandas"], reason="dask and pandas do not support SQL")
def test_sql(backend, con):
# execute the expression using SQL query
Expand Down
2 changes: 1 addition & 1 deletion ibis/backends/tests/test_dot_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,9 @@ def test_con_dot_sql_transpile(backend, con, dialect, df):


@dot_sql_notimpl
@dot_sql_notyet
@dot_sql_never
@pytest.mark.notimpl(["druid", "flink", "impala", "polars", "pyspark"])
@pytest.mark.notyet(["snowflake"], reason="snowflake column names are case insensitive")
def test_order_by_no_projection(backend):
con = backend.connection
astronauts = con.table("astronauts")
Expand Down

1 comment on commit 2c1bf93

@ibis-squawk-bot
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 3.

Benchmark suite Current: 2c1bf93 Previous: b37804a Ratio
ibis/tests/benchmarks/test_benchmarks.py::test_compile_with_drops[bigquery] 34.94720510192858 iter/sec (stddev: 0.000532950471871581) 130.02477424437095 iter/sec (stddev: 0.000247510166318326) 3.72
ibis/tests/benchmarks/test_benchmarks.py::test_compile[large-bigquery] 24.571639687444932 iter/sec (stddev: 0.0008715598577479763) 146.4406797920701 iter/sec (stddev: 0.00006932716190392247) 5.96
ibis/tests/benchmarks/test_benchmarks.py::test_compile_with_drops[snowflake] 8.151179272534847 iter/sec (stddev: 0.7854007568336726) 100.02154787442706 iter/sec (stddev: 0.000812633800283309) 12.27
ibis/tests/benchmarks/test_benchmarks.py::test_compile[small-bigquery] 1497.6014384813116 iter/sec (stddev: 0.00008198230682671597) 10602.847663506958 iter/sec (stddev: 0.00005834248372791223) 7.08
ibis/tests/benchmarks/test_benchmarks.py::test_compile[medium-bigquery] 57.4166455113932 iter/sec (stddev: 0.0009094148726715771) 603.5295570002766 iter/sec (stddev: 0.000035398257191080955) 10.51

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.