5
5
import contextlib
6
6
import functools
7
7
import getpass
8
+ import json
8
9
import os
9
10
import sys
10
11
import tempfile
20
21
import ibis
21
22
import ibis .backends .sql .compilers as sc
22
23
import ibis .common .exceptions as exc
24
+ import ibis .expr .datatypes as dt
23
25
import ibis .expr .operations as ops
24
26
import ibis .expr .schema as sch
25
27
import ibis .expr .types as ir
26
28
from ibis import util
27
29
from ibis .backends import CanCreateDatabase , PyArrowExampleLoader , UrlFromPath
28
30
from ibis .backends .sql import SQLBackend
29
31
from ibis .backends .sql .compilers .base import STAR , AlterTable , RenameTable
32
+ from ibis .backends .sql .datatypes import DatabricksType
30
33
31
34
if TYPE_CHECKING :
32
- from collections .abc import Mapping
35
+ from collections .abc import Iterable , Mapping
33
36
34
37
import pandas as pd
35
38
import polars as pl
36
39
37
40
from ibis .expr .schema import SchemaLike
38
41
39
42
43
+ def _databricks_type_to_ibis (typ , nullable : bool = True ) -> dt .DataType :
44
+ """Convert a Databricks type to an Ibis type."""
45
+ typname = typ ["name" ]
46
+ if typname == "array" :
47
+ return dt .Array (
48
+ _databricks_type_to_ibis (
49
+ typ ["element_type" ], nullable = typ ["element_nullable" ]
50
+ ),
51
+ nullable = nullable ,
52
+ )
53
+ elif typname == "map" :
54
+ return dt .Map (
55
+ key_type = _databricks_type_to_ibis (typ ["key_type" ]),
56
+ value_type = _databricks_type_to_ibis (
57
+ typ ["value_type" ], nullable = typ ["value_nullable" ]
58
+ ),
59
+ nullable = nullable ,
60
+ )
61
+ elif typname == "struct" :
62
+ return dt .Struct (
63
+ {
64
+ field ["name" ]: _databricks_type_to_ibis (
65
+ field ["type" ], nullable = field ["nullable" ]
66
+ )
67
+ for field in typ ["fields" ]
68
+ },
69
+ nullable = nullable ,
70
+ )
71
+ elif typname == "decimal" :
72
+ return dt .Decimal (
73
+ precision = typ ["precision" ], scale = typ ["scale" ], nullable = nullable
74
+ )
75
+ else :
76
+ return DatabricksType .from_string (typname , nullable = nullable )
77
+
78
+
79
+ def _databricks_schema_to_ibis (schema : Iterable [Mapping [str , Any ]]) -> sch .Schema :
80
+ """Convert a Databricks schema to an Ibis schema."""
81
+ return sch .Schema (
82
+ {
83
+ item ["name" ]: _databricks_type_to_ibis (
84
+ item ["type" ], nullable = item ["nullable" ]
85
+ )
86
+ for item in schema
87
+ }
88
+ )
89
+
90
+
40
91
class Backend (SQLBackend , CanCreateDatabase , UrlFromPath , PyArrowExampleLoader ):
41
92
name = "databricks"
42
93
compiler = sc .databricks .compiler
@@ -143,6 +194,9 @@ def create_table(
143
194
else :
144
195
table = obj
145
196
197
+ if not schema :
198
+ schema = table .schema ()
199
+
146
200
self ._run_pre_execute_hooks (table )
147
201
148
202
query = self .compiler .to_sqlglot (table )
@@ -158,10 +212,7 @@ def create_table(
158
212
dialect = self .dialect
159
213
160
214
initial_table = sg .table (temp_name , catalog = catalog , db = database , quoted = quoted )
161
- target = sge .Schema (
162
- this = initial_table ,
163
- expressions = (schema or table .schema ()).to_sqlglot (dialect ),
164
- )
215
+ target = sge .Schema (this = initial_table , expressions = schema .to_sqlglot (dialect ))
165
216
166
217
properties = sge .Properties (expressions = properties )
167
218
create_stmt = sge .Create (kind = "TABLE" , this = target , properties = properties )
@@ -256,23 +307,18 @@ def get_schema(
256
307
"""
257
308
table = sg .table (
258
309
table_name , db = database , catalog = catalog , quoted = self .compiler .quoted
259
- )
260
- sql = sge .Describe (kind = "TABLE" , this = table ).sql (self .dialect )
310
+ ).sql (self .dialect )
261
311
try :
262
312
with self .con .cursor () as cur :
263
- out = cur .execute (sql ). fetchall_arrow ()
313
+ [( out ,)] = cur .execute (f"DESCRIBE EXTENDED { table } AS JSON" ). fetchall ()
264
314
except databricks .sql .exc .ServerOperationError as e :
265
315
raise exc .TableNotFound (
266
316
f"Table { table_name !r} not found in "
267
317
f"{ catalog or self .current_catalog } .{ database or self .current_database } "
268
318
) from e
269
319
270
- names = out ["col_name" ].to_pylist ()
271
- types = out ["data_type" ].to_pylist ()
272
-
273
- return sch .Schema (
274
- dict (zip (names , map (self .compiler .type_mapper .from_string , types )))
275
- )
320
+ js = json .loads (out )
321
+ return _databricks_schema_to_ibis (js ["columns" ])
276
322
277
323
@contextlib .contextmanager
278
324
def _safe_raw_sql (self , query , * args , ** kwargs ):
0 commit comments