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.
Files changed (35) hide show
  1. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/CHANGELOG.md +9 -0
  2. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/PKG-INFO +1 -1
  3. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/pyproject.toml +1 -1
  4. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/__init__.py +1 -1
  5. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/client.py +53 -4
  6. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/parameters/__init__.py +2 -0
  7. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/parameters/native.py +125 -11
  8. databricks_sql_connector-4.0.4/src/databricks/sql/thrift_api/TCLIService/ttypes.py +50630 -0
  9. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_backend.py +17 -0
  10. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/utils.py +38 -17
  11. databricks_sql_connector-4.0.3/src/databricks/sql/thrift_api/TCLIService/ttypes.py +0 -111775
  12. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/LICENSE +0 -0
  13. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/README.md +0 -0
  14. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/__init__.py +0 -0
  15. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/__init__.py +0 -0
  16. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/auth.py +0 -0
  17. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/authenticators.py +0 -0
  18. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/endpoint.py +0 -0
  19. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/oauth.py +0 -0
  20. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/oauth_http_handler.py +0 -0
  21. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/retry.py +0 -0
  22. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/auth/thrift_http_client.py +0 -0
  23. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/cloudfetch/download_manager.py +0 -0
  24. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/cloudfetch/downloader.py +0 -0
  25. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/exc.py +0 -0
  26. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/experimental/__init__.py +0 -0
  27. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/experimental/oauth_persistence.py +0 -0
  28. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/parameters/py.typed +0 -0
  29. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/py.typed +0 -0
  30. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/TCLIService/TCLIService-remote +0 -0
  31. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/TCLIService/TCLIService.py +0 -0
  32. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/TCLIService/__init__.py +0 -0
  33. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/TCLIService/constants.py +0 -0
  34. {databricks_sql_connector-4.0.3 → databricks_sql_connector-4.0.4}/src/databricks/sql/thrift_api/__init__.py +0 -0
  35. {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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: databricks-sql-connector
3
- Version: 4.0.3
3
+ Version: 4.0.4
4
4
  Summary: Databricks SQL Connector for Python
5
5
  License: Apache-2.0
6
6
  Author: Databricks
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "databricks-sql-connector"
3
- version = "4.0.3"
3
+ version = "4.0.4"
4
4
  description = "Databricks SQL Connector for Python"
5
5
  authors = ["Databricks <databricks-sql-connector-maintainers@databricks.com>"]
6
6
  license = "Apache-2.0"
@@ -68,7 +68,7 @@ DATETIME = DBAPITypeObject("timestamp")
68
68
  DATE = DBAPITypeObject("date")
69
69
  ROWID = DBAPITypeObject()
70
70
 
71
- __version__ = "4.0.3"
71
+ __version__ = "4.0.4"
72
72
  USER_AGENT_NAME = "PyDatabricksSqlConnector"
73
73
 
74
74
  # These two functions are pyhive legacy
@@ -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
- self.close()
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
- self.close()
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
- self.active_op_handle = None
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
- results = pyarrow.concat_tables([results, partial_results])
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):
@@ -12,4 +12,6 @@ from databricks.sql.parameters.native import (
12
12
  TimestampNTZParameter,
13
13
  TinyIntParameter,
14
14
  DecimalParameter,
15
+ MapParameter,
16
+ ArrayParameter,
15
17
  )
@@ -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, int, float, datetime.datetime, datetime.date, bool, decimal.Decimal, None
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 type(value) is int:
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 type(value) is str:
659
+ elif isinstance(value, str):
549
660
  return StringParameter(value=value, name=name)
550
- elif type(value) is float:
661
+ elif isinstance(value, float):
551
662
  return FloatParameter(value=value, name=name)
552
- elif type(value) is datetime.datetime:
663
+ elif isinstance(value, datetime.datetime):
553
664
  return TimestampParameter(value=value, name=name)
554
- elif type(value) is datetime.date:
665
+ elif isinstance(value, datetime.date):
555
666
  return DateParameter(value=value, name=name)
556
- elif type(value) is bool:
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