databricks-sql-connector 4.0.3__tar.gz → 4.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/CHANGELOG.md +9 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/PKG-INFO +1 -1
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/pyproject.toml +1 -1
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/__init__.py +1 -1
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/client.py +53 -4
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/parameters/__init__.py +2 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/parameters/native.py +125 -11
- databricks_sql_connector-4.0.4/src/databricks/sql/thrift_api/TCLIService/ttypes.py +50630 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_backend.py +17 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/utils.py +38 -17
- databricks_sql_connector-4.0.3/src/databricks/sql/thrift_api/TCLIService/ttypes.py +0 -111775
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/LICENSE +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/README.md +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/__init__.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/__init__.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/auth.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/authenticators.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/endpoint.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/oauth.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/oauth_http_handler.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/retry.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/thrift_http_client.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/cloudfetch/download_manager.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/cloudfetch/downloader.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/exc.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/experimental/__init__.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/experimental/oauth_persistence.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/parameters/py.typed +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/py.typed +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/TCLIService/TCLIService-remote +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/TCLIService/TCLIService.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/TCLIService/__init__.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/TCLIService/constants.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/__init__.py +0 -0
- {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/types.py +0 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
# 4.0.4 (2025-06-16)
|
|
4
|
+
|
|
5
|
+
- Update thrift client library after cleaning up unused fields and structs (databricks/databricks-sql-python#553 by @vikrantpuppala)
|
|
6
|
+
- Refactor decimal conversion in PyArrow tables to use direct casting (databricks/databricks-sql-python#544 by @jayantsing-db)
|
|
7
|
+
- Fix: `fetchall_arrow` to always return results in `arrow` format (databricks/databricks-sql-python#551 by @shivam2680)
|
|
8
|
+
- Enhance cursor close handling and context manager exception management to prevent server side resource leaks (databricks/databricks-sql-python#554 by @madhav-db)
|
|
9
|
+
- Added additional logging to enhance debugging (databricks/databricks-sql-python#556 by @saishreeeee)
|
|
10
|
+
- Feature: Added support for complex data types such as Arrays and Map [Private Preview] (databricks/databricks-sql-python#559 by @jprakash-db)
|
|
11
|
+
|
|
3
12
|
# 4.0.3 (2025-04-22)
|
|
4
13
|
|
|
5
14
|
- Fix: Removed `packaging` dependency in favour of default libraries, for `urllib3` version checks (databricks/databricks-sql-python#547 by @jprakash-db)
|
{databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/client.py
RENAMED
|
@@ -214,6 +214,12 @@ class Connection:
|
|
|
214
214
|
# use_cloud_fetch
|
|
215
215
|
# Enable use of cloud fetch to extract large query results in parallel via cloud storage
|
|
216
216
|
|
|
217
|
+
logger.debug(
|
|
218
|
+
"Connection.__init__(server_hostname=%s, http_path=%s)",
|
|
219
|
+
server_hostname,
|
|
220
|
+
http_path,
|
|
221
|
+
)
|
|
222
|
+
|
|
217
223
|
if access_token:
|
|
218
224
|
access_token_kv = {"access_token": access_token}
|
|
219
225
|
kwargs = {**kwargs, **access_token_kv}
|
|
@@ -315,7 +321,13 @@ class Connection:
|
|
|
315
321
|
return self
|
|
316
322
|
|
|
317
323
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
318
|
-
|
|
324
|
+
try:
|
|
325
|
+
self.close()
|
|
326
|
+
except BaseException as e:
|
|
327
|
+
logger.warning(f"Exception during connection close in __exit__: {e}")
|
|
328
|
+
if exc_type is None:
|
|
329
|
+
raise
|
|
330
|
+
return False
|
|
319
331
|
|
|
320
332
|
def __del__(self):
|
|
321
333
|
if self.open:
|
|
@@ -456,7 +468,14 @@ class Cursor:
|
|
|
456
468
|
return self
|
|
457
469
|
|
|
458
470
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
459
|
-
|
|
471
|
+
try:
|
|
472
|
+
logger.debug("Cursor context manager exiting, calling close()")
|
|
473
|
+
self.close()
|
|
474
|
+
except BaseException as e:
|
|
475
|
+
logger.warning(f"Exception during cursor close in __exit__: {e}")
|
|
476
|
+
if exc_type is None:
|
|
477
|
+
raise
|
|
478
|
+
return False
|
|
460
479
|
|
|
461
480
|
def __iter__(self):
|
|
462
481
|
if self.active_result_set:
|
|
@@ -787,6 +806,9 @@ class Cursor:
|
|
|
787
806
|
|
|
788
807
|
:returns self
|
|
789
808
|
"""
|
|
809
|
+
logger.debug(
|
|
810
|
+
"Cursor.execute(operation=%s, parameters=%s)", operation, parameters
|
|
811
|
+
)
|
|
790
812
|
|
|
791
813
|
param_approach = self._determine_parameter_approach(parameters)
|
|
792
814
|
if param_approach == ParameterApproach.NONE:
|
|
@@ -1163,7 +1185,21 @@ class Cursor:
|
|
|
1163
1185
|
def close(self) -> None:
|
|
1164
1186
|
"""Close cursor"""
|
|
1165
1187
|
self.open = False
|
|
1166
|
-
|
|
1188
|
+
|
|
1189
|
+
# Close active operation handle if it exists
|
|
1190
|
+
if self.active_op_handle:
|
|
1191
|
+
try:
|
|
1192
|
+
self.thrift_backend.close_command(self.active_op_handle)
|
|
1193
|
+
except RequestError as e:
|
|
1194
|
+
if isinstance(e.args[1], CursorAlreadyClosedError):
|
|
1195
|
+
logger.info("Operation was canceled by a prior request")
|
|
1196
|
+
else:
|
|
1197
|
+
logging.warning(f"Error closing operation handle: {e}")
|
|
1198
|
+
except Exception as e:
|
|
1199
|
+
logging.warning(f"Error closing operation handle: {e}")
|
|
1200
|
+
finally:
|
|
1201
|
+
self.active_op_handle = None
|
|
1202
|
+
|
|
1167
1203
|
if self.active_result_set:
|
|
1168
1204
|
self._close_and_clear_active_result_set()
|
|
1169
1205
|
|
|
@@ -1415,9 +1451,22 @@ class ResultSet:
|
|
|
1415
1451
|
while not self.has_been_closed_server_side and self.has_more_rows:
|
|
1416
1452
|
self._fill_results_buffer()
|
|
1417
1453
|
partial_results = self.results.remaining_rows()
|
|
1418
|
-
|
|
1454
|
+
if isinstance(results, ColumnTable) and isinstance(
|
|
1455
|
+
partial_results, ColumnTable
|
|
1456
|
+
):
|
|
1457
|
+
results = self.merge_columnar(results, partial_results)
|
|
1458
|
+
else:
|
|
1459
|
+
results = pyarrow.concat_tables([results, partial_results])
|
|
1419
1460
|
self._next_row_index += partial_results.num_rows
|
|
1420
1461
|
|
|
1462
|
+
# If PyArrow is installed and we have a ColumnTable result, convert it to PyArrow Table
|
|
1463
|
+
# Valid only for metadata commands result set
|
|
1464
|
+
if isinstance(results, ColumnTable) and pyarrow:
|
|
1465
|
+
data = {
|
|
1466
|
+
name: col
|
|
1467
|
+
for name, col in zip(results.column_names, results.column_table)
|
|
1468
|
+
}
|
|
1469
|
+
return pyarrow.Table.from_pydict(data)
|
|
1421
1470
|
return results
|
|
1422
1471
|
|
|
1423
1472
|
def fetchall_columnar(self):
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import decimal
|
|
3
3
|
from enum import Enum, auto
|
|
4
|
-
from typing import Optional, Sequence
|
|
4
|
+
from typing import Optional, Sequence, Any
|
|
5
5
|
|
|
6
6
|
from databricks.sql.exc import NotSupportedError
|
|
7
7
|
from databricks.sql.thrift_api.TCLIService.ttypes import (
|
|
8
8
|
TSparkParameter,
|
|
9
9
|
TSparkParameterValue,
|
|
10
|
+
TSparkParameterValueArg,
|
|
10
11
|
)
|
|
11
12
|
|
|
12
13
|
import datetime
|
|
@@ -54,7 +55,17 @@ class DatabricksSupportedType(Enum):
|
|
|
54
55
|
|
|
55
56
|
|
|
56
57
|
TAllowedParameterValue = Union[
|
|
57
|
-
str,
|
|
58
|
+
str,
|
|
59
|
+
int,
|
|
60
|
+
float,
|
|
61
|
+
datetime.datetime,
|
|
62
|
+
datetime.date,
|
|
63
|
+
bool,
|
|
64
|
+
decimal.Decimal,
|
|
65
|
+
None,
|
|
66
|
+
list,
|
|
67
|
+
dict,
|
|
68
|
+
tuple,
|
|
58
69
|
]
|
|
59
70
|
|
|
60
71
|
|
|
@@ -82,6 +93,7 @@ class DbsqlParameterBase:
|
|
|
82
93
|
|
|
83
94
|
CAST_EXPR: str
|
|
84
95
|
name: Optional[str]
|
|
96
|
+
value: Any
|
|
85
97
|
|
|
86
98
|
def as_tspark_param(self, named: bool) -> TSparkParameter:
|
|
87
99
|
"""Returns a TSparkParameter object that can be passed to the DBR thrift server."""
|
|
@@ -98,6 +110,10 @@ class DbsqlParameterBase:
|
|
|
98
110
|
def _tspark_param_value(self):
|
|
99
111
|
return TSparkParameterValue(stringValue=str(self.value))
|
|
100
112
|
|
|
113
|
+
def _tspark_value_arg(self):
|
|
114
|
+
"""Returns a TSparkParameterValueArg object that can be passed to the DBR thrift server."""
|
|
115
|
+
return TSparkParameterValueArg(value=str(self.value), type=self._cast_expr())
|
|
116
|
+
|
|
101
117
|
def _cast_expr(self):
|
|
102
118
|
return self.CAST_EXPR
|
|
103
119
|
|
|
@@ -428,6 +444,99 @@ class TinyIntParameter(DbsqlParameterBase):
|
|
|
428
444
|
CAST_EXPR = DatabricksSupportedType.TINYINT.name
|
|
429
445
|
|
|
430
446
|
|
|
447
|
+
class ArrayParameter(DbsqlParameterBase):
|
|
448
|
+
"""Wrap a Python `Sequence` that will be bound to a Databricks SQL ARRAY type."""
|
|
449
|
+
|
|
450
|
+
def __init__(self, value: Sequence[Any], name: Optional[str] = None):
|
|
451
|
+
"""
|
|
452
|
+
:value:
|
|
453
|
+
The value to bind for this parameter. This will be casted to a ARRAY.
|
|
454
|
+
:name:
|
|
455
|
+
If None, your query must contain a `?` marker. Like:
|
|
456
|
+
|
|
457
|
+
```sql
|
|
458
|
+
SELECT * FROM table WHERE field = ?
|
|
459
|
+
```
|
|
460
|
+
If not None, your query should contain a named parameter marker. Like:
|
|
461
|
+
```sql
|
|
462
|
+
SELECT * FROM table WHERE field = :my_param
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
The `name` argument to this function would be `my_param`.
|
|
466
|
+
"""
|
|
467
|
+
self.name = name
|
|
468
|
+
self.value = [dbsql_parameter_from_primitive(val) for val in value]
|
|
469
|
+
|
|
470
|
+
def as_tspark_param(self, named: bool = False) -> TSparkParameter:
|
|
471
|
+
"""Returns a TSparkParameter object that can be passed to the DBR thrift server."""
|
|
472
|
+
|
|
473
|
+
tsp = TSparkParameter(type=self._cast_expr())
|
|
474
|
+
tsp.arguments = [val._tspark_value_arg() for val in self.value]
|
|
475
|
+
|
|
476
|
+
if named:
|
|
477
|
+
tsp.name = self.name
|
|
478
|
+
tsp.ordinal = False
|
|
479
|
+
elif not named:
|
|
480
|
+
tsp.ordinal = True
|
|
481
|
+
return tsp
|
|
482
|
+
|
|
483
|
+
def _tspark_value_arg(self):
|
|
484
|
+
"""Returns a TSparkParameterValueArg object that can be passed to the DBR thrift server."""
|
|
485
|
+
tva = TSparkParameterValueArg(type=self._cast_expr())
|
|
486
|
+
tva.arguments = [val._tspark_value_arg() for val in self.value]
|
|
487
|
+
return tva
|
|
488
|
+
|
|
489
|
+
CAST_EXPR = DatabricksSupportedType.ARRAY.name
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class MapParameter(DbsqlParameterBase):
|
|
493
|
+
"""Wrap a Python `dict` that will be bound to a Databricks SQL MAP type."""
|
|
494
|
+
|
|
495
|
+
def __init__(self, value: dict, name: Optional[str] = None):
|
|
496
|
+
"""
|
|
497
|
+
:value:
|
|
498
|
+
The value to bind for this parameter. This will be casted to a MAP.
|
|
499
|
+
:name:
|
|
500
|
+
If None, your query must contain a `?` marker. Like:
|
|
501
|
+
|
|
502
|
+
```sql
|
|
503
|
+
SELECT * FROM table WHERE field = ?
|
|
504
|
+
```
|
|
505
|
+
If not None, your query should contain a named parameter marker. Like:
|
|
506
|
+
```sql
|
|
507
|
+
SELECT * FROM table WHERE field = :my_param
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
The `name` argument to this function would be `my_param`.
|
|
511
|
+
"""
|
|
512
|
+
self.name = name
|
|
513
|
+
self.value = [
|
|
514
|
+
dbsql_parameter_from_primitive(item)
|
|
515
|
+
for key, val in value.items()
|
|
516
|
+
for item in (key, val)
|
|
517
|
+
]
|
|
518
|
+
|
|
519
|
+
def as_tspark_param(self, named: bool = False) -> TSparkParameter:
|
|
520
|
+
"""Returns a TSparkParameter object that can be passed to the DBR thrift server."""
|
|
521
|
+
|
|
522
|
+
tsp = TSparkParameter(type=self._cast_expr())
|
|
523
|
+
tsp.arguments = [val._tspark_value_arg() for val in self.value]
|
|
524
|
+
if named:
|
|
525
|
+
tsp.name = self.name
|
|
526
|
+
tsp.ordinal = False
|
|
527
|
+
elif not named:
|
|
528
|
+
tsp.ordinal = True
|
|
529
|
+
return tsp
|
|
530
|
+
|
|
531
|
+
def _tspark_value_arg(self):
|
|
532
|
+
"""Returns a TSparkParameterValueArg object that can be passed to the DBR thrift server."""
|
|
533
|
+
tva = TSparkParameterValueArg(type=self._cast_expr())
|
|
534
|
+
tva.arguments = [val._tspark_value_arg() for val in self.value]
|
|
535
|
+
return tva
|
|
536
|
+
|
|
537
|
+
CAST_EXPR = DatabricksSupportedType.MAP.name
|
|
538
|
+
|
|
539
|
+
|
|
431
540
|
class DecimalParameter(DbsqlParameterBase):
|
|
432
541
|
"""Wrap a Python `Decimal` that will be bound to a Databricks SQL DECIMAL type."""
|
|
433
542
|
|
|
@@ -543,23 +652,26 @@ def dbsql_parameter_from_primitive(
|
|
|
543
652
|
# havoc. We can't use TYPE_INFERRENCE_MAP because mypy doesn't trust
|
|
544
653
|
# its logic
|
|
545
654
|
|
|
546
|
-
if
|
|
655
|
+
if isinstance(value, bool):
|
|
656
|
+
return BooleanParameter(value=value, name=name)
|
|
657
|
+
elif isinstance(value, int):
|
|
547
658
|
return dbsql_parameter_from_int(value, name=name)
|
|
548
|
-
elif
|
|
659
|
+
elif isinstance(value, str):
|
|
549
660
|
return StringParameter(value=value, name=name)
|
|
550
|
-
elif
|
|
661
|
+
elif isinstance(value, float):
|
|
551
662
|
return FloatParameter(value=value, name=name)
|
|
552
|
-
elif
|
|
663
|
+
elif isinstance(value, datetime.datetime):
|
|
553
664
|
return TimestampParameter(value=value, name=name)
|
|
554
|
-
elif
|
|
665
|
+
elif isinstance(value, datetime.date):
|
|
555
666
|
return DateParameter(value=value, name=name)
|
|
556
|
-
elif
|
|
557
|
-
return BooleanParameter(value=value, name=name)
|
|
558
|
-
elif type(value) is decimal.Decimal:
|
|
667
|
+
elif isinstance(value, decimal.Decimal):
|
|
559
668
|
return DecimalParameter(value=value, name=name)
|
|
669
|
+
elif isinstance(value, dict):
|
|
670
|
+
return MapParameter(value=value, name=name)
|
|
671
|
+
elif isinstance(value, Sequence) and not isinstance(value, str):
|
|
672
|
+
return ArrayParameter(value=value, name=name)
|
|
560
673
|
elif value is None:
|
|
561
674
|
return VoidParameter(value=value, name=name)
|
|
562
|
-
|
|
563
675
|
else:
|
|
564
676
|
raise NotSupportedError(
|
|
565
677
|
f"Could not infer parameter type from value: {value} - {type(value)} \n"
|
|
@@ -581,6 +693,8 @@ TDbsqlParameter = Union[
|
|
|
581
693
|
TimestampNTZParameter,
|
|
582
694
|
TinyIntParameter,
|
|
583
695
|
DecimalParameter,
|
|
696
|
+
ArrayParameter,
|
|
697
|
+
MapParameter,
|
|
584
698
|
]
|
|
585
699
|
|
|
586
700
|
|